diff --git a/Cargo.lock b/Cargo.lock index 00162423..ac6fb462 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1099,7 +1099,7 @@ version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading", + "libloading 0.8.9", ] [[package]] @@ -2174,6 +2174,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "bindgen_cuda" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be55fb326843bb67cccceeeaf21c961ef303f60018f9a2ab69494dad8eaf9" +dependencies = [ + "glob", + "num_cpus", + "rayon", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -2601,22 +2612,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c15b675b80d994b2eadb20a4bbe434eabeb454eac3ee5e2b4cf6f147ee9be091" dependencies = [ "byteorder", - "float8", - "gemm", + "candle-kernels", + "candle-metal-kernels", + "candle-ug", + "cudarc 0.19.7", + "float8 0.6.0", + "gemm 0.19.0", "half", "libm", "memmap2 0.9.7", "num-traits", "num_cpus", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", "rand 0.9.1", "rand_distr", "rayon", - "safetensors", + "safetensors 0.7.0", "thiserror 2.0.17", "yoke 0.8.1", "zip 7.2.0", ] +[[package]] +name = "candle-kernels" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8455f84bd810047c7c41216683c1020c915a9f8a740b3b0eabdd4fb2fbaa660" +dependencies = [ + "bindgen_cuda", +] + +[[package]] +name = "candle-metal-kernels" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fdfe9d06de16ce49961e49084e5b79a75a9bdf157246e7c7b6328e87a7aa25d" +dependencies = [ + "half", + "objc2 0.6.3", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", + "once_cell", + "thiserror 2.0.17", + "tracing", +] + [[package]] name = "candle-nn" version = "0.9.2" @@ -2624,11 +2665,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3045fa9e7aef8567d209a27d56b692f60b96f4d0569f4c3011f8ca6715c65e03" dependencies = [ "candle-core", + "candle-metal-kernels", "half", "libc", "num-traits", + "objc2-metal 0.3.2", "rayon", - "safetensors", + "safetensors 0.7.0", "serde", "thiserror 2.0.17", ] @@ -2645,6 +2688,36 @@ dependencies = [ "prost-build", ] +[[package]] +name = "candle-transformers" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b538ec4aa807c416a2ddd3621044888f188827862e2a6fcacba4738e89795d01" +dependencies = [ + "byteorder", + "candle-core", + "candle-nn", + "fancy-regex 0.17.0", + "num-traits", + "rand 0.9.1", + "rayon", + "serde", + "serde_json", + "serde_plain", + "tracing", +] + +[[package]] +name = "candle-ug" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d62be69068bf58987a45f690612739d8d2ea1bf508c1b87dc6815a019575d" +dependencies = [ + "ug", + "ug-cuda", + "ug-metal", +] + [[package]] name = "cast" version = "0.3.0" @@ -2839,7 +2912,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.9", ] [[package]] @@ -3727,6 +3800,27 @@ dependencies = [ "cmov", ] +[[package]] +name = "cudarc" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf99ab37ee7072d64d906aa2dada9a3422f1d975cdf8c8055a573bc84897ed8" +dependencies = [ + "half", + "libloading 0.8.9", +] + +[[package]] +name = "cudarc" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cea5f10a99e025c1b44ae2354c2d8326b25ddbd0baf76bde8e55cfd4018a2cc" +dependencies = [ + "float8 0.7.0", + "half", + "libloading 0.9.0", +] + [[package]] name = "cursor-icon" version = "1.1.0" @@ -4317,7 +4411,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading", + "libloading 0.8.9", ] [[package]] @@ -4907,12 +5001,22 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f463a8a37ede13dac13316d1a1eeafa992300906a0c4c7fa1177f366d10bcbf" dependencies = [ + "cudarc 0.19.7", "half", "num-traits", "rand 0.9.1", "rand_distr", ] +[[package]] +name = "float8" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d1f04709a8ac06e8e8042875a3c466cc4832d3c1a18dbcb9dba3c6e83046bc" +dependencies = [ + "half", +] + [[package]] name = "fluent-uri" version = "0.1.4" @@ -5313,7 +5417,7 @@ dependencies = [ [[package]] name = "galaxy" -version = "1.6.2" +version = "1.6.3" dependencies = [ "addr", "aho-corasick", @@ -5453,6 +5557,7 @@ dependencies = [ "libc", "libsqlite3-sys", "line-ending", + "local_inference", "log", "log-panics", "lsp", @@ -6057,7 +6162,7 @@ dependencies = [ "log", "markdown_parser", "memmap2 0.9.7", - "metal", + "metal 0.33.0", "native-dialog", "nix 0.26.4", "notify-rust", @@ -6202,6 +6307,26 @@ dependencies = [ "windows-result 0.2.0", ] +[[package]] +name = "gemm" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab96b703d31950f1aeddded248bc95543c9efc7ac9c4a21fda8703a83ee35451" +dependencies = [ + "dyn-stack", + "gemm-c32 0.18.2", + "gemm-c64 0.18.2", + "gemm-common 0.18.2", + "gemm-f16 0.18.2", + "gemm-f32 0.18.2", + "gemm-f64 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + [[package]] name = "gemm" version = "0.19.0" @@ -6209,12 +6334,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa0673db364b12263d103b68337a68fbecc541d6f6b61ba72fe438654709eacb" dependencies = [ "dyn-stack", - "gemm-c32", - "gemm-c64", - "gemm-common", - "gemm-f16", - "gemm-f32", - "gemm-f64", + "gemm-c32 0.19.0", + "gemm-c64 0.19.0", + "gemm-common 0.19.0", + "gemm-f16 0.19.0", + "gemm-f32 0.19.0", + "gemm-f64 0.19.0", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + +[[package]] +name = "gemm-c32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6db9fd9f40421d00eea9dd0770045a5603b8d684654816637732463f4073847" +dependencies = [ + "dyn-stack", + "gemm-common 0.18.2", "num-complex", "num-traits", "paste", @@ -6229,7 +6369,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "086936dbdcb99e37aad81d320f98f670e53c1e55a98bee70573e83f95beb128c" dependencies = [ "dyn-stack", - "gemm-common", + "gemm-common 0.19.0", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + +[[package]] +name = "gemm-c64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcad8a3d35a43758330b635d02edad980c1e143dc2f21e6fd25f9e4eada8edf" +dependencies = [ + "dyn-stack", + "gemm-common 0.18.2", "num-complex", "num-traits", "paste", @@ -6244,7 +6399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20c8aeeeec425959bda4d9827664029ba1501a90a0d1e6228e48bef741db3a3f" dependencies = [ "dyn-stack", - "gemm-common", + "gemm-common 0.19.0", "num-complex", "num-traits", "paste", @@ -6252,6 +6407,27 @@ dependencies = [ "seq-macro", ] +[[package]] +name = "gemm-common" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a352d4a69cbe938b9e2a9cb7a3a63b7e72f9349174a2752a558a8a563510d0f3" +dependencies = [ + "bytemuck", + "dyn-stack", + "half", + "libm", + "num-complex", + "num-traits", + "once_cell", + "paste", + "pulp 0.21.5", + "raw-cpuid", + "rayon", + "seq-macro", + "sysctl", +] + [[package]] name = "gemm-common" version = "0.19.0" @@ -6266,13 +6442,31 @@ dependencies = [ "num-traits", "once_cell", "paste", - "pulp", + "pulp 0.22.2", "raw-cpuid", "rayon", "seq-macro", "sysctl", ] +[[package]] +name = "gemm-f16" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff95ae3259432f3c3410eaa919033cd03791d81cebd18018393dc147952e109" +dependencies = [ + "dyn-stack", + "gemm-common 0.18.2", + "gemm-f32 0.18.2", + "half", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "rayon", + "seq-macro", +] + [[package]] name = "gemm-f16" version = "0.19.0" @@ -6280,8 +6474,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3df7a55202e6cd6739d82ae3399c8e0c7e1402859b30e4cb780e61525d9486e" dependencies = [ "dyn-stack", - "gemm-common", - "gemm-f32", + "gemm-common 0.19.0", + "gemm-f32 0.19.0", "half", "num-complex", "num-traits", @@ -6291,6 +6485,21 @@ dependencies = [ "seq-macro", ] +[[package]] +name = "gemm-f32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc8d3d4385393304f407392f754cd2dc4b315d05063f62cf09f47b58de276864" +dependencies = [ + "dyn-stack", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + [[package]] name = "gemm-f32" version = "0.19.0" @@ -6298,7 +6507,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02e0b8c9da1fbec6e3e3ab2ce6bc259ef18eb5f6f0d3e4edf54b75f9fd41a81c" dependencies = [ "dyn-stack", - "gemm-common", + "gemm-common 0.19.0", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + +[[package]] +name = "gemm-f64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b2a4f76ce4b8b16eadc11ccf2e083252d8237c1b589558a49b0183545015bd" +dependencies = [ + "dyn-stack", + "gemm-common 0.18.2", "num-complex", "num-traits", "paste", @@ -6313,7 +6537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "056131e8f2a521bfab322f804ccd652520c79700d81209e9d9275bbdecaadc6a" dependencies = [ "dyn-stack", - "gemm-common", + "gemm-common 0.19.0", "num-complex", "num-traits", "paste", @@ -6974,6 +7198,30 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hf-hub" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629d8f3bbeda9d148036d6b0de0a3ab947abd08ce90626327fc3547a49d59d97" +dependencies = [ + "dirs 6.0.0", + "futures", + "http 1.4.1", + "indicatif", + "libc", + "log", + "native-tls", + "num_cpus", + "rand 0.9.1", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "ureq 2.12.1", + "windows-sys 0.60.2", +] + [[package]] name = "hifijson" version = "0.5.0" @@ -7284,6 +7532,22 @@ dependencies = [ "webpki-roots 1.0.1", ] +[[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 1.8.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.16" @@ -8152,7 +8416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading", + "libloading 0.8.9", "pkg-config", ] @@ -8303,6 +8567,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + [[package]] name = "libm" version = "0.2.15" @@ -8422,6 +8696,25 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" +[[package]] +name = "local_inference" +version = "0.0.0" +dependencies = [ + "anyhow", + "async-trait", + "candle-core", + "candle-nn", + "candle-transformers", + "directories", + "env_logger", + "hf-hub", + "log", + "serde", + "serde_json", + "tokenizers", + "tokio", +] + [[package]] name = "locale_config" version = "0.3.0" @@ -8854,6 +9147,21 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.9.4", + "block", + "core-graphics-types 0.1.3", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + [[package]] name = "metal" version = "0.33.0" @@ -9861,7 +10169,9 @@ checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" dependencies = [ "bitflags 2.9.4", "block2 0.6.2", + "dispatch2", "objc2 0.6.3", + "objc2-core-foundation", "objc2-foundation 0.3.2", ] @@ -10188,7 +10498,7 @@ dependencies = [ "pkg-config", "sha2 0.10.9", "tar", - "ureq", + "ureq 3.0.12", ] [[package]] @@ -11120,6 +11430,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "pulp" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b86df24f0a7ddd5e4b95c94fc9ed8a98f1ca94d3b01bdce2824097e7835907" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "reborrow", + "version_check", +] + [[package]] name = "pulp" version = "0.22.2" @@ -11725,10 +12049,12 @@ dependencies = [ "http-body-util", "hyper 1.8.1", "hyper-rustls 0.27.7", + "hyper-tls", "hyper-util", "js-sys", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -11740,6 +12066,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-rustls 0.26.4", "tokio-util", "tower", @@ -12273,6 +12600,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +[[package]] +name = "safetensors" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44560c11236a6130a46ce36c836a62936dc81ebf8c36a37947423571be0e55b6" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "safetensors" version = "0.7.0" @@ -12511,7 +12848,7 @@ dependencies = [ "sentry-panic", "sentry-tracing", "tokio", - "ureq", + "ureq 3.0.12", ] [[package]] @@ -12737,6 +13074,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + [[package]] name = "serde_regex" version = "1.1.0" @@ -14268,6 +14614,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -14727,6 +15083,54 @@ dependencies = [ "winapi", ] +[[package]] +name = "ug" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b761acf8af3494640d826a8609e2265e19778fb43306c7f15379c78c9b05b0" +dependencies = [ + "gemm 0.18.2", + "half", + "libloading 0.8.9", + "memmap2 0.9.7", + "num", + "num-traits", + "num_cpus", + "rayon", + "safetensors 0.4.5", + "serde", + "thiserror 1.0.63", + "tracing", + "yoke 0.7.5", +] + +[[package]] +name = "ug-cuda" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0a1fa748f26166778c33b8498255ebb7c6bffb472bcc0a72839e07ebb1d9b5" +dependencies = [ + "cudarc 0.17.8", + "half", + "serde", + "thiserror 1.0.63", + "ug", +] + +[[package]] +name = "ug-metal" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7adf545a99a086d362efc739e7cf4317c18cbeda22706000fd434d70ea3d95" +dependencies = [ + "half", + "metal 0.29.0", + "objc", + "serde", + "thiserror 1.0.63", + "ug", +] + [[package]] name = "ui_components" version = "0.0.0" @@ -14889,6 +15293,26 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "native-tls", + "once_cell", + "rustls 0.23.39", + "rustls-pki-types", + "serde", + "serde_json", + "socks", + "url", + "webpki-roots 0.26.11", +] + [[package]] name = "ureq" version = "3.0.12" @@ -15763,7 +16187,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading", + "libloading 0.8.9", "log", "naga", "ndk-sys", @@ -16140,6 +16564,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -16173,13 +16606,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows-threading" version = "0.1.0" @@ -16219,6 +16669,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -16231,6 +16687,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -16243,12 +16705,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -16261,6 +16735,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -16273,6 +16753,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -16285,6 +16771,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -16297,6 +16789,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winit" version = "0.30.1" @@ -16489,7 +16987,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading", + "libloading 0.8.9", "once_cell", "rustix 1.1.2", "x11rb-protocol", diff --git a/Cargo.toml b/Cargo.toml index 46246bd6..ce82c8ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ integration = { path = "crates/integration" } ipc = { path = "crates/ipc" } jsonrpc = { path = "crates/jsonrpc" } languages = { path = "crates/languages" } +local_inference = { path = "crates/local_inference" } lsp = { path = "crates/lsp" } markdown_parser = { path = "crates/markdown_parser" } natural_language_detection = { path = "crates/natural_language_detection" } diff --git a/GALAXY.md b/GALAXY.md index 1d0e8bfc..d0dcd142 100644 --- a/GALAXY.md +++ b/GALAXY.md @@ -68,6 +68,18 @@ Global rules are loaded from `~/.galaxy-ai/rules/*.md` (filename = rule name, co Project rules are loaded from `GALAXY.md` or `AGENTS.md` files found in the project directory tree. +**Model Discovery** (`app/src/ai/bedrock/discovery.rs`): +At startup (and on manual refresh from the Bedrock settings page), Galaxy discovers available models using the user-selected auth settings (profile/SSO/static keys — no external config fallback) via: +1. **STS GetCallerIdentity** — validates AWS credentials before proceeding +2. **ListInferenceProfiles** (system-defined) + **ListFoundationModels** (TEXT output, ON_DEMAND) — fetched in parallel; results are deduplicated by underlying foundation model ID with inference profiles taking priority (they include cross-region routing) +3. Filters out legacy/deprecated models and non-LLM models (embedding, image gen, etc.) +4. **Invoke probe** — each candidate gets a minimal `Converse` call (max_tokens=1); only a successful probe (or a transient throttling/capacity error) counts as accessible. Validation errors (no Converse/on-demand support), access denials, and missing resources are rejected to avoid listing unusable models +5. Tags Claude 4.6+ models with `[1m]` suffix to signal 1M context window support + +The settings page refresh (`RefreshAwsBedrock` in `app/src/settings_view/ai_page.rs`) clears the cached model list immediately, shows a shimmer progress indicator while discovery runs, and repopulates the list when it completes. + +The `[1m]` suffix is an internal marker stripped by `strip_context_marker()` in `client.rs` before API calls. It's used by `context_window_for_model()` in `response_translator.rs` to report the correct context window size. + **External Config Fallback** (`app/src/ai/bedrock/external_config.rs`): When Galaxy's own Bedrock settings are at defaults, it falls back to configurations from: 1. **Claude Code** (`~/.claude/settings.json`) — reads `env.AWS_PROFILE`, `env.AWS_REGION`, and `env.DCP_MODEL_MAP` (ARN-based model mappings) @@ -75,11 +87,31 @@ When Galaxy's own Bedrock settings are at defaults, it falls back to configurati Priority: Galaxy explicit settings > Claude Code > OpenCode > hardcoded defaults. Fallback only applies when profile is `"default"` (for profile) or empty (for region/models). External model ARNs are merged with Galaxy's built-in default model list. -**Token Usage & Cost Tracking** (`app/src/ai/bedrock/stream.rs`): -The Bedrock stream extracts full token metadata from responses: `input_tokens`, `output_tokens`, `cache_read_input_tokens`, `cache_write_input_tokens`. These flow through `build_stream_finished` → `TokenUsage` struct → `conversation.update_cost_and_usage_for_request()`. Cost is estimated using Sonnet-tier Bedrock pricing as a conservative default. Displayed in: +**Token Usage & Cost Tracking** (`app/src/ai/bedrock/response_translator.rs`): +The Bedrock stream extracts full token metadata from responses: `input_tokens`, `output_tokens`, `cache_read_input_tokens`, `cache_write_input_tokens`. These flow through `build_stream_finished` → `TokenUsage` struct → `conversation.update_cost_and_usage_for_request()`. Cost is estimated per-model using Bedrock pricing. Displayed in: - **Agent management cards** — total token count in metadata row - **Conversation usage footer** — full breakdown (input/output/cache read/cache write) + estimated cost +**Progressive Summarization** (`app/src/ai/blocklist/controller.rs`): +When context window usage reaches 85%, Galaxy automatically summarizes older messages while keeping the last 100 `ConversationMessage` entries verbatim. This replaces the old `/compact` hard-compact that destroyed all context. The summary is prepended to the messages array (not the system prompt) as a `` block. Key details: +- Trigger: automatic at 85% context usage, >100 messages in history +- Uses Sonnet via `BedrockClient::converse_collect` in a background `ctx.spawn` +- Cost attributed to conversation totals transparently +- No UI shown — user only sees context usage drop +- `recall_tool_history` tool lets the agent retrieve past tool outputs that were summarized away + +**Failed Tool Call Visibility** (`app/src/ai/bedrock/response_translator.rs`): +When the model calls an unknown/hallucinated tool name, the response translator now emits a visible `AgentOutput` text message to the UI (via `build_add_agent_output_message`) showing what tool was attempted and the error. Previously, synthetic error results were only stored in history (for Bedrock message ordering) but never rendered. + +**Loop Prevention Guardrail** (`app/src/ai/blocklist/controller.rs`): +Detects when the agent gets stuck in a recursive failure loop (same tool + same input failing repeatedly) and injects a corrective system instruction to break the cycle. Key details: +- Tracks last 10 failed action results per conversation via `LoopDetectionState` +- Uses tool discriminant + Display hash to identify repeated patterns +- Threshold: 3 identical failures triggers intervention +- Intervention: injects a `[SYSTEM] Loop detected` user message instructing the model to take a different approach +- Clears on any successful action result (progress = not looping) +- Resets when user sends a new query + ### Key Architectural Patterns 1. **Entity-Handle System**: Views reference other views via handles, not direct ownership diff --git a/WARP.md b/WARP.md index efb7ea6e..6973d778 100644 --- a/WARP.md +++ b/WARP.md @@ -56,18 +56,22 @@ Key files in `app/src/ai/bedrock/`: - `convert.rs` — Shared types (`ConversationMessage`, `ToolDefinition`) and Bedrock SDK type builders - `client.rs` — AWS SDK client construction and `converse_stream` call - `models.rs` — Model registry and cross-region inference prefix logic -- `discovery.rs` — AWS profile and model listing +- `discovery.rs` — AWS profile listing and model discovery (STS identity check + ListFoundationModels) - `diagnostic.rs` — Debug logging (enabled via `GALAXY_BEDROCK_DIAGNOSTICS=1`) - `external_config.rs` — Fallback config from Claude Code/OpenCode settings Key invariants: - Known tools are in `KNOWN_TOOLS` constant in `response_translator.rs` -- Tool definitions are built via `tool_definition_for_name()` in `request_translator.rs` -- Unknown/hallucinated tool calls are caught in the stream, paired with synthetic error results +- Tool definitions are built via `tool_definition_for_name()` in `convert_request.rs`; includes `recall_tool_history` for retrieving past tool results +- Unknown/hallucinated tool calls are caught in the stream, paired with synthetic error results, and now emit a visible `AgentOutput` text message to the UI +- `recall_tool_history` is handled inline in the response translator (synthetic result from `messages_sent`) +- Tool result archive: before progressive summarization drains messages, `ConversationMessage::archive_tool_results()` extracts all tool_use/tool_result pairs into a separate `tool_result_archive` vec. `recall_tool_history` searches both live history + archived results, and supports a `tool_use_id` parameter for exact ID lookup - Prompt caching uses three cache points: system prompt, conversation history (second-to-last message), tool config - `ensure_tool_results_paired()` enforces Bedrock's invariant that every `tool_use` has a matching `tool_result` - `inject_input_messages_into_task()` and `extract_user_query_text()` ensure user queries persist for session restore - The stream emits a `UserQuery` proto message at the start of each response for conversation title +- Progressive summary (if present) is prepended to the messages array as a user/assistant pair in `translator.rs` +- Loop prevention guardrail in `controller.rs` detects repeated tool failures (3+ identical) and injects corrective instructions ### Platform Setup - `./script/bootstrap` - Platform-specific setup (calls platform-specific bootstrap scripts) diff --git a/app/Cargo.toml b/app/Cargo.toml index 14c641b7..5ce25267 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -5,7 +5,7 @@ description = "Galaxy - AI-powered terminal" edition = "2021" autobins = false name = "galaxy" -version = "1.6.2" +version = "1.6.3" publish.workspace = true license.workspace = true @@ -123,6 +123,7 @@ lsp-types = "0.97.0" indexmap = { version = "2.0.2", features = ["serde"] } input_classifier.workspace = true instant.workspace = true +local_inference = { workspace = true, optional = true } ipc.workspace = true itertools.workspace = true kmeans_colors = { version = "0.5", default-features = false, features = [ @@ -613,6 +614,7 @@ default = [ "drag_tabs_to_windows", "plugin_host", "file_and_diff_set_comments", + "local_ai", ] # Enable this feature to automatically perform heap profiling. NOTE: This will # substantially slow down program execution. @@ -832,6 +834,8 @@ projects = [] vim_code_editor = [] allow_opening_file_links_using_editor_env = [] nld_improvements = ["nld_onnx_model"] +local_ai = ["dep:local_inference"] +local_ai_metal = ["local_ai", "local_inference/metal"] undo_closed_panes = [] revert_diff_hunk = [] code_review_save_changes = [] diff --git a/app/build.rs b/app/build.rs index 8fb07f53..97e70245 100644 --- a/app/build.rs +++ b/app/build.rs @@ -69,7 +69,6 @@ fn main() -> Result<()> { let plugin_src = Path::new("DockTilePlugin/GalaxyDockTilePlugin.docktileplugin"); let plugin_dst = target_dir.join("GalaxyDockTilePlugin.docktileplugin"); - if !status.success() { fs::remove_dir_all(plugin_src).expect("Failed to clean up plugin directory"); panic!("Dock tile plugin build failed"); @@ -170,6 +169,7 @@ fn add_features(target_family: &str, target_os: &str) { if env::var("PROFILE").ok().is_some_and(|val| val == "debug") { println!("cargo:rustc-cfg=feature=\"agent_mode_debug\""); } + } fn build_and_link_sentry() { diff --git a/app/src/ai/agent/api.rs b/app/src/ai/agent/api.rs index 2d57a5dc..972efd78 100644 --- a/app/src/ai/agent/api.rs +++ b/app/src/ai/agent/api.rs @@ -134,16 +134,18 @@ pub struct RequestParams { /// Full Bedrock conversation history for direct Bedrock calls. /// When present, the Bedrock path uses this instead of extracting from task_context. pub bedrock_message_history: Vec, - /// Compacted summary from a prior summarization pass. Injected into the system - /// prompt so it benefits from system-level caching. - pub bedrock_compact_summary: Option, + /// Progressive summary of older conversation history. Prepended as the first + /// message pair in the messages array sent to Bedrock. + pub bedrock_progressive_summary: Option, + /// Archived tool_use/tool_result pairs from previous summarization drains. + /// Passed to the Bedrock translator so `recall_tool_history` can search archived + /// results even after they've been summarized away from live history. + pub bedrock_tool_result_archive: Vec, /// Populated by the Bedrock path after building the message list. /// Contains the full messages sent (old history + new input) so the controller /// can store them back into the conversation for the next request cycle. pub bedrock_messages_sent: std::sync::Arc>>, - /// Whether this request is a conversation summarization/compaction. - pub is_summarization: bool, } pub type Event = Result>; @@ -331,9 +333,9 @@ impl RequestParams { parent_agent_id: None, agent_name: None, bedrock_message_history: Vec::new(), - bedrock_compact_summary: None, + bedrock_progressive_summary: None, + bedrock_tool_result_archive: Vec::new(), bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())), - is_summarization: false, } } } diff --git a/app/src/ai/agent/api/convert_conversation.rs b/app/src/ai/agent/api/convert_conversation.rs index 9b6751fa..68af769c 100644 --- a/app/src/ai/agent/api/convert_conversation.rs +++ b/app/src/ai/agent/api/convert_conversation.rs @@ -85,6 +85,8 @@ pub fn convert_conversation_data_to_ai_conversation( run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, RestorationMode::Continue => AgentConversationData { server_conversation_token: Some( @@ -104,6 +106,8 @@ pub fn convert_conversation_data_to_ai_conversation( run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, }; diff --git a/app/src/ai/agent/api/impl.rs b/app/src/ai/agent/api/impl.rs index 6e50210f..4ef53b4b 100644 --- a/app/src/ai/agent/api/impl.rs +++ b/app/src/ai/agent/api/impl.rs @@ -153,9 +153,9 @@ pub async fn generate_multi_agent_output( model_id, root_task_id: params.root_task_id.clone(), bedrock_message_history: params.bedrock_message_history.clone(), - bedrock_compact_summary: params.bedrock_compact_summary.clone(), + bedrock_tool_result_archive: params.bedrock_tool_result_archive.clone(), + bedrock_progressive_summary: params.bedrock_progressive_summary.clone(), bedrock_messages_sent: params.bedrock_messages_sent.clone(), - is_summarization: params.is_summarization, }; match translator::execute(translator_request, &mut request).await { diff --git a/app/src/ai/agent/api/impl_tests.rs b/app/src/ai/agent/api/impl_tests.rs index 70b3bc97..1d98e657 100644 --- a/app/src/ai/agent/api/impl_tests.rs +++ b/app/src/ai/agent/api/impl_tests.rs @@ -41,9 +41,9 @@ fn request_params_with_ask_user_question_enabled(ask_user_question_enabled: bool parent_agent_id: None, agent_name: None, bedrock_message_history: Vec::new(), - bedrock_compact_summary: None, + bedrock_progressive_summary: None, + bedrock_tool_result_archive: Vec::new(), bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())), - is_summarization: false, } } diff --git a/app/src/ai/agent/conversation.rs b/app/src/ai/agent/conversation.rs index e442a18b..0fcb11e3 100644 --- a/app/src/ai/agent/conversation.rs +++ b/app/src/ai/agent/conversation.rs @@ -236,19 +236,28 @@ pub struct AIConversation { /// request cycles. This is the source of truth for what Bedrock sees. bedrock_message_history: Vec, + /// Archived tool_use/tool_result pairs from messages that were drained by + /// progressive summarization. Kept separately so `recall_tool_history` can + /// find results even after they've been summarized away from live history. + tool_result_archive: Vec, + /// Live context token count from the most recent Bedrock response. /// This is the actual input_tokens reported by Bedrock — represents the current /// context window size, NOT a cumulative total. current_context_tokens: u32, - /// When set, contains the compacted summary of prior conversation history. - /// Injected into the system prompt (not the messages array) so it benefits - /// from system-level caching at the 1-hour TTL. - compact_summary: Option, + /// Progressive summary of older conversation history. + /// Prepended as the first message in the messages array sent to Bedrock, + /// while recent messages are kept verbatim. + progressive_summary: Option, - /// Guards against repeated auto-compact triggers within the same high-usage window. - /// Set to true when auto-compact fires; reset when summarization completes. - has_pending_auto_compact: bool, + /// The number of messages that were drained from bedrock_message_history + /// when the progressive summary was last produced. Used to handle race + /// conditions if new messages arrive during an in-flight summarization. + messages_summarized_up_to: usize, + + /// Guards against concurrent progressive summarization requests. + has_pending_progressive_summary: bool, subagent_retry_count: u8, } @@ -303,9 +312,11 @@ impl AIConversation { is_remote_child: false, last_event_sequence: None, bedrock_message_history: Vec::new(), - compact_summary: None, + tool_result_archive: Vec::new(), + progressive_summary: None, + messages_summarized_up_to: 0, current_context_tokens: 0, - has_pending_auto_compact: false, + has_pending_progressive_summary: false, subagent_retry_count: 0, } } @@ -401,6 +412,8 @@ impl AIConversation { run_id, autoexecute_override, last_event_sequence, + progressive_summary, + messages_summarized_up_to, ) = if let Some(data) = conversation_data { let server_conversation_token = data .server_conversation_token @@ -432,6 +445,8 @@ impl AIConversation { AIConversationAutoexecuteMode::default() }; let last_event_sequence = data.last_event_sequence; + let progressive_summary = data.progressive_summary; + let messages_summarized_up_to = data.messages_summarized_up_to; ( server_conversation_token, @@ -445,6 +460,8 @@ impl AIConversation { run_id, autoexecute_override, last_event_sequence, + progressive_summary, + messages_summarized_up_to, ) } else { ( @@ -459,6 +476,8 @@ impl AIConversation { None, AIConversationAutoexecuteMode::default(), None, + None, + 0, ) }; @@ -526,9 +545,11 @@ impl AIConversation { is_remote_child: false, last_event_sequence, bedrock_message_history, - compact_summary: None, + tool_result_archive: Vec::new(), + progressive_summary, + messages_summarized_up_to, current_context_tokens: 0, - has_pending_auto_compact: false, + has_pending_progressive_summary: false, subagent_retry_count: 0, }) } @@ -547,6 +568,56 @@ impl AIConversation { &mut self.bedrock_message_history } + pub fn tool_result_archive(&self) -> &[crate::ai::bedrock::convert::ConversationMessage] { + &self.tool_result_archive + } + + pub fn archive_tool_results( + &mut self, + messages: Vec, + ) { + use crate::ai::bedrock::convert::{ContentPart, MessageContent}; + + let mut pending_tool_uses: Vec = + Vec::new(); + + for msg in messages { + match &msg.content { + MessageContent::ToolUse { .. } => pending_tool_uses.push(msg), + MessageContent::ToolResult { .. } => { + if !pending_tool_uses.is_empty() { + let tool_use = pending_tool_uses.remove(pending_tool_uses.len() - 1); + self.tool_result_archive.push(tool_use); + } + self.tool_result_archive.push(msg); + } + MessageContent::MultiPart(parts) => { + for part in parts { + match part { + ContentPart::ToolUse { .. } => { + pending_tool_uses.push(msg.clone()); + } + ContentPart::ToolResult { .. } => { + if !pending_tool_uses.is_empty() { + let tool_use = + pending_tool_uses.remove(pending_tool_uses.len() - 1); + self.tool_result_archive.push(tool_use); + } + self.tool_result_archive.push(msg.clone()); + } + _ => {} + } + } + } + _ => {} + } + } + + for tool_use in pending_tool_uses { + self.tool_result_archive.push(tool_use); + } + } + pub fn append_to_bedrock_history( &mut self, messages: Vec, @@ -554,12 +625,21 @@ impl AIConversation { self.bedrock_message_history.extend(messages); } - pub fn compact_summary(&self) -> Option<&str> { - self.compact_summary.as_deref() + pub fn progressive_summary(&self) -> Option<&str> { + self.progressive_summary.as_deref() } - pub fn set_compact_summary(&mut self, summary: Option) { - self.compact_summary = summary; + pub fn set_progressive_summary(&mut self, summary: Option, messages_summarized: usize) { + self.progressive_summary = summary; + self.messages_summarized_up_to = messages_summarized; + } + + pub fn messages_summarized_up_to(&self) -> usize { + self.messages_summarized_up_to + } + + pub fn reset_messages_summarized_up_to(&mut self) { + self.messages_summarized_up_to = 0; } /// Assigns fresh exchange IDs to all exchanges in this conversation. @@ -601,12 +681,12 @@ impl AIConversation { self.current_context_tokens = tokens; } - pub fn has_pending_auto_compact(&self) -> bool { - self.has_pending_auto_compact + pub fn has_pending_progressive_summary(&self) -> bool { + self.has_pending_progressive_summary } - pub fn set_has_pending_auto_compact(&mut self, value: bool) { - self.has_pending_auto_compact = value; + pub fn set_has_pending_progressive_summary(&mut self, value: bool) { + self.has_pending_progressive_summary = value; } pub fn credits_spent(&self) -> f32 { @@ -1424,22 +1504,18 @@ impl AIConversation { self.all_exchanges() .into_iter() .flat_map(|exchange| { - exchange - .output_status - .output() - .into_iter() - .map(|output| { - output - .get() - .actions() - .filter(|a| { - matches!( - a.action, - super::AIAgentActionType::RequestCommandOutput { .. } - ) - }) - .count() - }) + exchange.output_status.output().into_iter().map(|output| { + output + .get() + .actions() + .filter(|a| { + matches!( + a.action, + super::AIAgentActionType::RequestCommandOutput { .. } + ) + }) + .count() + }) }) .sum() } @@ -1825,7 +1901,7 @@ impl AIConversation { // so we only update the summarized flag if it's going from false to true. if usage_metadata.summarized && !self.conversation_usage_metadata.was_summarized { self.conversation_usage_metadata.was_summarized = usage_metadata.summarized; - self.has_pending_auto_compact = false; + self.has_pending_progressive_summary = false; } } Ok(()) @@ -2783,16 +2859,12 @@ impl AIConversation { Some(result) => match result { Ok(todos_op) => todos_op, Err(e) => { - log::error!( - "[bedrock] AppendToMessageContent failed: {e:?}" - ); + log::error!("[bedrock] AppendToMessageContent failed: {e:?}"); return Err(e.into()); } }, None => { - log::error!( - "[bedrock] AppendToMessageContent: TaskNotFound in task_store" - ); + log::error!("[bedrock] AppendToMessageContent: TaskNotFound in task_store"); return Err(UpdateConversationError::TaskNotFound); } }; @@ -3125,6 +3197,8 @@ impl AIConversation { run_id: self.task_id.map(|id| id.to_string()), autoexecute_override: Some(self.autoexecute_override.into()), last_event_sequence: self.last_event_sequence, + progressive_summary: self.progressive_summary.clone(), + messages_summarized_up_to: self.messages_summarized_up_to, }, }; ctx.spawn( @@ -3320,7 +3394,10 @@ impl AIConversation { pub fn cache_miss_tokens(&self) -> u32 { self.total_token_usage_by_model .values() - .map(|u| u.total_input.saturating_sub(u.input_cache_read + u.input_cache_write)) + .map(|u| { + u.total_input + .saturating_sub(u.input_cache_read + u.input_cache_write) + }) .sum() } @@ -3341,7 +3418,10 @@ impl AIConversation { pub fn last_block_cache_miss_tokens(&self) -> u32 { self.last_block_token_usage_by_model .values() - .map(|u| u.total_input.saturating_sub(u.input_cache_read + u.input_cache_write)) + .map(|u| { + u.total_input + .saturating_sub(u.input_cache_read + u.input_cache_write) + }) .sum() } diff --git a/app/src/ai/agent_conversations_model_tests.rs b/app/src/ai/agent_conversations_model_tests.rs index 22f11f07..75890783 100644 --- a/app/src/ai/agent_conversations_model_tests.rs +++ b/app/src/ai/agent_conversations_model_tests.rs @@ -155,6 +155,8 @@ fn test_display_status_uses_matching_conversation_for_in_progress_task() { run_id: Some(task_id.clone()), autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); @@ -207,6 +209,8 @@ fn test_display_status_updates_when_blocked_conversation_resumes() { run_id: Some(task_id.clone()), autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); @@ -283,6 +287,8 @@ fn test_display_status_terminal_task_state_overrides_matching_conversation() { run_id: Some(task_id.clone()), autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); @@ -335,6 +341,8 @@ fn test_status_filter_uses_display_status_for_task_backed_conversations() { run_id: Some(task_id.clone()), autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); @@ -770,6 +778,8 @@ fn test_get_tasks_and_conversations_prefers_task_when_task_id_matches_conversati run_id: Some(task_id.clone()), autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); @@ -827,6 +837,8 @@ fn test_get_tasks_and_conversations_prefers_task_when_server_token_matches() { run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); @@ -883,6 +895,8 @@ fn test_get_tasks_and_conversations_keeps_unrelated_tasks_and_conversations() { run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); diff --git a/app/src/ai/agent_sdk/artifact_upload_tests.rs b/app/src/ai/agent_sdk/artifact_upload_tests.rs index f845fa0a..df53f40a 100644 --- a/app/src/ai/agent_sdk/artifact_upload_tests.rs +++ b/app/src/ai/agent_sdk/artifact_upload_tests.rs @@ -42,6 +42,10 @@ fn create_conversation_metadata( credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), + total_cache_read_tokens: 0, + total_cache_write_tokens: 0, + total_cache_miss_tokens: 0, + total_cost_cents: 0.0, }, metadata: create_mock_server_metadata(), permissions: ServerPermissions::mock_personal(), diff --git a/app/src/ai/bedrock/client.rs b/app/src/ai/bedrock/client.rs index 0803008c..b1a98a33 100644 --- a/app/src/ai/bedrock/client.rs +++ b/app/src/ai/bedrock/client.rs @@ -7,13 +7,23 @@ use aws_sdk_bedrockruntime::Client as BedrockRuntimeClient; use crate::settings::ai::BedrockAuthMethod; -use super::external_config::ExternalBedrockConfig; use super::convert::{build_converse_request, CachingConfig, ConversationMessage, ToolDefinition}; use super::diagnostic::BedrockDiagnosticLogger; +use super::external_config::ExternalBedrockConfig; use super::models::apply_cross_region_prefix; use super::response_translator::bedrock_stream_to_response_events; use crate::ai::agent::api::ResponseStream; +fn strip_context_marker(model_id: &str) -> String { + if let Some(base) = model_id.strip_suffix("[1m]") { + base.to_string() + } else if let Some(base) = model_id.strip_suffix("[1M]") { + base.to_string() + } else { + model_id.to_string() + } +} + pub struct BedrockClient { runtime_client: BedrockRuntimeClient, region: String, @@ -141,12 +151,13 @@ impl BedrockClient { user_query: Option, diagnostic_logger: Option>, messages_sent: Arc>>, - is_summarization: bool, + tool_result_archive: Vec, ) -> Result { + let base_model_id = strip_context_marker(model_id); let effective_model_id = if cross_region_inference { - apply_cross_region_prefix(model_id, &self.region) + apply_cross_region_prefix(&base_model_id, &self.region) } else { - model_id.to_string() + base_model_id }; let external_config = ExternalBedrockConfig::load(); @@ -238,9 +249,74 @@ impl BedrockClient { user_query, diagnostic_logger, messages_sent, - effective_model_id, - is_summarization, + model_id.to_string(), + tool_result_archive, ))) } + /// Performs a non-streaming converse call and collects the full response text. + /// Used for background progressive summarization where we don't need streaming UI. + /// Returns (response_text, input_tokens, output_tokens). + pub async fn converse_collect( + &self, + model_id: &str, + messages: Vec, + system_prompt: Option, + max_tokens: i32, + cross_region_inference: bool, + ) -> Result<(String, u32, u32), BedrockError> { + let base_model_id = strip_context_marker(model_id); + let effective_model_id = if cross_region_inference { + apply_cross_region_prefix(&base_model_id, &self.region) + } else { + base_model_id + }; + + let external_config = ExternalBedrockConfig::load(); + let caching_config = CachingConfig::from_external_config(&external_config); + + let converted = build_converse_request( + messages, + system_prompt, + None, + vec![], + max_tokens, + None, + None, + None, + caching_config, + ); + + let request = self + .runtime_client + .converse() + .model_id(&effective_model_id) + .set_system(Some(converted.system)) + .set_messages(Some(converted.messages)) + .inference_config(converted.inference_config); + + let output = request.send().await.map_err(|e| { + let msg = format!("{e}"); + log::error!("[bedrock] converse_collect error: {msg}"); + BedrockError::ApiError(msg) + })?; + + let mut response_text = String::new(); + if let Some(output_msg) = output.output() { + if let aws_sdk_bedrockruntime::types::ConverseOutput::Message(msg) = output_msg { + for block in msg.content() { + if let aws_sdk_bedrockruntime::types::ContentBlock::Text(text) = block { + response_text.push_str(text); + } + } + } + } + + let (input_tokens, output_tokens) = output + .usage() + .map(|u| (u.input_tokens() as u32, u.output_tokens() as u32)) + .unwrap_or((0, 0)); + + Ok((response_text, input_tokens, output_tokens)) + } } diff --git a/app/src/ai/bedrock/convert.rs b/app/src/ai/bedrock/convert.rs index baa657a3..4788d1cf 100644 --- a/app/src/ai/bedrock/convert.rs +++ b/app/src/ai/bedrock/convert.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use aws_sdk_bedrockruntime::types::{ CachePointBlock, CachePointType, CacheTtl, ContentBlock, ConversationRole, - InferenceConfiguration, Message as BedrockMessage, SystemContentBlock, Tool, - ToolConfiguration, ToolInputSchema, ToolResultBlock, ToolResultContentBlock, - ToolResultStatus, ToolSpecification, ToolUseBlock, + InferenceConfiguration, Message as BedrockMessage, SystemContentBlock, Tool, ToolConfiguration, + ToolInputSchema, ToolResultBlock, ToolResultContentBlock, ToolResultStatus, ToolSpecification, + ToolUseBlock, }; use aws_smithy_types::Document; use serde_json::Value as JsonValue; @@ -141,7 +141,10 @@ fn json_to_document(value: JsonValue) -> Document { } } -fn convert_messages(messages: Vec, caching_config: &CachingConfig) -> Vec { +fn convert_messages( + messages: Vec, + caching_config: &CachingConfig, +) -> Vec { let mut result = Vec::new(); for msg in messages { @@ -300,7 +303,7 @@ fn coalesce_consecutive_roles(messages: Vec) -> Vec, - compact_summary: Option, + _compact_summary: Option, caching_config: &CachingConfig, ) -> Vec { let mut blocks = Vec::new(); @@ -311,11 +314,9 @@ fn convert_system_prompt( } } - if let Some(summary) = compact_summary { - blocks.push(SystemContentBlock::Text(format!( - "\n{summary}\n" - ))); - } + // Progressive summary is now prepended to the messages array instead of + // being injected here. The compact_summary parameter is kept for API compat + // but ignored. if !blocks.is_empty() && caching_config.enabled { let mut builder = CachePointBlock::builder().r#type(CachePointType::Default); @@ -351,7 +352,10 @@ fn build_inference_config( builder.build() } -fn build_tool_config(tools: Vec, caching_config: &CachingConfig) -> Option { +fn build_tool_config( + tools: Vec, + caching_config: &CachingConfig, +) -> Option { if tools.is_empty() { return None; } diff --git a/app/src/ai/bedrock/convert_request.rs b/app/src/ai/bedrock/convert_request.rs index 255c379b..35293d6f 100644 --- a/app/src/ai/bedrock/convert_request.rs +++ b/app/src/ai/bedrock/convert_request.rs @@ -705,6 +705,31 @@ fn tool_definition_for_name(name: &str) -> ToolDefinition { "required": ["prompt", "label"] }), }, + "recall_tool_history" => ToolDefinition { + name: "recall_tool_history".to_string(), + description: "Retrieve the full output of a past tool execution from conversation history. Use when you need details from a tool call that is no longer in your current context window.".to_string(), + input_schema: serde_json::json!({ + "type": "object", + "properties": { + "search_query": { + "type": "string", + "description": "Search text to match against tool names, inputs, or outputs" + }, + "tool_name": { + "type": "string", + "description": "Filter by tool name (run_shell_command, read_files, grep, file_glob, apply_file_diffs)" + }, + "tool_use_id": { + "type": "string", + "description": "Exact tool_use_id to look up (most precise)" + }, + "offset_from_end": { + "type": "integer", + "description": "How many matching tool calls back from most recent (0 = most recent match)" + } + } + }), + }, _ => ToolDefinition { name: name.to_string(), description: format!("Tool: {}", name), @@ -724,6 +749,7 @@ fn default_tool_definitions() -> Vec { tool_definition_for_name("grep"), tool_definition_for_name("file_glob"), tool_definition_for_name("suggest_next_prompt"), + tool_definition_for_name("recall_tool_history"), ] } diff --git a/app/src/ai/bedrock/convert_tests.rs b/app/src/ai/bedrock/convert_tests.rs index 1f8cb519..daf4aa6e 100644 --- a/app/src/ai/bedrock/convert_tests.rs +++ b/app/src/ai/bedrock/convert_tests.rs @@ -10,7 +10,17 @@ fn test_text_message_converts_to_single_block() { content: MessageContent::Text("Hello".to_string()), }]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.messages.len(), 1); assert_eq!(result.messages[0].role(), &ConversationRole::User); @@ -29,7 +39,17 @@ fn test_tool_use_produces_valid_json_input() { }, }]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.messages.len(), 1); assert_eq!(result.messages[0].role(), &ConversationRole::Assistant); @@ -53,7 +73,17 @@ fn test_tool_result_with_matching_id() { }, }]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.messages.len(), 1); match &result.messages[0].content()[0] { @@ -75,7 +105,17 @@ fn test_tool_result_error_status() { }, }]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); match &result.messages[0].content()[0] { ContentBlock::ToolResult(block) => { @@ -101,7 +141,17 @@ fn test_consecutive_same_role_messages_coalesced() { }, ]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.messages.len(), 1); assert_eq!(result.messages[0].content().len(), 2); @@ -126,7 +176,17 @@ fn test_alternating_roles_not_coalesced() { }, ]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.messages.len(), 3); assert_eq!(result.messages[0].role(), &ConversationRole::User); @@ -160,17 +220,46 @@ fn test_system_prompt_separated_from_messages() { #[test] fn test_empty_system_prompt_produces_empty_vec() { - let result = - build_converse_request(vec![], Some("".to_string()), None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + vec![], + Some("".to_string()), + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert!(result.system.is_empty()); - let result2 = build_converse_request(vec![], None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result2 = build_converse_request( + vec![], + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert!(result2.system.is_empty()); } #[test] fn test_empty_tools_produce_none_config() { - let result = build_converse_request(vec![], None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + vec![], + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert!(result.tool_config.is_none()); } @@ -188,7 +277,17 @@ fn test_tool_definitions_produce_tool_config() { }), }]; - let result = build_converse_request(vec![], None, None, tools, 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + vec![], + None, + None, + tools, + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert!(result.tool_config.is_some()); let config = result.tool_config.unwrap(); @@ -197,7 +296,17 @@ fn test_tool_definitions_produce_tool_config() { #[test] fn test_inference_config_max_tokens_only() { - let result = build_converse_request(vec![], None, None, vec![], 8192, None, None, None, CachingConfig::default()); + let result = build_converse_request( + vec![], + None, + None, + vec![], + 8192, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.inference_config.max_tokens(), Some(8192)); assert_eq!(result.inference_config.temperature(), None); assert_eq!(result.inference_config.top_p(), None); @@ -237,7 +346,17 @@ fn test_multipart_content_produces_multiple_blocks() { ]), }]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.messages[0].content().len(), 2); assert!(matches!( @@ -275,7 +394,17 @@ fn test_tool_result_after_tool_use_coalesced_into_user_message() { }, ]; - let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None, CachingConfig::default()); + let result = build_converse_request( + messages, + None, + None, + vec![], + 4096, + None, + None, + None, + CachingConfig::default(), + ); assert_eq!(result.messages.len(), 3); assert_eq!(result.messages[0].role(), &ConversationRole::User); @@ -392,7 +521,10 @@ fn test_caching_enabled_has_cache_points() { .content() .iter() .any(|c| matches!(c, ContentBlock::CachePoint(_))); - assert!(has_cache_point, "Second-to-last message should have cache point"); + assert!( + has_cache_point, + "Second-to-last message should have cache point" + ); // Check that cache point exists in system use aws_sdk_bedrockruntime::types::SystemContentBlock; diff --git a/app/src/ai/bedrock/discovery.rs b/app/src/ai/bedrock/discovery.rs deleted file mode 100644 index 7fbfe407..00000000 --- a/app/src/ai/bedrock/discovery.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::collections::BTreeSet; -use std::path::PathBuf; - -use anyhow::Result; -use aws_config::BehaviorVersion; -use aws_sdk_bedrock::types::InferenceProfileType; -use aws_sdk_bedrock::Client as BedrockControlClient; -use aws_sdk_bedrockruntime::config::Region; - -use crate::settings::ai::{BedrockAuthMethod, BedrockModelConfig}; - -use super::client::BedrockClientConfig; - -pub fn list_aws_profiles() -> Vec { - let mut profiles = BTreeSet::new(); - - if let Some(home) = dirs::home_dir() { - parse_config_file(home.join(".aws").join("config"), &mut profiles); - parse_credentials_file(home.join(".aws").join("credentials"), &mut profiles); - } - - profiles.into_iter().collect() -} - -fn parse_config_file(path: PathBuf, profiles: &mut BTreeSet) { - let contents = match std::fs::read_to_string(&path) { - Ok(c) => c, - Err(_) => return, - }; - - for line in contents.lines() { - let trimmed = line.trim(); - if trimmed.starts_with('[') && trimmed.ends_with(']') { - let section = &trimmed[1..trimmed.len() - 1]; - if section == "default" { - profiles.insert("default".to_string()); - } else if let Some(name) = section.strip_prefix("profile ") { - profiles.insert(name.trim().to_string()); - } - } - } -} - -fn parse_credentials_file(path: PathBuf, profiles: &mut BTreeSet) { - let contents = match std::fs::read_to_string(&path) { - Ok(c) => c, - Err(_) => return, - }; - - for line in contents.lines() { - let trimmed = line.trim(); - if trimmed.starts_with('[') && trimmed.ends_with(']') { - let name = &trimmed[1..trimmed.len() - 1]; - profiles.insert(name.trim().to_string()); - } - } -} - -pub async fn discover_inference_profiles( - config: &BedrockClientConfig, -) -> Result> { - let aws_config = build_aws_config(config).await; - let client = BedrockControlClient::new(&aws_config); - - let mut models = Vec::new(); - - fetch_profiles_by_type(&client, InferenceProfileType::SystemDefined, &mut models).await?; - fetch_profiles_by_type(&client, InferenceProfileType::Application, &mut models).await?; - - models.sort_by(|a, b| a.display_name.cmp(&b.display_name)); - models.dedup_by(|a, b| a.model_id == b.model_id); - - Ok(models) -} - -async fn fetch_profiles_by_type( - client: &BedrockControlClient, - profile_type: InferenceProfileType, - models: &mut Vec, -) -> Result<()> { - let mut next_token: Option = None; - - loop { - let mut req = client - .list_inference_profiles() - .type_equals(profile_type.clone()) - .max_results(100); - if let Some(token) = next_token.take() { - req = req.next_token(token); - } - - let resp = req.send().await?; - - for summary in resp.inference_profile_summaries() { - let profile_id = summary.inference_profile_id(); - let profile_name = summary.inference_profile_name(); - let profile_arn = summary.inference_profile_arn(); - - let model_id = if profile_arn.contains(":application-inference-profile/") { - profile_arn.to_string() - } else { - profile_id.to_string() - }; - - if should_skip_model(profile_name) { - continue; - } - - models.push(BedrockModelConfig { - model_id, - display_name: profile_name.to_string(), - vision_supported: true, - }); - } - - next_token = resp.next_token().map(|s| s.to_string()); - if next_token.is_none() { - break; - } - } - - Ok(()) -} - -fn should_skip_model(name: &str) -> bool { - let lower = name.to_lowercase(); - lower.contains("embed") - || lower.contains("stable image") - || lower.contains("stable-image") - || lower.contains("upscale") - || lower.contains("outpaint") - || lower.contains("inpaint") - || lower.contains("recolor") - || lower.contains("erase") - || lower.contains("style transfer") - || lower.contains("style guide") - || lower.contains("remove background") - || lower.contains("search and replace") - || lower.contains("control sketch") - || lower.contains("control structure") -} - -async fn build_aws_config(config: &BedrockClientConfig) -> aws_config::SdkConfig { - match config.auth_method { - BedrockAuthMethod::Profile | BedrockAuthMethod::Sso => { - let mut loader = - aws_config::defaults(BehaviorVersion::latest()).profile_name(&config.profile); - - if !config.region.is_empty() { - loader = loader.region(Region::new(config.region.clone())); - } - - loader.load().await - } - BedrockAuthMethod::StaticKeys => { - let creds = aws_credential_types::Credentials::new( - &config.access_key_id, - &config.secret_access_key, - None, - None, - "warp-bedrock-discovery", - ); - - let mut loader = - aws_config::defaults(BehaviorVersion::latest()).credentials_provider(creds); - - if !config.region.is_empty() { - loader = loader.region(Region::new(config.region.clone())); - } else { - loader = loader.region(Region::new("us-east-1".to_string())); - } - - loader.load().await - } - } -} diff --git a/app/src/ai/bedrock/e2e_tests.rs b/app/src/ai/bedrock/e2e_tests.rs index 880fd3de..046d0af4 100644 --- a/app/src/ai/bedrock/e2e_tests.rs +++ b/app/src/ai/bedrock/e2e_tests.rs @@ -558,7 +558,7 @@ impl AgentSimulation { None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("converse_stream should succeed"); @@ -1147,7 +1147,7 @@ async fn test_reasoning_model_produces_substantial_output() { None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("converse_stream should succeed"); @@ -1272,7 +1272,7 @@ async fn test_event_sequence_matches_controller_expectations() { None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("should connect"); @@ -1389,7 +1389,7 @@ async fn test_followup_turn_does_not_send_create_task() { None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("should connect"); @@ -1475,7 +1475,7 @@ async fn run_slash_command_test( None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("stream should connect"); @@ -1705,7 +1705,7 @@ async fn test_slash_resume_conversation() { None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("stream should connect"); @@ -1819,7 +1819,7 @@ async fn test_empty_messages_safety_check() { None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("safety fallback message should work"); @@ -1982,7 +1982,7 @@ async fn test_full_proto_round_trip_with_tool_history() { None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await; diff --git a/app/src/ai/bedrock/external_config.rs b/app/src/ai/bedrock/external_config.rs index 96d3bfd5..3101b2be 100644 --- a/app/src/ai/bedrock/external_config.rs +++ b/app/src/ai/bedrock/external_config.rs @@ -22,12 +22,8 @@ impl ExternalBedrockConfig { // Claude Code takes priority over OpenCode since it has richer model mappings Self { - profile: claude_config - .profile - .or(opencode_config.profile), - region: claude_config - .region - .or(opencode_config.region), + profile: claude_config.profile.or(opencode_config.profile), + region: claude_config.region.or(opencode_config.region), models: if claude_config.models.is_empty() { opencode_config.models } else { @@ -73,10 +69,12 @@ fn parse_claude_code_config(path: PathBuf) -> ExternalBedrockConfig { let env = match json.get("env").and_then(|v| v.as_object()) { Some(e) => e, - None => return ExternalBedrockConfig { - auth_refresh_command, - ..Default::default() - }, + None => { + return ExternalBedrockConfig { + auth_refresh_command, + ..Default::default() + } + } }; let profile = env @@ -146,6 +144,7 @@ fn parse_claude_code_model_map( model_id: arn, display_name, vision_supported: true, + context_size: 200_000, } }) .collect() @@ -175,10 +174,7 @@ fn derive_display_name(model_id: &str) -> String { fn prettify_model_slug(slug: &str) -> String { // Remove version suffixes like "-v1:0", "-v1", "-v2:0" - let slug = slug - .split("-v") - .next() - .unwrap_or(slug); + let slug = slug.split("-v").next().unwrap_or(slug); // Remove date suffixes like "-20250514" or "-20251001" let parts: Vec<&str> = slug.split('-').collect(); @@ -275,6 +271,53 @@ fn parse_opencode_config(path: PathBuf) -> ExternalBedrockConfig { } } +pub fn list_aws_profiles() -> Vec { + use std::collections::BTreeSet; + + let mut profiles = BTreeSet::new(); + + if let Some(home) = dirs::home_dir() { + parse_aws_config_file(home.join(".aws").join("config"), &mut profiles); + parse_aws_credentials_file(home.join(".aws").join("credentials"), &mut profiles); + } + + profiles.into_iter().collect() +} + +fn parse_aws_config_file(path: PathBuf, profiles: &mut std::collections::BTreeSet) { + let contents = match std::fs::read_to_string(&path) { + Ok(c) => c, + Err(_) => return, + }; + + for line in contents.lines() { + let trimmed = line.trim(); + if trimmed.starts_with('[') && trimmed.ends_with(']') { + let section = &trimmed[1..trimmed.len() - 1]; + if section == "default" { + profiles.insert("default".to_string()); + } else if let Some(name) = section.strip_prefix("profile ") { + profiles.insert(name.trim().to_string()); + } + } + } +} + +fn parse_aws_credentials_file(path: PathBuf, profiles: &mut std::collections::BTreeSet) { + let contents = match std::fs::read_to_string(&path) { + Ok(c) => c, + Err(_) => return, + }; + + for line in contents.lines() { + let trimmed = line.trim(); + if trimmed.starts_with('[') && trimmed.ends_with(']') { + let name = &trimmed[1..trimmed.len() - 1]; + profiles.insert(name.trim().to_string()); + } + } +} + #[cfg(test)] #[path = "external_config_tests.rs"] mod tests; diff --git a/app/src/ai/bedrock/external_config_tests.rs b/app/src/ai/bedrock/external_config_tests.rs index 9dc55131..2d0587cf 100644 --- a/app/src/ai/bedrock/external_config_tests.rs +++ b/app/src/ai/bedrock/external_config_tests.rs @@ -156,18 +156,18 @@ fn test_claude_code_takes_priority_over_opencode() { let claude = ExternalBedrockConfig { profile: Some("claude-profile".to_string()), region: Some("us-east-1".to_string()), - models: Vec::new(), + ..Default::default() }; let opencode = ExternalBedrockConfig { profile: Some("opencode-profile".to_string()), region: Some("eu-west-1".to_string()), - models: Vec::new(), + ..Default::default() }; let merged = ExternalBedrockConfig { profile: claude.profile.or(opencode.profile), region: claude.region.or(opencode.region), - models: Vec::new(), + ..Default::default() }; assert_eq!(merged.profile, Some("claude-profile".to_string())); diff --git a/app/src/ai/bedrock/integration_tests.rs b/app/src/ai/bedrock/integration_tests.rs index 486df1ec..3b133d36 100644 --- a/app/src/ai/bedrock/integration_tests.rs +++ b/app/src/ai/bedrock/integration_tests.rs @@ -67,7 +67,7 @@ async fn collect_stream_output( None, None, Arc::new(Mutex::new(Vec::new())), - false, + Vec::new(), ) .await .expect("converse_stream should succeed"); @@ -624,7 +624,11 @@ async fn test_all_tools_visible_to_model() { let tools = super::request_translator::default_tool_definitions(); let tool_names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect(); - println!("[test] Sending {} tools to Bedrock: {:?}", tools.len(), tool_names); + println!( + "[test] Sending {} tools to Bedrock: {:?}", + tools.len(), + tool_names + ); let messages = vec![ConversationMessage { role: MessageRole::User, @@ -656,7 +660,10 @@ async fn test_all_tools_visible_to_model() { } if !missing_tools.is_empty() { - println!("[test] WARNING: Model did not mention these tools: {:?}", missing_tools); + println!( + "[test] WARNING: Model did not mention these tools: {:?}", + missing_tools + ); } let expected_core_tools = [ diff --git a/app/src/ai/bedrock/mod.rs b/app/src/ai/bedrock/mod.rs index 885614b9..1745bcef 100644 --- a/app/src/ai/bedrock/mod.rs +++ b/app/src/ai/bedrock/mod.rs @@ -1,7 +1,6 @@ pub mod client; pub mod convert; pub mod diagnostic; -pub mod discovery; pub mod external_config; pub mod models; pub mod request_translator; diff --git a/app/src/ai/bedrock/models.rs b/app/src/ai/bedrock/models.rs index f3d4a531..3ec272dc 100644 --- a/app/src/ai/bedrock/models.rs +++ b/app/src/ai/bedrock/models.rs @@ -6,53 +6,105 @@ pub struct DefaultModel { pub model_id: &'static str, pub display_name: &'static str, pub vision_supported: bool, + pub context_size: u32, } pub const DEFAULT_BEDROCK_MODELS: &[DefaultModel] = &[ DefaultModel { - model_id: "anthropic.claude-opus-4-6", - display_name: "Claude Opus 4.6", + model_id: "us.anthropic.claude-opus-4-6-v1[1m]", + display_name: "Claude Opus 4.6 (1M)", vision_supported: true, + context_size: 1_000_000, }, DefaultModel { - model_id: "anthropic.claude-opus-4-7", - display_name: "Claude Opus 4.7", + model_id: "us.anthropic.claude-sonnet-4-6[1m]", + display_name: "Claude Sonnet 4.6 (1M)", vision_supported: true, + context_size: 1_000_000, }, DefaultModel { - model_id: "anthropic.claude-sonnet-4-6", - display_name: "Claude Sonnet 4.6", + model_id: "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + display_name: "Claude Sonnet 4.5", vision_supported: true, + context_size: 200_000, }, DefaultModel { - model_id: "anthropic.claude-sonnet-4-20250514-v1:0", + model_id: "us.anthropic.claude-sonnet-4-20250514-v1:0", display_name: "Claude Sonnet 4", vision_supported: true, + context_size: 200_000, }, DefaultModel { - model_id: "anthropic.claude-haiku-4-5-20251001-v1:0", + model_id: "us.anthropic.claude-3-sonnet-20240229-v1:0", + display_name: "Claude 3 Sonnet", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "global.anthropic.claude-sonnet-4-6", + display_name: "Claude Sonnet 4.6 (Global)", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + display_name: "Claude Sonnet 4.5 (Global)", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + display_name: "Claude Sonnet 4 (Global)", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "us.anthropic.claude-opus-4-5-20251101-v1:0", + display_name: "Claude Opus 4.5", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "us.anthropic.claude-opus-4-1-20250805-v1:0", + display_name: "Claude Opus 4.1", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "global.anthropic.claude-opus-4-6-v1", + display_name: "Claude Opus 4.6 (Global)", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + display_name: "Claude Opus 4.5 (Global)", + vision_supported: true, + context_size: 200_000, + }, + DefaultModel { + model_id: "us.anthropic.claude-haiku-4-5-20251001-v1:0", display_name: "Claude Haiku 4.5", vision_supported: true, + context_size: 200_000, }, DefaultModel { - model_id: "amazon.nova-pro-v1:0", - display_name: "Amazon Nova Pro", + model_id: "us.anthropic.claude-3-haiku-20240307-v1:0", + display_name: "Claude 3 Haiku", vision_supported: true, + context_size: 200_000, }, DefaultModel { - model_id: "amazon.nova-lite-v1:0", - display_name: "Amazon Nova Lite", + model_id: "us.anthropic.claude-3-5-haiku-20241022-v1:0", + display_name: "Claude 3.5 Haiku", vision_supported: true, + context_size: 200_000, }, DefaultModel { - model_id: "amazon.nova-micro-v1:0", - display_name: "Amazon Nova Micro", - vision_supported: false, - }, - DefaultModel { - model_id: "deepseek.r1-v1:0", - display_name: "DeepSeek R1", - vision_supported: false, + model_id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + display_name: "Claude Haiku 4.5 (Global)", + vision_supported: true, + context_size: 200_000, }, ]; @@ -76,6 +128,7 @@ pub fn get_effective_models(user_models: &[BedrockModelConfig]) -> Vec Vec Vec { timestamp: None, server_message_data: String::new(), citations: vec![], - message: Some(api::message::Message::UserQuery( - api::message::UserQuery { - query: prompt, - ..Default::default() - }, - )), + message: Some(api::message::Message::UserQuery(api::message::UserQuery { + query: prompt, + ..Default::default() + })), }); } _ => {} @@ -440,8 +438,13 @@ pub fn sanitize_messages_for_bedrock(messages: &mut Vec) { /// If the last message is an assistant message (e.g. after compaction), /// append a continuation prompt. fn ensure_ends_with_user_message(messages: &mut Vec) { - if messages.last().is_some_and(|m| m.role == MessageRole::Assistant) { - log::info!("[bedrock] Appending continuation prompt (conversation ended with assistant message)"); + if messages + .last() + .is_some_and(|m| m.role == MessageRole::Assistant) + { + log::info!( + "[bedrock] Appending continuation prompt (conversation ended with assistant message)" + ); messages.push(ConversationMessage { role: MessageRole::User, content: MessageContent::Text("Continue.".to_string()), @@ -514,12 +517,24 @@ fn strip_tool_result_parts(content: &mut MessageContent) { let part = parts.remove(0); *content = match part { ContentPart::Text(t) => MessageContent::Text(t), - ContentPart::ToolUse { tool_use_id, name, input } => { - MessageContent::ToolUse { tool_use_id, name, input } - } - ContentPart::ToolResult { tool_use_id, content: c, is_error } => { - MessageContent::ToolResult { tool_use_id, content: c, is_error } - } + ContentPart::ToolUse { + tool_use_id, + name, + input, + } => MessageContent::ToolUse { + tool_use_id, + name, + input, + }, + ContentPart::ToolResult { + tool_use_id, + content: c, + is_error, + } => MessageContent::ToolResult { + tool_use_id, + content: c, + is_error, + }, }; } } @@ -544,12 +559,24 @@ fn strip_orphaned_tool_result_parts( let part = parts.remove(0); *content = match part { ContentPart::Text(t) => MessageContent::Text(t), - ContentPart::ToolUse { tool_use_id, name, input } => { - MessageContent::ToolUse { tool_use_id, name, input } - } - ContentPart::ToolResult { tool_use_id, content: c, is_error } => { - MessageContent::ToolResult { tool_use_id, content: c, is_error } - } + ContentPart::ToolUse { + tool_use_id, + name, + input, + } => MessageContent::ToolUse { + tool_use_id, + name, + input, + }, + ContentPart::ToolResult { + tool_use_id, + content: c, + is_error, + } => MessageContent::ToolResult { + tool_use_id, + content: c, + is_error, + }, }; } } @@ -565,7 +592,10 @@ fn is_empty_content(content: &MessageContent) -> bool { } } -fn collect_tool_use_ids_into(content: &MessageContent, ids: &mut std::collections::HashSet) { +fn collect_tool_use_ids_into( + content: &MessageContent, + ids: &mut std::collections::HashSet, +) { match content { MessageContent::ToolUse { tool_use_id, .. } => { ids.insert(tool_use_id.clone()); @@ -895,7 +925,9 @@ pub fn extract_system_prompt(request: &api::Request) -> Option { prompt.push_str("- For tasks that require interacting with external services, web UIs, or capabilities not covered by your filesystem and shell tools, use your MCP tools.\n"); prompt.push_str("- For complex multi-step tasks where a single script would replace many tool calls, write code (Python, Node, bash) via `run_shell_command` to reduce round-trips. But never use scripts for simple operations that a single command handles.\n\n"); prompt.push_str("**Critical rules:**\n"); - prompt.push_str("- Use ONLY the tools in your tool configuration. Never invent or guess tool names.\n"); + prompt.push_str( + "- Use ONLY the tools in your tool configuration. Never invent or guess tool names.\n", + ); prompt.push_str("- ALWAYS pass `--no-pager` (or equivalent) flags to CLI tools like git, less, man, etc. Tools that lock stdin will freeze the session.\n"); prompt.push_str("- Output text directly in your response instead of using `echo` — echo requires user approval and adds unnecessary friction.\n"); prompt.push_str("- Use absolute paths based on the working directory shown above.\n"); @@ -1442,6 +1474,7 @@ fn is_server_result_error(text: &str) -> bool { || lower.starts_with("mcp tool error:") || lower.starts_with("search codebase error:") || lower.starts_with("failed to read files") + || lower.starts_with("user cancelled") || (lower.starts_with("exit code:") && !lower.starts_with("exit code: 0")) } @@ -1533,7 +1566,6 @@ fn convert_proto_message_for_test(msg: &api::Message) -> Option>, messages_sent: Arc>>, model_id: String, - is_summarization: bool, + tool_result_archive: Vec, ) -> BoxStream<'static, Event> { let request_id = Uuid::new_v4().to_string(); let conversation_id = Uuid::new_v4().to_string(); @@ -224,6 +224,50 @@ pub fn bedrock_stream_to_response_events( current_tool_use_id.clear(); current_tool_name.clear(); current_tool_input_json.clear(); + } else if current_tool_name == "recall_tool_history" { + // Handle recall_tool_history locally by searching + // the conversation messages that were sent. + log::info!("[bedrock] Handling recall_tool_history locally"); + let input_json: serde_json::Value = serde_json::from_str(¤t_tool_input_json) + .unwrap_or(serde_json::Value::Object(serde_json::Map::new())); + let search_query = input_json.get("search_query") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let tool_name_filter = input_json.get("tool_name") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let tool_use_id = input_json.get("tool_use_id") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let offset = input_json.get("offset_from_end") + .and_then(|v| v.as_u64()) + .unwrap_or(0) as usize; + + let recall_result = match messages_sent.lock() { + Ok(sent) => recall_from_history( + &sent, + &tool_result_archive, + search_query, + tool_name_filter, + tool_use_id, + offset, + ), + Err(_) => "Error: could not access conversation history.".to_string(), + }; + + history_tool_calls.push(ContentPart::ToolUse { + tool_use_id: current_tool_use_id.clone(), + name: current_tool_name.clone(), + input: input_json, + }); + synthetic_tool_results.push(ContentPart::ToolResult { + tool_use_id: current_tool_use_id.clone(), + content: recall_result, + is_error: false, + }); + current_tool_use_id.clear(); + current_tool_name.clear(); + current_tool_input_json.clear(); } else if !is_known_tool(¤t_tool_name) { // Unknown/hallucinated tool: record it in history // with a paired error result so the conversation @@ -233,6 +277,10 @@ pub fn bedrock_stream_to_response_events( "[bedrock] Model called unknown tool '{}' (id={}), synthesizing error result", current_tool_name, current_tool_use_id ); + let error_text = format!( + "Error: '{}' is not a valid tool. Available tools are: run_shell_command, read_files, apply_file_diffs, grep, file_glob. Please use one of these tools instead.", + current_tool_name + ); let input_json: serde_json::Value = serde_json::from_str(¤t_tool_input_json) .unwrap_or(serde_json::Value::Object(serde_json::Map::new())); history_tool_calls.push(ContentPart::ToolUse { @@ -245,12 +293,20 @@ pub fn bedrock_stream_to_response_events( // fix it up later (and so the executor doesn't hang). synthetic_tool_results.push(ContentPart::ToolResult { tool_use_id: current_tool_use_id.clone(), - content: format!( - "Error: '{}' is not a valid tool. Available tools are: run_shell_command, read_files, apply_file_diffs, grep, file_glob. Please use one of these tools instead.", - current_tool_name - ), + content: error_text.clone(), is_error: true, }); + // Emit to the UI so the user sees the failed tool call + let error_msg_id = Uuid::new_v4().to_string(); + let error_display = format!( + "Failed tool call: `{}`\n\n{}", + current_tool_name, error_text + ); + yield Ok(build_add_agent_output_message( + &task_id, + &error_msg_id, + &error_display, + )); if let Some(ref logger) = diagnostic_logger { logger.log_stream_event(&format!( "UnknownToolCall: name={}, id={} — synthesized error result", @@ -496,7 +552,7 @@ pub fn bedrock_stream_to_response_events( cache_read_input_tokens, cache_write_input_tokens, &model_id, - is_summarization, + false, ); yield Ok(finished_event); }; @@ -537,12 +593,10 @@ fn build_user_query_message(task_id: &str, query_text: &str) -> ResponseEvent { timestamp: None, server_message_data: String::new(), citations: vec![], - message: Some(api::message::Message::UserQuery( - api::message::UserQuery { - query: query_text.to_string(), - ..Default::default() - }, - )), + message: Some(api::message::Message::UserQuery(api::message::UserQuery { + query: query_text.to_string(), + ..Default::default() + })), }; let action = ClientAction { @@ -656,7 +710,7 @@ pub(super) fn build_stream_finished( /// Nova Lite: input $0.06, output $0.24 /// Nova Micro: input $0.035, output $0.14 /// DeepSeek R1: input $1.35, output $5.40 -fn estimate_cost_cents( +pub fn estimate_cost_cents( input_tokens: u32, output_tokens: u32, cache_read_tokens: u32, @@ -666,23 +720,22 @@ fn estimate_cost_cents( let lower = model_id.to_lowercase(); // (input_per_1m, output_per_1m, cache_read_per_1m, cache_write_per_1m) in dollars - let (input_rate, output_rate, cache_read_rate, cache_write_rate) = - if lower.contains("opus") { - (15.0, 75.0, 1.50, 18.75) - } else if lower.contains("haiku") { - (0.80, 4.0, 0.08, 1.0) - } else if lower.contains("nova-pro") { - (0.80, 3.20, 0.0, 0.0) - } else if lower.contains("nova-lite") { - (0.06, 0.24, 0.0, 0.0) - } else if lower.contains("nova-micro") { - (0.035, 0.14, 0.0, 0.0) - } else if lower.contains("deepseek") { - (1.35, 5.40, 0.0, 0.0) - } else { - // Default to Sonnet pricing - (3.0, 15.0, 0.30, 3.75) - }; + let (input_rate, output_rate, cache_read_rate, cache_write_rate) = if lower.contains("opus") { + (15.0, 75.0, 1.50, 18.75) + } else if lower.contains("haiku") { + (0.80, 4.0, 0.08, 1.0) + } else if lower.contains("nova-pro") { + (0.80, 3.20, 0.0, 0.0) + } else if lower.contains("nova-lite") { + (0.06, 0.24, 0.0, 0.0) + } else if lower.contains("nova-micro") { + (0.035, 0.14, 0.0, 0.0) + } else if lower.contains("deepseek") { + (1.35, 5.40, 0.0, 0.0) + } else { + // Default to Sonnet pricing + (3.0, 15.0, 0.30, 3.75) + }; // Convert from dollars per 1M tokens to cents per token let input_cost = input_tokens as f64 * input_rate * 100.0 / 1_000_000.0; @@ -914,22 +967,22 @@ fn build_tool_call_message( .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); - Some(api::message::tool_call::Tool::WriteToLongRunningShellCommand( - api::message::tool_call::WriteToLongRunningShellCommand { - input: text_input.into_bytes(), - mode: None, - command_id: String::new(), - }, - )) - } - "read_shell_command_output" => { - Some(api::message::tool_call::Tool::ReadShellCommandOutput( - api::message::tool_call::ReadShellCommandOutput { - command_id: String::new(), - delay: None, - }, - )) + Some( + api::message::tool_call::Tool::WriteToLongRunningShellCommand( + api::message::tool_call::WriteToLongRunningShellCommand { + input: text_input.into_bytes(), + mode: None, + command_id: String::new(), + }, + ), + ) } + "read_shell_command_output" => Some(api::message::tool_call::Tool::ReadShellCommandOutput( + api::message::tool_call::ReadShellCommandOutput { + command_id: String::new(), + delay: None, + }, + )), "read_mcp_resource" => { let server_id = input .get("server_id") @@ -942,10 +995,7 @@ fn build_tool_call_message( .unwrap_or("") .to_string(); Some(api::message::tool_call::Tool::ReadMcpResource( - api::message::tool_call::ReadMcpResource { - uri, - server_id, - }, + api::message::tool_call::ReadMcpResource { uri, server_id }, )) } "read_documents" => { @@ -994,8 +1044,16 @@ fn build_tool_call_message( .filter_map(|d| { Some(api::message::tool_call::edit_documents::DocumentDiff { document_id: d.get("document_id")?.as_str()?.to_string(), - search: d.get("search").and_then(|v| v.as_str()).unwrap_or("").to_string(), - replace: d.get("replace").and_then(|v| v.as_str()).unwrap_or("").to_string(), + search: d + .get("search") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + replace: d + .get("replace") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), }) }) .collect() @@ -1006,20 +1064,34 @@ fn build_tool_call_message( )) } "start_agent" => { - let name = input.get("name").and_then(|v| v.as_str()).unwrap_or("").to_string(); - let prompt = input.get("prompt").and_then(|v| v.as_str()).unwrap_or("").to_string(); - Some(api::message::tool_call::Tool::StartAgent( - api::StartAgent { - name, - prompt, - execution_mode: None, - lifecycle_subscription: None, - }, - )) + let name = input + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + let prompt = input + .get("prompt") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + Some(api::message::tool_call::Tool::StartAgent(api::StartAgent { + name, + prompt, + execution_mode: None, + lifecycle_subscription: None, + })) } "send_message_to_agent" => { - let agent_id = input.get("agent_id").and_then(|v| v.as_str()).unwrap_or("").to_string(); - let message = input.get("message").and_then(|v| v.as_str()).unwrap_or("").to_string(); + let agent_id = input + .get("agent_id") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + let message = input + .get("message") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); Some(api::message::tool_call::Tool::SendMessageToAgent( api::SendMessageToAgent { addresses: vec![agent_id], @@ -1029,28 +1101,36 @@ fn build_tool_call_message( )) } "ask_user_question" => { - let question_text = input.get("question").and_then(|v| v.as_str()).unwrap_or("").to_string(); + let question_text = input + .get("question") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); let options: Vec = input .get("options") .and_then(|v| v.as_array()) .map(|arr| { arr.iter() .filter_map(|o| o.as_str()) - .map(|label| api::ask_user_question::Option { label: label.to_string() }) + .map(|label| api::ask_user_question::Option { + label: label.to_string(), + }) .collect() }) .unwrap_or_default(); let question = api::ask_user_question::Question { question_id: Uuid::new_v4().to_string(), question: question_text, - question_type: Some(api::ask_user_question::question::QuestionType::MultipleChoice( - api::ask_user_question::MultipleChoice { - options, - is_multiselect: false, - supports_other: true, - recommended_option_index: 0, - }, - )), + question_type: Some( + api::ask_user_question::question::QuestionType::MultipleChoice( + api::ask_user_question::MultipleChoice { + options, + is_multiselect: false, + supports_other: true, + recommended_option_index: 0, + }, + ), + ), }; Some(api::message::tool_call::Tool::AskUserQuestion( api::AskUserQuestion { @@ -1059,7 +1139,11 @@ fn build_tool_call_message( )) } "read_skill" => { - let skill = input.get("skill").and_then(|v| v.as_str()).unwrap_or("").to_string(); + let skill = input + .get("skill") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); Some(api::message::tool_call::Tool::ReadSkill( api::message::tool_call::ReadSkill { name: skill.clone(), @@ -1070,7 +1154,11 @@ fn build_tool_call_message( )) } "fetch_conversation" => { - let conversation_id = input.get("conversation_id").and_then(|v| v.as_str()).unwrap_or("").to_string(); + let conversation_id = input + .get("conversation_id") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); Some(api::message::tool_call::Tool::FetchConversation( api::message::tool_call::FetchConversation { conversation_id }, )) @@ -1103,7 +1191,10 @@ fn build_tool_call_message( let (server_name, mcp_tool_name) = if parts.len() == 3 { (parts[1].to_string(), parts[2].to_string()) } else { - (String::new(), name.strip_prefix("mcp__").unwrap_or(name).to_string()) + ( + String::new(), + name.strip_prefix("mcp__").unwrap_or(name).to_string(), + ) }; let args = json_to_prost_struct(&input); Some(api::message::tool_call::Tool::CallMcpTool( @@ -1193,8 +1284,143 @@ const KNOWN_TOOLS: &[&str] = &[ "suggest_next_prompt", "read_skill", "fetch_conversation", + "recall_tool_history", ]; fn is_known_tool(name: &str) -> bool { KNOWN_TOOLS.contains(&name) || name.starts_with("mcp__") } + +/// Searches conversation message history for tool call results matching the given criteria. +fn recall_from_history( + messages: &[ConversationMessage], + archive: &[ConversationMessage], + search_query: &str, + tool_name_filter: &str, + tool_use_id: &str, + offset_from_end: usize, +) -> String { + use super::convert::{ContentPart, MessageContent}; + + struct ToolEntry { + tool_use_id: String, + name: String, + input: String, + result: String, + } + + let mut tool_entries: Vec = Vec::new(); + + let mut pending_tool_uses: Vec<(String, String, String)> = Vec::new(); // (id, name, input) + + for msg in messages.iter().chain(archive.iter()) { + match &msg.content { + MessageContent::ToolUse { + tool_use_id, + name, + input, + } => { + pending_tool_uses.push((tool_use_id.clone(), name.clone(), input.to_string())); + } + MessageContent::ToolResult { + tool_use_id, + content, + .. + } => { + if let Some(pos) = pending_tool_uses + .iter() + .position(|(id, _, _)| id == tool_use_id) + { + let (tuid, name, input) = pending_tool_uses.remove(pos); + tool_entries.push(ToolEntry { + tool_use_id: tuid, + name, + input, + result: content.clone(), + }); + } + } + MessageContent::MultiPart(parts) => { + for part in parts { + match part { + ContentPart::ToolUse { + tool_use_id, + name, + input, + } => { + pending_tool_uses.push(( + tool_use_id.clone(), + name.clone(), + input.to_string(), + )); + } + ContentPart::ToolResult { + tool_use_id, + content, + .. + } => { + if let Some(pos) = pending_tool_uses + .iter() + .position(|(id, _, _)| id == tool_use_id) + { + let (tuid, name, input) = pending_tool_uses.remove(pos); + tool_entries.push(ToolEntry { + tool_use_id: tuid, + name, + input, + result: content.clone(), + }); + } + } + _ => {} + } + } + } + _ => {} + } + } + + let filtered: Vec<&ToolEntry> = tool_entries + .iter() + .filter(|entry| { + if !tool_use_id.is_empty() && entry.tool_use_id != tool_use_id { + return false; + } + if !tool_name_filter.is_empty() && entry.name != tool_name_filter { + return false; + } + if !search_query.is_empty() { + let haystack = format!("{} {} {}", entry.name, entry.input, entry.result); + let query_lower = search_query.to_lowercase(); + if !haystack.to_lowercase().contains(&query_lower) { + return false; + } + } + true + }) + .collect(); + + if filtered.is_empty() { + return "No matching tool calls found in conversation history.".to_string(); + } + + // Get the entry at offset_from_end (0 = most recent) + let idx = if offset_from_end >= filtered.len() { + 0 + } else { + filtered.len() - 1 - offset_from_end + }; + + let entry = &filtered[idx]; + let result_display = if entry.result.len() > 50000 { + let trunc = entry.result.chars().take(50000).collect::(); + format!("{trunc}... [truncated, {} total chars]", entry.result.len()) + } else { + entry.result.clone() + }; + + format!( + "Tool: {}\nTool Use ID: {}\nInput: {}\nResult:\n{}", + entry.name, entry.tool_use_id, entry.input, result_display + ) +} diff --git a/app/src/ai/bedrock/response_translator_tests.rs b/app/src/ai/bedrock/response_translator_tests.rs index dd5b7da9..0d9e6011 100644 --- a/app/src/ai/bedrock/response_translator_tests.rs +++ b/app/src/ai/bedrock/response_translator_tests.rs @@ -19,7 +19,7 @@ fn test_build_stream_init_has_valid_ids() { #[test] fn test_build_stream_finished_done_reason() { let reason = stream_finished::Reason::Done(stream_finished::Done {}); - let event = build_stream_finished(reason, 100, 50, 20, 10, "anthropic.claude-sonnet-4-6"); + let event = build_stream_finished(reason, 100, 50, 20, 10, "anthropic.claude-sonnet-4-6", false); match event.r#type { Some(api::response_event::Type::Finished(finished)) => { @@ -53,7 +53,7 @@ fn test_build_stream_finished_done_reason() { #[test] fn test_build_stream_finished_max_token_limit() { let reason = stream_finished::Reason::MaxTokenLimit(stream_finished::ReachedMaxTokenLimit {}); - let event = build_stream_finished(reason, 200, 100, 0, 0, "anthropic.claude-sonnet-4-6"); + let event = build_stream_finished(reason, 200, 100, 0, 0, "anthropic.claude-sonnet-4-6", false); match event.r#type { Some(api::response_event::Type::Finished(finished)) => { @@ -69,7 +69,7 @@ fn test_build_stream_finished_max_token_limit() { #[test] fn test_build_stream_finished_other_reason() { let reason = stream_finished::Reason::Other(stream_finished::Other {}); - let event = build_stream_finished(reason, 0, 0, 0, 0, "anthropic.claude-sonnet-4-6"); + let event = build_stream_finished(reason, 0, 0, 0, 0, "anthropic.claude-sonnet-4-6", false); match event.r#type { Some(api::response_event::Type::Finished(finished)) => { @@ -121,16 +121,34 @@ fn test_build_create_task_has_no_parent() { #[test] fn test_context_window_for_model_1m_marker() { - assert_eq!(context_window_for_model("anthropic.claude-opus-4-6[1m]"), 1_000_000); - assert_eq!(context_window_for_model("us.anthropic.claude-opus-4-6[1M]"), 1_000_000); - assert_eq!(context_window_for_model("anthropic.claude-sonnet-4-6[1m]"), 1_000_000); + assert_eq!( + context_window_for_model("anthropic.claude-opus-4-6[1m]"), + 1_000_000 + ); + assert_eq!( + context_window_for_model("us.anthropic.claude-opus-4-6[1M]"), + 1_000_000 + ); + assert_eq!( + context_window_for_model("anthropic.claude-sonnet-4-6[1m]"), + 1_000_000 + ); } #[test] fn test_context_window_for_model_standard_claude() { - assert_eq!(context_window_for_model("anthropic.claude-opus-4-6"), 200_000); - assert_eq!(context_window_for_model("us.anthropic.claude-sonnet-4-6"), 200_000); - assert_eq!(context_window_for_model("anthropic.claude-haiku-4-5-20251001-v1:0"), 200_000); + assert_eq!( + context_window_for_model("anthropic.claude-opus-4-6"), + 200_000 + ); + assert_eq!( + context_window_for_model("us.anthropic.claude-sonnet-4-6"), + 200_000 + ); + assert_eq!( + context_window_for_model("anthropic.claude-haiku-4-5-20251001-v1:0"), + 200_000 + ); } #[test] @@ -148,11 +166,35 @@ fn test_context_window_for_model_deepseek() { #[test] fn test_cost_varies_by_model() { let reason = stream_finished::Reason::Done(stream_finished::Done {}); - let opus_event = build_stream_finished(reason.clone(), 1000, 1000, 0, 0, "anthropic.claude-opus-4-6"); + let opus_event = build_stream_finished( + reason.clone(), + 1000, + 1000, + 0, + 0, + "anthropic.claude-opus-4-6", + false, + ); let reason = stream_finished::Reason::Done(stream_finished::Done {}); - let sonnet_event = build_stream_finished(reason.clone(), 1000, 1000, 0, 0, "anthropic.claude-sonnet-4-6"); + let sonnet_event = build_stream_finished( + reason.clone(), + 1000, + 1000, + 0, + 0, + "anthropic.claude-sonnet-4-6", + false, + ); let reason = stream_finished::Reason::Done(stream_finished::Done {}); - let haiku_event = build_stream_finished(reason, 1000, 1000, 0, 0, "anthropic.claude-haiku-4-5-20251001-v1:0"); + let haiku_event = build_stream_finished( + reason, + 1000, + 1000, + 0, + 0, + "anthropic.claude-haiku-4-5-20251001-v1:0", + false, + ); let opus_cost = match opus_event.r#type { Some(api::response_event::Type::Finished(f)) => f.token_usage[0].cost_in_cents, @@ -168,14 +210,20 @@ fn test_cost_varies_by_model() { }; assert!(opus_cost > sonnet_cost, "Opus should cost more than Sonnet"); - assert!(sonnet_cost > haiku_cost, "Sonnet should cost more than Haiku"); - assert!(haiku_cost > 0.0, "All costs should be positive for non-zero tokens"); + assert!( + sonnet_cost > haiku_cost, + "Sonnet should cost more than Haiku" + ); + assert!( + haiku_cost > 0.0, + "All costs should be positive for non-zero tokens" + ); } #[test] fn test_cost_zero_for_zero_tokens() { let reason = stream_finished::Reason::Done(stream_finished::Done {}); - let event = build_stream_finished(reason, 0, 0, 0, 0, "anthropic.claude-sonnet-4-6"); + let event = build_stream_finished(reason, 0, 0, 0, 0, "anthropic.claude-sonnet-4-6", false); let cost = match event.r#type { Some(api::response_event::Type::Finished(f)) => f.token_usage[0].cost_in_cents, diff --git a/app/src/ai/bedrock/settings_view.rs b/app/src/ai/bedrock/settings_view.rs index 675222e5..8d2f854c 100644 --- a/app/src/ai/bedrock/settings_view.rs +++ b/app/src/ai/bedrock/settings_view.rs @@ -3,9 +3,7 @@ use crate::ai::bedrock::external_config::ExternalBedrockConfig; use crate::appearance::Appearance; use crate::ui_components::blended_colors; use galaxyui::{ - elements::{ - Container, CornerRadius, CrossAxisAlignment, Flex, ParentElement, Radius, Text, - }, + elements::{Container, CornerRadius, CrossAxisAlignment, Flex, ParentElement, Radius, Text}, AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, }; diff --git a/app/src/ai/bedrock/translator.rs b/app/src/ai/bedrock/translator.rs index c36ff7fa..212a9761 100644 --- a/app/src/ai/bedrock/translator.rs +++ b/app/src/ai/bedrock/translator.rs @@ -13,9 +13,9 @@ pub struct TranslatorRequest { pub model_id: String, pub root_task_id: Option, pub bedrock_message_history: Vec, - pub bedrock_compact_summary: Option, + pub bedrock_tool_result_archive: Vec, + pub bedrock_progressive_summary: Option, pub bedrock_messages_sent: Arc>>, - pub is_summarization: bool, } pub async fn execute( @@ -45,7 +45,7 @@ pub async fn execute( let mut model_id = params.model_id; if model_id.is_empty() || model_id == "auto" { // Fall back to default if nothing is set - model_id = "us.anthropic.claude-opus-4-6".to_string(); + model_id = "us.anthropic.claude-opus-4-6[1m]".to_string(); } log::info!("[bedrock] Translator: model={model_id}, task_id={task_id}, needs_create_task={needs_create_task}"); @@ -60,13 +60,38 @@ pub async fn execute( request_translator::inject_input_messages_into_task(request); let new_input_messages = request_translator::extract_new_input_messages(request); + let new_input_count = new_input_messages.len(); + + let mut messages = Vec::new(); + + // Prepend progressive summary as the first message pair if present + if let Some(ref summary) = params.bedrock_progressive_summary { + use crate::ai::bedrock::convert::{MessageContent, MessageRole}; + messages.push(ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text(format!( + "\n{}\n\n\n\ + The above summarizes earlier conversation history. The detailed messages below are the most recent exchanges.", + summary + )), + }); + messages.push(ConversationMessage { + role: MessageRole::Assistant, + content: MessageContent::Text( + "Understood, I have the prior context. Continuing with the recent conversation." + .to_string(), + ), + }); + } + + let history_len = params.bedrock_message_history.len(); + messages.extend(params.bedrock_message_history); - let mut messages = params.bedrock_message_history; if !new_input_messages.is_empty() { log::info!( "[bedrock] Appending {} new input messages to history of {}", new_input_messages.len(), - messages.len() + history_len ); messages.extend(new_input_messages); } @@ -74,20 +99,24 @@ pub async fn execute( request_translator::sanitize_messages_for_bedrock(&mut messages); let system_prompt = request_translator::extract_system_prompt(request); - let compact_summary = params.bedrock_compact_summary; let tools = request_translator::extract_tools(request); log::info!( - "[bedrock] Sending {} messages, system_prompt={}, compact_summary={}, tools={}", + "[bedrock] Sending {} messages, system_prompt={}, progressive_summary={}, tools={}", messages.len(), system_prompt.is_some(), - compact_summary.is_some(), + params.bedrock_progressive_summary.is_some(), tools.len() ); for (i, msg) in messages.iter().enumerate() { let content_desc = describe_message_content(&msg.content); - log::info!("[bedrock] msg[{}]: role={:?}, content={}", i, msg.role, content_desc); + log::info!( + "[bedrock] msg[{}]: role={:?}, content={}", + i, + msg.role, + content_desc + ); } let user_query_text = request_translator::extract_user_query_text(request); @@ -99,7 +128,7 @@ pub async fn execute( needs_create_task, messages.clone(), system_prompt, - compact_summary, + None, // progressive summary is in messages array, not system prompt tools, 64000, None, @@ -107,12 +136,20 @@ pub async fn execute( user_query_text, diagnostic_logger, params.bedrock_messages_sent.clone(), - params.is_summarization, + params.bedrock_tool_result_archive, ) .await?; if let Ok(mut sent) = params.bedrock_messages_sent.lock() { - *sent = messages; + // Only persist the actual conversation history (history + new inputs), not the + // ephemeral prepended summary pair, so we don't duplicate the summary on every + // subsequent write-back. The summary is prepended at request time each turn. + let persistent_count = history_len + new_input_count; + if persistent_count > 0 && messages.len() >= persistent_count { + *sent = messages.split_off(messages.len() - persistent_count); + } else { + *sent = messages; + } } Ok(stream) diff --git a/app/src/ai/blocklist/action_model.rs b/app/src/ai/blocklist/action_model.rs index c90fbb49..e9dec001 100644 --- a/app/src/ai/blocklist/action_model.rs +++ b/app/src/ai/blocklist/action_model.rs @@ -482,7 +482,9 @@ impl BlocklistAIActionModel { .and_then(|queue| queue.front()) .cloned() else { - log::info!("[tool-debug] try_to_execute_available_actions: no more pending actions"); + log::info!( + "[tool-debug] try_to_execute_available_actions: no more pending actions" + ); return; }; diff --git a/app/src/ai/blocklist/action_model/execute.rs b/app/src/ai/blocklist/action_model/execute.rs index d98ee397..9ad7f9e1 100644 --- a/app/src/ai/blocklist/action_model/execute.rs +++ b/app/src/ai/blocklist/action_model/execute.rs @@ -739,14 +739,20 @@ impl BlocklistAIActionExecutor { ); match execution { AnyActionExecution::NotReady => { - log::info!("[tool-debug] try_to_execute_action: NOT READY - action_id={:?}", action_id); + log::info!( + "[tool-debug] try_to_execute_action: NOT READY - action_id={:?}", + action_id + ); TryExecuteResult::NotExecuted { reason: NotExecutedReason::NotReady, action: Box::new(action_clone), } } AnyActionExecution::InvalidAction => { - log::error!("[tool-debug] try_to_execute_action: INVALID ACTION - action_id={:?}", action_id); + log::error!( + "[tool-debug] try_to_execute_action: INVALID ACTION - action_id={:?}", + action_id + ); debug_assert!(false, "Tried to execute AIAgentAction with wrong executor."); TryExecuteResult::NotExecuted { reason: NotExecutedReason::NotReady, diff --git a/app/src/ai/blocklist/action_model/execute/ask_user_question.rs b/app/src/ai/blocklist/action_model/execute/ask_user_question.rs index aeea72ea..0a6faa92 100644 --- a/app/src/ai/blocklist/action_model/execute/ask_user_question.rs +++ b/app/src/ai/blocklist/action_model/execute/ask_user_question.rs @@ -55,7 +55,8 @@ impl AskUserQuestionExecutor { // For child agent conversations, route the question to the parent // for silent auto-answer instead of presenting UI to the user. - if let Some(parent_conversation_id) = self.parent_conversation_id(input.conversation_id, ctx) + if let Some(parent_conversation_id) = + self.parent_conversation_id(input.conversation_id, ctx) { let question_text = questions .iter() @@ -86,14 +87,12 @@ impl AskUserQuestionExecutor { async move { receiver.recv().await }, |result, _ctx| match result { Ok(AskUserQuestionDecision::Completed(answers)) => { - AIAgentActionResultType::AskUserQuestion( - AskUserQuestionResult::Success { answers }, - ) + AIAgentActionResultType::AskUserQuestion(AskUserQuestionResult::Success { + answers, + }) } Ok(AskUserQuestionDecision::Cancelled) | Err(_) => { - AIAgentActionResultType::AskUserQuestion( - AskUserQuestionResult::Cancelled, - ) + AIAgentActionResultType::AskUserQuestion(AskUserQuestionResult::Cancelled) } }, ); diff --git a/app/src/ai/blocklist/action_model/execute/call_mcp_tool.rs b/app/src/ai/blocklist/action_model/execute/call_mcp_tool.rs index e620f47d..e27227e8 100644 --- a/app/src/ai/blocklist/action_model/execute/call_mcp_tool.rs +++ b/app/src/ai/blocklist/action_model/execute/call_mcp_tool.rs @@ -141,12 +141,18 @@ impl CallMCPToolExecutor { }; let Some(reconnecting_peer) = templatable_peer else { - log::error!("[tool-debug] CallMCPToolExecutor: MCP server for tool '{}' NOT FOUND", name_owned); + log::error!( + "[tool-debug] CallMCPToolExecutor: MCP server for tool '{}' NOT FOUND", + name_owned + ); return ActionExecution::Sync(AIAgentActionResultType::CallMCPTool( CallMCPToolResult::Error("MCP server for tool not found".to_owned()), )); }; - log::info!("[tool-debug] CallMCPToolExecutor: found MCP server peer for tool '{}'", name_owned); + log::info!( + "[tool-debug] CallMCPToolExecutor: found MCP server peer for tool '{}'", + name_owned + ); let name_owned_inner = name_owned.clone(); ActionExecution::new_async( diff --git a/app/src/ai/blocklist/action_model/execute/file_glob.rs b/app/src/ai/blocklist/action_model/execute/file_glob.rs index 7fa0305c..7f21cd49 100644 --- a/app/src/ai/blocklist/action_model/execute/file_glob.rs +++ b/app/src/ai/blocklist/action_model/execute/file_glob.rs @@ -111,7 +111,11 @@ impl FileGlobExecutor { else { return ActionExecution::InvalidAction; }; - log::info!("[tool-debug] FileGlobExecutor::execute: patterns={:?}, path={:?}", patterns, path); + log::info!( + "[tool-debug] FileGlobExecutor::execute: patterns={:?}, path={:?}", + patterns, + path + ); // If the path is not provided, use the current working directory. let path = path.clone().unwrap_or_else(|| ".".to_string()); diff --git a/app/src/ai/blocklist/action_model/execute/grep.rs b/app/src/ai/blocklist/action_model/execute/grep.rs index 39896c01..7718b2dd 100644 --- a/app/src/ai/blocklist/action_model/execute/grep.rs +++ b/app/src/ai/blocklist/action_model/execute/grep.rs @@ -252,7 +252,11 @@ impl GrepExecutor { else { return ActionExecution::InvalidAction; }; - log::info!("[tool-debug] GrepExecutor::execute: queries={:?}, path={:?}", queries, path); + log::info!( + "[tool-debug] GrepExecutor::execute: queries={:?}, path={:?}", + queries, + path + ); let shell_launch_data = self.active_session.as_ref(ctx).shell_launch_data(ctx); let shell_type = self.active_session.as_ref(ctx).shell_type(ctx); diff --git a/app/src/ai/blocklist/action_model/execute/request_file_edits.rs b/app/src/ai/blocklist/action_model/execute/request_file_edits.rs index de49ee97..42d868ab 100644 --- a/app/src/ai/blocklist/action_model/execute/request_file_edits.rs +++ b/app/src/ai/blocklist/action_model/execute/request_file_edits.rs @@ -151,10 +151,16 @@ impl RequestFileEditsExecutor { else { return ActionExecution::InvalidAction; }; - log::info!("[tool-debug] RequestFileEditsExecutor::execute: action_id={:?}", id); + log::info!( + "[tool-debug] RequestFileEditsExecutor::execute: action_id={:?}", + id + ); let Some(diff_view) = self.diff_views.get(id) else { - log::warn!("[tool-debug] RequestFileEditsExecutor: no diff view found for action_id={:?}", id); + log::warn!( + "[tool-debug] RequestFileEditsExecutor: no diff view found for action_id={:?}", + id + ); return ActionExecution::NotReady; }; diff --git a/app/src/ai/blocklist/action_model/execute/start_agent.rs b/app/src/ai/blocklist/action_model/execute/start_agent.rs index 75126f31..abee58af 100644 --- a/app/src/ai/blocklist/action_model/execute/start_agent.rs +++ b/app/src/ai/blocklist/action_model/execute/start_agent.rs @@ -68,7 +68,9 @@ impl StartAgentExecutor { let history_model = BlocklistAIHistoryModel::handle(ctx); ctx.subscribe_to_model(&history_model, Self::handle_history_event); - Self { pending: Vec::new() } + Self { + pending: Vec::new(), + } } fn handle_history_event( @@ -91,8 +93,7 @@ impl StartAgentExecutor { let parent_id = conversation.parent_conversation_id(); // Find the first pending entry that matches this parent and hasn't been assigned a child yet. if let Some(pending) = self.pending.iter_mut().find(|p| { - p.child_conversation_id.is_none() - && parent_id == Some(p.parent_conversation_id) + p.child_conversation_id.is_none() && parent_id == Some(p.parent_conversation_id) }) { pending.child_conversation_id = Some(*new_conversation_id); } @@ -100,17 +101,19 @@ impl StartAgentExecutor { BlocklistAIHistoryEvent::ConversationServerTokenAssigned { conversation_id, .. } => { - let Some(idx) = self.pending.iter().position(|p| { - p.child_conversation_id.as_ref() == Some(conversation_id) - }) else { + let Some(idx) = self + .pending + .iter() + .position(|p| p.child_conversation_id.as_ref() == Some(conversation_id)) + else { return; }; // Don't remove yet if we're waiting for completion — we need // the entry to stay so UpdatedConversationStatus can find it. if self.pending[idx].wait_for_completion { // Just log and continue — we'll resolve on Success status. - let conversation = BlocklistAIHistoryModel::as_ref(ctx) - .conversation(conversation_id); + let conversation = + BlocklistAIHistoryModel::as_ref(ctx).conversation(conversation_id); let agent_id = conversation .and_then(|c| c.orchestration_agent_id()) .or_else(|| { @@ -132,8 +135,8 @@ impl StartAgentExecutor { return; } let pending = self.pending.remove(idx); - let conversation = BlocklistAIHistoryModel::as_ref(ctx) - .conversation(conversation_id); + let conversation = + BlocklistAIHistoryModel::as_ref(ctx).conversation(conversation_id); // orchestration_agent_id() uses run_id in v2 mode, which won't // exist for locally-spawned Bedrock child agents. Fall back to // the server conversation token (set by the stream Init event) @@ -195,9 +198,11 @@ impl StartAgentExecutor { BlocklistAIHistoryEvent::UpdatedConversationStatus { conversation_id, .. } => { - let Some(idx) = self.pending.iter().position(|p| { - p.child_conversation_id.as_ref() == Some(conversation_id) - }) else { + let Some(idx) = self + .pending + .iter() + .position(|p| p.child_conversation_id.as_ref() == Some(conversation_id)) + else { return; }; let history = BlocklistAIHistoryModel::as_ref(ctx); @@ -227,10 +232,9 @@ impl StartAgentExecutor { .map(|t| t.as_str().to_string()) }) .unwrap_or_else(|| conversation_id.to_string()); - let _ = pending.sender.try_send(StartAgentDecision::Completed { - agent_id, - output, - }); + let _ = pending + .sender + .try_send(StartAgentDecision::Completed { agent_id, output }); } status => { let error_msg = start_agent_error_message_for_status( diff --git a/app/src/ai/blocklist/action_model/execute/start_agent_tests.rs b/app/src/ai/blocklist/action_model/execute/start_agent_tests.rs index e9a882c2..1325c621 100644 --- a/app/src/ai/blocklist/action_model/execute/start_agent_tests.rs +++ b/app/src/ai/blocklist/action_model/execute/start_agent_tests.rs @@ -72,7 +72,7 @@ fn execute_returns_error_when_child_startup_is_blocked_before_initialization() { assert_eq!( executor .pending - .as_ref() + .first() .and_then(|pending| pending.child_conversation_id), Some(child_conversation_id) ); @@ -102,7 +102,7 @@ fn execute_returns_error_when_child_startup_is_blocked_before_initialization() { )); executor.read(&app, |executor, _| { - assert!(executor.pending.is_none()); + assert!(executor.pending.is_empty()); }); }); } diff --git a/app/src/ai/blocklist/agent_view/subagent_inline_panel.rs b/app/src/ai/blocklist/agent_view/subagent_inline_panel.rs index ec716baf..d8b5fe6c 100644 --- a/app/src/ai/blocklist/agent_view/subagent_inline_panel.rs +++ b/app/src/ai/blocklist/agent_view/subagent_inline_panel.rs @@ -60,10 +60,7 @@ pub fn render_subagent_inline_panel( }; let status = conversation.status().clone(); - let agent_name = conversation - .agent_name() - .unwrap_or("Subagent") - .to_string(); + let agent_name = conversation.agent_name().unwrap_or("Subagent").to_string(); let panel_bg = blended_colors::neutral_2(theme); @@ -76,13 +73,7 @@ pub fn render_subagent_inline_panel( let header_expanded = state.is_expanded; column.add_child( Hoverable::new(header_mouse_state, move |_mouse_state| { - render_panel_header( - &agent_name, - &header_status, - header_expanded, - panel_bg, - app, - ) + render_panel_header(&agent_name, &header_status, header_expanded, panel_bg, app) }) .on_click(move |ctx, _, _| { ctx.dispatch_typed_action(AIBlockAction::ToggleSubagentPanel { @@ -137,12 +128,11 @@ fn render_panel_header( let mut left_side = Flex::row().with_cross_axis_alignment(CrossAxisAlignment::Center); let (icon, icon_color) = status.status_icon_and_color(theme); - let status_icon_element = ConstrainedBox::new( - galaxyui::elements::Icon::new(icon.into(), icon_color).finish(), - ) - .with_width(icon_size(app)) - .with_height(icon_size(app)) - .finish(); + let status_icon_element = + ConstrainedBox::new(galaxyui::elements::Icon::new(icon.into(), icon_color).finish()) + .with_width(icon_size(app)) + .with_height(icon_size(app)) + .finish(); left_side.add_child( Container::new(status_icon_element) @@ -192,11 +182,7 @@ fn render_panel_header( .with_width(icon_size(app)) .with_height(icon_size(app)) .finish(); - right_side.add_child( - Container::new(chevron) - .with_margin_right(4.) - .finish(), - ); + right_side.add_child(Container::new(chevron).with_margin_right(4.).finish()); header_row.add_child(right_side.finish()); @@ -208,10 +194,7 @@ fn render_panel_header( .finish() } -fn collect_mini_transcript( - conversation_id: &AIConversationId, - app: &AppContext, -) -> Vec { +fn collect_mini_transcript(conversation_id: &AIConversationId, app: &AppContext) -> Vec { let history_model = BlocklistAIHistoryModel::as_ref(app); let Some(conversation) = history_model.conversation(conversation_id) else { return vec![]; @@ -290,10 +273,7 @@ fn render_mini_transcript( .finish() } -fn get_completion_summary( - conversation_id: &AIConversationId, - app: &AppContext, -) -> Option { +fn get_completion_summary(conversation_id: &AIConversationId, app: &AppContext) -> Option { let history_model = BlocklistAIHistoryModel::as_ref(app); let conversation = history_model.conversation(conversation_id)?; @@ -317,11 +297,7 @@ fn get_completion_summary( None } -fn render_summary_footer( - summary: &str, - _background: ColorU, - app: &AppContext, -) -> Box { +fn render_summary_footer(summary: &str, _background: ColorU, app: &AppContext) -> Box { let appearance = Appearance::as_ref(app); let theme = appearance.theme(); let surface = theme.surface_2(); diff --git a/app/src/ai/blocklist/block.rs b/app/src/ai/blocklist/block.rs index 271aa5ea..d1459e4b 100644 --- a/app/src/ai/blocklist/block.rs +++ b/app/src/ai/blocklist/block.rs @@ -391,12 +391,12 @@ pub(super) struct AIBlockStateHandles { /// Mouse state handle for the fork conversation button fork_conversation_handle: MouseStateHandle, - /// Mouse state handles per citation. /// A given citation should only appear once per block. footer_citation_chip_handles: HashMap, orchestration_navigation_card_handles: HashMap, - pub(super) subagent_panel_states: HashMap, + pub(super) subagent_panel_states: + HashMap, references_section_collapsible_handle: MouseStateHandle, diff --git a/app/src/ai/blocklist/block/status_bar.rs b/app/src/ai/blocklist/block/status_bar.rs index 8b44a77a..f06c3285 100644 --- a/app/src/ai/blocklist/block/status_bar.rs +++ b/app/src/ai/blocklist/block/status_bar.rs @@ -4,10 +4,9 @@ use super::{ cli_controller::{CLISubagentController, CLISubagentEvent, UserTakeOverReason}, model::{AIBlockModel, AIBlockModelImpl, AIBlockOutputStatus}, view_impl::common::{ - render_switch_control_to_user_button, render_warping_indicator, + random_load_output_message, render_switch_control_to_user_button, render_warping_indicator, render_warping_indicator_base, ButtonProps, ForceRefreshButtonProps, MaybeShimmeringText, - WarpingIndicatorProps, WarpingProps, random_load_output_message, - WAITING_FOR_USER_INPUT_MESSAGE, + WarpingIndicatorProps, WarpingProps, WAITING_FOR_USER_INPUT_MESSAGE, }, }; use crate::{ diff --git a/app/src/ai/blocklist/block/view_impl/common.rs b/app/src/ai/blocklist/block/view_impl/common.rs index 0665b817..07181f0a 100644 --- a/app/src/ai/blocklist/block/view_impl/common.rs +++ b/app/src/ai/blocklist/block/view_impl/common.rs @@ -474,8 +474,10 @@ pub fn render_warping_indicator( } else { // Show elapsed timer alongside the random Galaxy status message. if let Some(start_time) = props.warping_start_time { - non_shimmering_text = - Some(format!(" ({})", format_elapsed_compact(start_time.elapsed()))); + non_shimmering_text = Some(format!( + " ({})", + format_elapsed_compact(start_time.elapsed()) + )); } props.default_warping_text.clone() } @@ -3430,19 +3432,59 @@ pub struct FindContext<'a> { /// A palette of colors for the user avatar silhouette, randomly selected per session. const USER_AVATAR_PALETTE: &[ColorU] = &[ - ColorU { r: 99, g: 179, b: 237, a: 255 }, // blue - ColorU { r: 129, g: 230, b: 217, a: 255 }, // teal - ColorU { r: 183, g: 148, b: 244, a: 255 }, // purple - ColorU { r: 252, g: 165, b: 165, a: 255 }, // red/coral - ColorU { r: 251, g: 191, b: 36, a: 255 }, // amber - ColorU { r: 110, g: 231, b: 183, a: 255 }, // green - ColorU { r: 249, g: 168, b: 212, a: 255 }, // pink - ColorU { r: 253, g: 186, b: 116, a: 255 }, // orange + ColorU { + r: 99, + g: 179, + b: 237, + a: 255, + }, // blue + ColorU { + r: 129, + g: 230, + b: 217, + a: 255, + }, // teal + ColorU { + r: 183, + g: 148, + b: 244, + a: 255, + }, // purple + ColorU { + r: 252, + g: 165, + b: 165, + a: 255, + }, // red/coral + ColorU { + r: 251, + g: 191, + b: 36, + a: 255, + }, // amber + ColorU { + r: 110, + g: 231, + b: 183, + a: 255, + }, // green + ColorU { + r: 249, + g: 168, + b: 212, + a: 255, + }, // pink + ColorU { + r: 253, + g: 186, + b: 116, + a: 255, + }, // orange ]; fn session_avatar_color() -> ColorU { - use std::sync::OnceLock; use rand::Rng; + use std::sync::OnceLock; static COLOR: OnceLock = OnceLock::new(); *COLOR.get_or_init(|| { let idx = rand::thread_rng().gen_range(0..USER_AVATAR_PALETTE.len()); diff --git a/app/src/ai/blocklist/block/view_impl/common_tests.rs b/app/src/ai/blocklist/block/view_impl/common_tests.rs index 4e3470b7..b723f9dc 100644 --- a/app/src/ai/blocklist/block/view_impl/common_tests.rs +++ b/app/src/ai/blocklist/block/view_impl/common_tests.rs @@ -11,9 +11,8 @@ use super::{blocklist_image_asset_source, ResolvedBlocklistImageSources}; use super::{ collect_visual_markdown_lightbox_collection, compute_visual_section_width, format_elapsed_compact, inline_image_source_label, lightbox_trigger_for_section, - query_prefix_highlight_len, render_scrollable_collapsible_content, - text_sections_with_indices, CollapsibleElementState, CollapsibleExpansionState, - VisualMarkdownLightboxCollection, + query_prefix_highlight_len, render_scrollable_collapsible_content, text_sections_with_indices, + CollapsibleElementState, CollapsibleExpansionState, VisualMarkdownLightboxCollection, }; use crate::{ ai::agent::{ @@ -308,7 +307,10 @@ fn format_elapsed_compact_shows_minutes_under_120_minutes() { assert_eq!(format_elapsed_compact(Duration::from_secs(90)), "1m"); assert_eq!(format_elapsed_compact(Duration::from_secs(120)), "2m"); assert_eq!(format_elapsed_compact(Duration::from_secs(45 * 60)), "45m"); - assert_eq!(format_elapsed_compact(Duration::from_secs(119 * 60 + 59)), "119m"); + assert_eq!( + format_elapsed_compact(Duration::from_secs(119 * 60 + 59)), + "119m" + ); } #[test] diff --git a/app/src/ai/blocklist/block/view_impl/orchestration.rs b/app/src/ai/blocklist/block/view_impl/orchestration.rs index 7dea59e0..9ff25642 100644 --- a/app/src/ai/blocklist/block/view_impl/orchestration.rs +++ b/app/src/ai/blocklist/block/view_impl/orchestration.rs @@ -433,9 +433,7 @@ pub(super) fn render_start_agent( } if let Some(card_data) = child_conversation_card_data { // Render inline subagent panel instead of navigation card - if let Some(panel_state) = - props.state_handles.subagent_panel_states.get(action_id) - { + if let Some(panel_state) = props.state_handles.subagent_panel_states.get(action_id) { column.add_child( crate::ai::blocklist::agent_view::subagent_inline_panel::render_subagent_inline_panel( panel_state, diff --git a/app/src/ai/blocklist/block/view_impl/output.rs b/app/src/ai/blocklist/block/view_impl/output.rs index c3cc81f3..26602fb6 100644 --- a/app/src/ai/blocklist/block/view_impl/output.rs +++ b/app/src/ai/blocklist/block/view_impl/output.rs @@ -85,8 +85,8 @@ use crate::{ }, requested_command::RequestedCommand, search_codebase::SearchCodebaseView, - summarization::SummarizationView, suggested_unit_tests::SuggestedUnitTestsView, + summarization::SummarizationView, web_fetch::WebFetchView, web_search::WebSearchView, }, @@ -127,8 +127,8 @@ use super::{ use galaxyui::{ elements::{ Align, Border, ChildView, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, - Expanded, Fill, Flex, FormattedTextElement, Hoverable, MainAxisAlignment, - MainAxisSize, ParentElement, Radius, Shrinkable, Text, Wrap, + Expanded, Fill, Flex, FormattedTextElement, Hoverable, MainAxisAlignment, MainAxisSize, + ParentElement, Radius, Shrinkable, Text, Wrap, }, keymap::Keystroke, platform::{Cursor, OperatingSystem}, @@ -217,9 +217,7 @@ pub(super) fn render(props: Props, app: &AppContext) -> Box { .iter() .any(|i| matches!(i, AIAgentInput::SummarizeConversation { .. })); if is_summarize_input { - let key = crate::ai::agent::MessageId::new( - "__summarization_inline_view__".to_string(), - ); + let key = crate::ai::agent::MessageId::new("__summarization_inline_view__".to_string()); if let Some(summarization_view) = props.summarization_views.get(&key) { output_items.add_child(ChildView::new(summarization_view).finish()); } @@ -827,8 +825,7 @@ pub(super) fn render(props: Props, app: &AppContext) -> Box { if let Some(summarization_view) = props.summarization_views.get(&output_message.id) { - output_items - .add_child(ChildView::new(summarization_view).finish()); + output_items.add_child(ChildView::new(summarization_view).finish()); } else if !are_all_text_sections_empty(&text.sections) { let header_text = "Conversation summarized".to_string(); if let Some(element) = render_collapsible_block( @@ -3178,7 +3175,6 @@ fn render_response_footer(props: Props, app: &AppContext) -> Option Option( action_id: &AIAgentActionId, action_model: &ModelHandle, diff --git a/app/src/ai/blocklist/controller.rs b/app/src/ai/blocklist/controller.rs index 5449191c..945c3bd5 100644 --- a/app/src/ai/blocklist/controller.rs +++ b/app/src/ai/blocklist/controller.rs @@ -73,7 +73,7 @@ use itertools::Itertools; use parking_lot::FairMutex; use pending_response_streams::PendingResponseStreams; use session_sharing_protocol::common::ParticipantId; -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::sync::Arc; use std::time::Duration; use warp_multi_agent_api::{message, Task, ToolType}; @@ -168,6 +168,60 @@ pub enum BlocklistAIControllerEvent { FreeTierLimitCheckTriggered, } +/// Tracks recent failed action signatures for loop detection. +/// When the same tool+input pattern fails repeatedly, we inject +/// corrective instructions to break the cycle. +#[derive(Debug, Clone)] +struct LoopDetectionEntry { + /// Discriminant of the action result type (e.g. RequestCommandOutput, ApplyFileDiffs) + tool_discriminant: std::mem::Discriminant, + /// Hash of the action's identifying input (command string, file paths, etc.) + input_hash: u64, + /// Human-readable description of what failed + description: String, +} + +#[derive(Debug, Default, Clone)] +struct LoopDetectionState { + recent_failures: VecDeque, +} + +const LOOP_DETECTION_WINDOW: usize = 10; +const LOOP_DETECTION_THRESHOLD: usize = 3; + +impl LoopDetectionState { + fn record_failure(&mut self, entry: LoopDetectionEntry) { + self.recent_failures.push_back(entry); + if self.recent_failures.len() > LOOP_DETECTION_WINDOW { + self.recent_failures.pop_front(); + } + } + + fn detect_loop(&self) -> Option<&LoopDetectionEntry> { + use std::collections::HashMap as CountMap; + let mut counts: CountMap< + (std::mem::Discriminant, u64), + (usize, usize), + > = CountMap::new(); + for (idx, entry) in self.recent_failures.iter().enumerate() { + let key = (entry.tool_discriminant, entry.input_hash); + let counter = counts.entry(key).or_insert((0, 0)); + counter.0 += 1; + counter.1 = idx; // Track most recent occurrence + } + for ((_disc, _hash), (count, latest_idx)) in &counts { + if *count >= LOOP_DETECTION_THRESHOLD { + return self.recent_failures.get(*latest_idx); + } + } + None + } + + fn clear(&mut self) { + self.recent_failures.clear(); + } +} + #[derive(Debug)] pub struct RequestInput { pub conversation_id: AIConversationId, @@ -317,6 +371,9 @@ pub struct BlocklistAIController { pending_auto_resume_handles: HashMap, /// Passive conversations explicitly requested to follow up after actions complete. pending_passive_follow_ups: HashSet, + + /// Per-conversation loop detection state for preventing recursive tool failures. + loop_detection: HashMap, /// Passive suggestion results that should be included with the next request /// for a given conversation (e.g. accepted/iterated code diffs that weren't /// auto-resumed). @@ -555,6 +612,7 @@ impl BlocklistAIController { pending_auto_resume_handles: HashMap::new(), pending_passive_follow_ups: HashSet::new(), pending_passive_suggestion_results: HashMap::new(), + loop_detection: HashMap::new(), } } @@ -1026,6 +1084,9 @@ impl BlocklistAIController { is_queued_prompt: bool, ctx: &mut ModelContext, ) { + // User sending a new query resets loop detection — fresh context. + self.loop_detection.remove(&conversation_id); + let is_viewer = self .terminal_model .lock() @@ -1418,6 +1479,9 @@ impl BlocklistAIController { return; } + // Loop detection: record failures and check for repeated patterns + let loop_warning = self.check_and_record_loop_detection(conversation_id, &finished_results); + // Check whether any result will trigger a server-side subagent (e.g. CLI // subagent for LRC), or if one is already active. If so, we must not // piggyback orchestration events because the subagent cannot interpret @@ -1447,6 +1511,34 @@ impl BlocklistAIController { ctx, ); + // If a loop was detected, inject a corrective instruction alongside + // the action results so the model avoids repeating the same failure. + if let Some(warning_msg) = loop_warning { + log::warn!( + "[loop-detection] Injecting corrective instruction for conversation {:?}: {}", + conversation_id, + warning_msg + ); + if let Some(conversation) = + BlocklistAIHistoryModel::as_ref(ctx).conversation(&conversation_id) + { + let root_task_id = conversation.get_root_task_id().clone(); + request_input + .input_messages + .entry(root_task_id) + .or_default() + .push(AIAgentInput::UserQuery { + query: warning_msg, + context: Arc::from([]), + static_query_type: None, + referenced_attachments: HashMap::new(), + user_query_mode: UserQueryMode::Normal, + running_command: None, + intended_agent: None, + }); + } + } + // Include any pending orchestration events in this follow-up rather // than waiting for a separate idle injection turn. Skip when a server // subagent is or will be active — events will be delivered via the idle @@ -1495,6 +1587,67 @@ impl BlocklistAIController { self.pending_passive_follow_ups.remove(&conversation_id); } + /// Records failed actions into the loop detection state and returns a + /// corrective instruction if a loop is detected. + fn check_and_record_loop_detection( + &mut self, + conversation_id: AIConversationId, + results: &[AIAgentActionResult], + ) -> Option { + use std::hash::{Hash, Hasher}; + + let state = self.loop_detection.entry(conversation_id).or_default(); + let mut has_success = false; + + for result in results { + if result.result.is_failed() { + let discriminant = std::mem::discriminant(&result.result); + // Use a stable description that includes the tool type and the *input* + // (command, file paths, etc.) but NOT the variable output, so the same + // failing command with different output is still recognized as a loop. + let description = result.result.loop_description(); + + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + discriminant.hash(&mut hasher); + description.hash(&mut hasher); + let input_hash = hasher.finish(); + + state.record_failure(LoopDetectionEntry { + tool_discriminant: discriminant, + input_hash, + description: description.clone(), + }); + } else if result.result.is_successful() { + has_success = true; + } + } + + // If we had at least one success in this batch, clear loop state — + // the agent is making progress. + if has_success { + state.clear(); + return None; + } + + // Check for loops + if let Some(looping_entry) = state.detect_loop() { + let warning = format!( + "[SYSTEM] Loop detected: the same action has failed {} or more times consecutively. \ + Do NOT repeat this action or any similar approach.\n\n\ + Failing action: {}\n\n\ + Take a completely different approach to accomplish the goal. \ + If you cannot find an alternative, explain to the user what is failing and why.", + LOOP_DETECTION_THRESHOLD, + looping_entry.description + ); + // Clear the state so we don't keep injecting on every subsequent turn + state.clear(); + Some(warning) + } else { + None + } + } + /// Handles the EventsReady signal. Checks readiness, drains /// pending events from the service, and injects them into the conversation. fn handle_pending_events_ready( @@ -1566,9 +1719,8 @@ impl BlocklistAIController { conversation_id: AIConversationId, ctx: &mut ModelContext, ) { - let events = OrchestrationEventService::handle(ctx).update(ctx, |svc, _ctx| { - svc.drain_subagent_events(&conversation_id) - }); + let events = OrchestrationEventService::handle(ctx) + .update(ctx, |svc, _ctx| svc.drain_subagent_events(&conversation_id)); for event in events { match event.detail { @@ -1577,17 +1729,16 @@ impl BlocklistAIController { question_text, options, } => { - let answer = options.first().cloned().unwrap_or_else(|| { - format!("Proceed with: {}", question_text) - }); + let answer = options + .first() + .cloned() + .unwrap_or_else(|| format!("Proceed with: {}", question_text)); OrchestrationEventService::handle(ctx).update(ctx, |svc, ctx| { svc.route_answer_to_subagent(source_conversation_id, answer, ctx); }); } - PendingEventDetail::SubagentAnswer { - answer_text, - } => { + PendingEventDetail::SubagentAnswer { answer_text } => { self.complete_ask_user_question_with_answer(answer_text, ctx); } PendingEventDetail::SubagentCompletionSummary => {} @@ -1604,7 +1755,10 @@ impl BlocklistAIController { ) { use ai::agent::action_result::AskUserQuestionAnswerItem; - let executor = self.action_model.as_ref(ctx).ask_user_question_executor(ctx); + let executor = self + .action_model + .as_ref(ctx) + .ask_user_question_executor(ctx); let answer_item = AskUserQuestionAnswerItem::Answered { question_id: String::new(), selected_options: vec![answer_text.clone()], @@ -1963,7 +2117,8 @@ impl BlocklistAIController { parent_agent_id, agent_name, bedrock_history, - bedrock_compact_summary, + bedrock_tool_result_archive, + bedrock_progressive_summary, ) = { let Some(conversation) = history_model .as_ref(ctx) @@ -1987,7 +2142,8 @@ impl BlocklistAIController { conversation.parent_agent_id().map(str::to_string), conversation.agent_name().map(str::to_string), conversation.bedrock_message_history().to_vec(), - conversation.compact_summary().map(str::to_string), + conversation.tool_result_archive().to_vec(), + conversation.progressive_summary().map(str::to_string), ) }; @@ -2055,11 +2211,8 @@ impl BlocklistAIController { request_params.parent_agent_id = parent_agent_id; request_params.agent_name = agent_name; request_params.bedrock_message_history = bedrock_history; - request_params.bedrock_compact_summary = bedrock_compact_summary; - request_params.is_summarization = request_input - .all_inputs() - .any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. })); - + request_params.bedrock_tool_result_archive = bedrock_tool_result_archive; + request_params.bedrock_progressive_summary = bedrock_progressive_summary; let server_conversation_token_for_identifiers = conversation_data.server_conversation_token.clone(); @@ -2084,13 +2237,9 @@ impl BlocklistAIController { let input_contains_user_query = request_input .all_inputs() .any(|input| input.is_user_query()); - let input_is_summarization = request_input - .all_inputs() - .any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. })); ctx.subscribe_to_model(&response_stream, move |me, event, ctx| { me.handle_response_stream_event( input_contains_user_query, - input_is_summarization, event, &response_stream_clone, ctx, @@ -2280,7 +2429,6 @@ impl BlocklistAIController { fn handle_response_stream_event( &mut self, did_input_contain_user_query: bool, - is_summarization_request: bool, event: &ResponseStreamEvent, response_stream: &ModelHandle, ctx: &mut ModelContext, @@ -2384,69 +2532,32 @@ impl BlocklistAIController { Some(sent.clone()) } }); - if let Some(new_history) = new_history { + if let Some(mut new_history) = new_history { let history_model = BlocklistAIHistoryModel::handle(ctx); history_model.update(ctx, |history_model, _| { - if let Some(conversation) = history_model.conversation_mut(&conversation_id) { - // If this was a summarization request, compact the - // history to just the summary instead of keeping - // the full message list. This is what actually - // frees up context window space. - let is_summarization = is_summarization_request; - - if is_summarization { - // Extract the assistant's summary from the last - // message in the history (the response). - let summary_text = new_history + if let Some(conversation) = + history_model.conversation_mut(&conversation_id) + { + let skip = conversation.messages_summarized_up_to(); + if skip > 0 && skip <= new_history.len() { + let drained: Vec<_> = new_history .iter() - .rev() - .find_map(|msg| { - use crate::ai::bedrock::convert::{ - MessageContent, MessageRole, - }; - if msg.role == MessageRole::Assistant { - if let MessageContent::Text(text) = - &msg.content - { - Some(text.clone()) - } else { - None - } - } else { - None - } - }); - - if let Some(summary) = summary_text { - log::info!( - "[bedrock] Compacted conversation history from {} messages to system-level summary", - new_history.len() - ); - conversation.set_compact_summary(Some(summary.clone())); - *conversation.bedrock_message_history_mut() = Vec::new(); - - let estimated_tokens = (summary.len() / 4) as u32; - let max_context = crate::ai::bedrock::response_translator::context_window_for_model("claude-opus-4-6-20250514[1m]"); - let new_usage = estimated_tokens as f32 / max_context as f32; - conversation.set_context_window_usage(new_usage); - conversation.set_current_context_tokens(estimated_tokens); - log::info!( - "[bedrock] Post-compact context estimate: ~{} tokens ({:.1}% of context window)", - estimated_tokens, - new_usage * 100.0 - ); - } else { - *conversation.bedrock_message_history_mut() = - new_history; - } + .take(skip) + .cloned() + .collect(); + conversation.archive_tool_results(drained); + let reconciled = new_history.split_off(skip); + conversation.reset_messages_summarized_up_to(); + *conversation.bedrock_message_history_mut() = + reconciled; } else { *conversation.bedrock_message_history_mut() = new_history; - log::info!( - "[bedrock] Updated conversation bedrock history: {} messages", - conversation.bedrock_message_history().len() - ); } + log::info!( + "[bedrock] Updated conversation bedrock history: {} messages", + conversation.bedrock_message_history().len() + ); } }); } @@ -2488,22 +2599,24 @@ impl BlocklistAIController { }); } - let mut renderable_error: RenderableAIError = - if let AIApiError::Stream { stream_type, source } = e.as_ref() { - if *stream_type == "bedrock_converse" - && is_bedrock_credentials_error(&source.to_string()) - { - let model_name = - response_stream.as_ref(ctx).model_id().to_string(); - RenderableAIError::AwsBedrockCredentialsExpiredOrInvalid { - model_name, - } - } else { - e.as_ref().into() + let mut renderable_error: RenderableAIError = if let AIApiError::Stream { + stream_type, + source, + } = e.as_ref() + { + if *stream_type == "bedrock_converse" + && is_bedrock_credentials_error(&source.to_string()) + { + let model_name = response_stream.as_ref(ctx).model_id().to_string(); + RenderableAIError::AwsBedrockCredentialsExpiredOrInvalid { + model_name, } } else { e.as_ref().into() - }; + } + } else { + e.as_ref().into() + }; if let RenderableAIError::Other { will_attempt_resume, waiting_for_network, @@ -2555,7 +2668,9 @@ impl BlocklistAIController { log::warn!("Conversation not found."); return; }; - let new_exchange_ids: Vec<_> = conversation.new_exchange_ids_for_response(&stream_id).collect(); + let new_exchange_ids: Vec<_> = conversation + .new_exchange_ids_for_response(&stream_id) + .collect(); log::info!( "[bedrock-debug] AfterStreamFinished: stream_id={:?}, conversation_id={:?}, new_exchange_ids count={}", stream_id, conversation_id, new_exchange_ids.len() @@ -2963,42 +3078,311 @@ impl BlocklistAIController { ctx.emit(BlocklistAIControllerEvent::FreeTierLimitCheckTriggered); } - // Auto-compact: trigger summarization when context window usage >= 85%. - let should_auto_compact = { + // Progressive summarization: when context window usage >= 85% and we have + // more than 100 messages, summarize the oldest messages while keeping the + // most recent 100 verbatim. This runs as a background Bedrock call — no UI, + // no exchange created, no tool execution shown. + let should_progressive_summarize = { let history_model = BlocklistAIHistoryModel::as_ref(ctx); history_model .conversation(&conversation_id) .is_some_and(|conversation| { - let is_summarization_request = conversation - .latest_exchange() - .is_some_and(|exchange| { + let is_summarization_request = + conversation.latest_exchange().is_some_and(|exchange| { exchange .input .iter() .any(|i| matches!(i, AIAgentInput::SummarizeConversation { .. })) }); conversation.context_window_usage() >= 0.85 - && !conversation.has_pending_auto_compact() + && !conversation.has_pending_progressive_summary() && !is_summarization_request + && conversation.bedrock_message_history().len() > 100 }) }; - if should_auto_compact { - log::info!( - "[auto-compact] Context window usage >= 85% for conversation {:?}, triggering summarization", - conversation_id - ); - BlocklistAIHistoryModel::handle(ctx).update(ctx, |history_model, _| { - if let Some(conversation) = history_model.conversation_mut(&conversation_id) { - conversation.set_has_pending_auto_compact(true); - } - }); - self.send_slash_command_request( - SlashCommandRequest::Summarize { prompt: None }, - ctx, - ); + if should_progressive_summarize { + self.trigger_progressive_summarization(conversation_id, ctx); } } + + fn trigger_progressive_summarization( + &mut self, + conversation_id: AIConversationId, + ctx: &mut ModelContext, + ) { + use crate::ai::bedrock::client::{BedrockClient, BedrockClientConfig}; + use crate::ai::bedrock::convert::{ConversationMessage, MessageContent, MessageRole}; + use crate::ai::bedrock::response_translator::{ + context_window_for_model, estimate_cost_cents, + }; + use crate::settings::ai::AISettings; + use settings::Setting; + + let settings = AISettings::as_ref(ctx); + if !*settings.bedrock_enabled.value() { + return; + } + + let config = BedrockClientConfig { + auth_method: *settings.bedrock_auth_method.value(), + profile: settings.bedrock_profile.value().clone(), + region: settings.bedrock_region.value().clone(), + access_key_id: settings.bedrock_access_key_id.value().clone(), + secret_access_key: settings.bedrock_secret_access_key.value().clone(), + cross_region_inference: *settings.bedrock_cross_region_inference.value(), + } + .with_external_fallbacks(); + + let cross_region = config.cross_region_inference; + // Use Sonnet for summarization — cheaper and fast enough for this task + let model_id = "us.anthropic.claude-sonnet-4-6-20250514-v1:0".to_string(); + + let history_model = BlocklistAIHistoryModel::handle(ctx); + + // Extract the messages to summarize and set the guard flag + let (messages_to_summarize, existing_summary, messages_count) = { + let history = history_model.as_ref(ctx); + let Some(conversation) = history.conversation(&conversation_id) else { + return; + }; + + let history_len = conversation.bedrock_message_history().len(); + let split_point = history_len.saturating_sub(100); + if split_point == 0 { + return; + } + + let msgs: Vec = + conversation.bedrock_message_history()[..split_point].to_vec(); + let existing = conversation.progressive_summary().map(str::to_string); + + (msgs, existing, split_point) + }; + + history_model.update(ctx, |history_model, _| { + if let Some(conversation) = history_model.conversation_mut(&conversation_id) { + conversation.set_has_pending_progressive_summary(true); + } + }); + + log::info!( + "[progressive-summary] Triggering for conversation {:?}: summarizing {} messages, keeping last 100", + conversation_id, + messages_count + ); + + // Build the summarization input + let mut summarize_content = String::new(); + if let Some(ref prior) = existing_summary { + summarize_content.push_str("\n"); + summarize_content.push_str(prior); + summarize_content.push_str("\n\n\n"); + } + summarize_content.push_str("\n"); + fn safe_truncate(s: &str, max_chars: usize) -> String { + if s.len() <= max_chars { + s.to_string() + } else { + let trunc = s.chars().take(max_chars).collect::(); + format!("{trunc}... [truncated, {len} total chars]", len = s.len()) + } + } + + for msg in &messages_to_summarize { + let role_str = match msg.role { + MessageRole::User => "User", + MessageRole::Assistant => "Assistant", + }; + let content_str = match &msg.content { + MessageContent::Text(t) => t.clone(), + MessageContent::ToolUse { name, input, .. } => { + format!("[Tool Call: {}] {}", name, input) + } + MessageContent::ToolResult { content, .. } => safe_truncate(content, 2000), + MessageContent::MultiPart(parts) => { + use crate::ai::bedrock::convert::ContentPart; + parts + .iter() + .map(|p| match p { + ContentPart::Text(t) => t.clone(), + ContentPart::ToolUse { name, input, .. } => { + format!("[Tool: {}] {}", name, input) + } + ContentPart::ToolResult { content, .. } => safe_truncate(content, 2000), + }) + .collect::>() + .join("\n") + } + }; + summarize_content.push_str(&format!("[{}]: {}\n", role_str, content_str)); + } + summarize_content.push_str(""); + + let summarize_prompt = "Summarize the following conversation history. Preserve:\n\ + - All decisions made and their rationale\n\ + - All file paths modified and what was changed\n\ + - All tool calls with their significant results (commands run, files read, errors encountered)\n\ + - Current task state and any pending work\n\ + - Technical details, code patterns, and architecture discussed\n\n\ + Be comprehensive. This summary will be the only record of these exchanges."; + + let summarize_messages = vec![ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text(format!("{}\n\n{}", summarize_prompt, summarize_content)), + }]; + + // Spawn the background Bedrock call + let model_id_clone = model_id.clone(); + ctx.spawn( + async move { + let client = BedrockClient::from_config(config).await?; + client + .converse_collect( + &model_id_clone, + summarize_messages, + None, + 16000, + cross_region, + ) + .await + }, + move |me, result, ctx| { + let history_model = BlocklistAIHistoryModel::handle(ctx); + match result { + Ok((summary_text, input_tokens, output_tokens)) => { + log::info!( + "[progressive-summary] Completed for {:?}: {} chars, input={} output={} tokens", + conversation_id, + summary_text.len(), + input_tokens, + output_tokens, + ); + + let cost_cents = estimate_cost_cents( + input_tokens, + output_tokens, + 0, + 0, + &model_id, + ); + + // Use the conversation's active model for context window sizing, + // not the summarizer model. + let active_model_id = crate::ai::llms::LLMPreferences::as_ref(ctx) + .get_active_base_model(ctx, Some(me.terminal_view_id)) + .id + .to_string(); + + history_model.update(ctx, |history_model, _| { + if let Some(conversation) = + history_model.conversation_mut(&conversation_id) + { + // Drain the summarized messages from history + let drain_count = + messages_count.min(conversation.bedrock_message_history().len()); + let drained: Vec<_> = conversation + .bedrock_message_history() + .iter() + .take(drain_count) + .cloned() + .collect(); + conversation.archive_tool_results(drained); + conversation + .bedrock_message_history_mut() + .drain(0..drain_count); + + conversation + .set_progressive_summary(Some(summary_text.clone()), drain_count); + conversation.set_has_pending_progressive_summary(false); + + // Estimate new context window usage + let summary_tokens = (summary_text.len() / 4) as u32; + let remaining_msgs_tokens: u32 = conversation + .bedrock_message_history() + .iter() + .map(|m| match &m.content { + MessageContent::Text(t) => (t.len() / 4) as u32, + MessageContent::ToolUse { input, .. } => { + (input.to_string().len() / 4) as u32 + 20 + } + MessageContent::ToolResult { content, .. } => { + (content.len() / 4) as u32 + } + MessageContent::MultiPart(parts) => { + use crate::ai::bedrock::convert::ContentPart; + parts + .iter() + .map(|p| match p { + ContentPart::Text(t) => (t.len() / 4) as u32, + ContentPart::ToolUse { input, .. } => { + (input.to_string().len() / 4) as u32 + } + ContentPart::ToolResult { content, .. } => { + (content.len() / 4) as u32 + } + }) + .sum() + } + }) + .sum(); + + let max_ctx = context_window_for_model(&active_model_id); + let new_usage = + (summary_tokens + remaining_msgs_tokens) as f32 / max_ctx as f32; + conversation.set_context_window_usage(new_usage); + conversation + .set_current_context_tokens(summary_tokens + remaining_msgs_tokens); + + log::info!( + "[progressive-summary] Post-summary: ~{} tokens ({:.1}% of {} context), {} messages retained", + summary_tokens + remaining_msgs_tokens, + new_usage * 100.0, + active_model_id, + conversation.bedrock_message_history().len() + ); + } + }); + + // Update cost tracking + history_model.update(ctx, |history_model, _| { + use warp_multi_agent_api::response_event::stream_finished; + let token_usage = vec![stream_finished::TokenUsage { + model_id: "bedrock".to_string(), + total_input: input_tokens, + output: output_tokens, + input_cache_read: 0, + input_cache_write: 0, + cost_in_cents: cost_cents, + }]; + history_model.update_conversation_cost_and_usage_for_request( + conversation_id, + None, + token_usage, + None, + false, + ); + }); + } + Err(e) => { + log::error!( + "[progressive-summary] Failed for {:?}: {:?}", + conversation_id, + e + ); + history_model.update(ctx, |history_model, _| { + if let Some(conversation) = + history_model.conversation_mut(&conversation_id) + { + conversation.set_has_pending_progressive_summary(false); + } + }); + } + } + let _ = me; + }, + ); + } } impl Entity for BlocklistAIController { @@ -3028,7 +3412,9 @@ fn is_bedrock_credentials_error(msg: &str) -> bool { || (lower.contains("sso/cache") && lower.contains("notfound")) || (lower.contains("sso/cache") && lower.contains("no such file")) || (lower.contains("accessdenied") - && (lower.contains("token") || lower.contains("credential") || lower.contains("security"))) + && (lower.contains("token") + || lower.contains("credential") + || lower.contains("security"))) } #[allow(clippy::too_many_arguments)] diff --git a/app/src/ai/blocklist/controller/response_stream.rs b/app/src/ai/blocklist/controller/response_stream.rs index a163ffb2..d5702d97 100644 --- a/app/src/ai/blocklist/controller/response_stream.rs +++ b/app/src/ai/blocklist/controller/response_stream.rs @@ -92,14 +92,17 @@ impl ResponseStream { return None; } let auth_method = *settings.bedrock_auth_method.value(); - Some(BedrockClientConfig { - auth_method, - profile: settings.bedrock_profile.value().clone(), - region: settings.bedrock_region.value().clone(), - access_key_id: settings.bedrock_access_key_id.value().clone(), - secret_access_key: settings.bedrock_secret_access_key.value().clone(), - cross_region_inference: *settings.bedrock_cross_region_inference.value(), - }.with_external_fallbacks()) + Some( + BedrockClientConfig { + auth_method, + profile: settings.bedrock_profile.value().clone(), + region: settings.bedrock_region.value().clone(), + access_key_id: settings.bedrock_access_key_id.value().clone(), + secret_access_key: settings.bedrock_secret_access_key.value().clone(), + cross_region_inference: *settings.bedrock_cross_region_inference.value(), + } + .with_external_fallbacks(), + ) } pub fn new( @@ -190,14 +193,15 @@ impl ResponseStream { self.current_request_id = Some(request_id); let params = self.params.clone(); let bedrock_config = Self::bedrock_config_if_applicable(params.model.as_str(), ctx); - let _ = ctx.spawn( - async move { - generate_multi_agent_output(bedrock_config, params, cancellation_rx).await - }, - move |me, stream, ctx| { - me.handle_response_stream_result(request_id, stream, ctx); - }, - ); + let _ = + ctx.spawn( + async move { + generate_multi_agent_output(bedrock_config, params, cancellation_rx).await + }, + move |me, stream, ctx| { + me.handle_response_stream_result(request_id, stream, ctx); + }, + ); } /// Cancels the stream. The conversation_id is preserved in the emitted event for async handling. diff --git a/app/src/ai/blocklist/controller/slash_command.rs b/app/src/ai/blocklist/controller/slash_command.rs index 2f654039..d99ead4b 100644 --- a/app/src/ai/blocklist/controller/slash_command.rs +++ b/app/src/ai/blocklist/controller/slash_command.rs @@ -11,7 +11,6 @@ use crate::{ }, blocklist::agent_view::AgentViewEntryOrigin, }, - search::slash_command_menu::static_commands::commands, terminal::input::slash_commands::SlashCommandTrigger, BlocklistAIHistoryModel, }; @@ -33,9 +32,6 @@ pub enum SlashCommandRequest { repos: Vec, use_current_dir: bool, }, - Summarize { - prompt: Option, - }, FetchReviewComments { repo_path: String, }, @@ -55,13 +51,6 @@ impl SlashCommandRequest { return Some(Self::InitProjectRules); } - // Check if query starts with /compact and route to summarize conversation - if let Some(prompt) = query.strip_prefix(commands::COMPACT.name) { - return Some(Self::Summarize { - prompt: prompt.strip_prefix(' ').map(String::from), - }); - } - None } @@ -85,7 +74,6 @@ impl SlashCommandRequest { ctx, ); let entrypoint = self.entrypoint(); - let is_summarize = matches!(self, Self::Summarize { .. }); let inputs = self.input(context, controller.context_model.as_ref(ctx), ctx); if inputs.is_empty() { return; @@ -155,14 +143,12 @@ impl SlashCommandRequest { }); } // Emit SentRequest event to trigger buffer clearing - if is_summarize { - ctx.emit(BlocklistAIControllerEvent::SentRequest { - contains_user_query: true, - is_queued_prompt, - model_id, - stream_id, - }); - } + ctx.emit(BlocklistAIControllerEvent::SentRequest { + contains_user_query: true, + is_queued_prompt, + model_id, + stream_id, + }); } Err(e) => log::error!("Failed to send agent slash command request: {e:?}"), } @@ -174,8 +160,7 @@ impl SlashCommandRequest { app: &AppContext, ) -> Option { match self { - Self::Summarize { .. } - | Self::CreateEnvironment { .. } + Self::CreateEnvironment { .. } | Self::InvokeSkill { .. } | Self::FetchReviewComments { .. } => controller .context_model @@ -226,9 +211,6 @@ impl SlashCommandRequest { repo_paths: repos, }] } - SlashCommandRequest::Summarize { prompt, .. } => { - vec![AIAgentInput::SummarizeConversation { prompt }] - } SlashCommandRequest::FetchReviewComments { repo_path } => { vec![AIAgentInput::FetchReviewComments { repo_path, context }] } @@ -263,7 +245,6 @@ impl SlashCommandRequest { SlashCommandRequest::InitProjectRules => EntrypointType::InitProjectRules, SlashCommandRequest::CreateNewProject { .. } | SlashCommandRequest::CreateEnvironment { .. } - | SlashCommandRequest::Summarize { .. } | SlashCommandRequest::FetchReviewComments { .. } | SlashCommandRequest::InvokeSkill { .. } => EntrypointType::UserInitiated, } diff --git a/app/src/ai/blocklist/history_model.rs b/app/src/ai/blocklist/history_model.rs index 7063ecfa..481119eb 100644 --- a/app/src/ai/blocklist/history_model.rs +++ b/app/src/ai/blocklist/history_model.rs @@ -1095,6 +1095,8 @@ impl BlocklistAIHistoryModel { // The event cursor belongs to the source conversation's run; the // forked conversation will establish its own cursor. last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }; let forked_conversation_id = AIConversationId::new(); if let Err(e) = sqlite_sender.send(ModelEvent::UpdateMultiAgentConversation { @@ -1250,6 +1252,8 @@ impl BlocklistAIHistoryModel { // The event cursor belongs to the source conversation's run; the // forked conversation will establish its own cursor. last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }; let forked_conversation_id = AIConversationId::new(); diff --git a/app/src/ai/blocklist/history_model_test.rs b/app/src/ai/blocklist/history_model_test.rs index 003a4015..27ca3b45 100644 --- a/app/src/ai/blocklist/history_model_test.rs +++ b/app/src/ai/blocklist/history_model_test.rs @@ -328,6 +328,10 @@ fn create_server_metadata( credits_spent_for_last_block: None, token_usage: vec![], tool_usage_metadata: Default::default(), + total_cache_read_tokens: 0, + total_cache_write_tokens: 0, + total_cache_miss_tokens: 0, + total_cost_cents: 0.0, }; ServerAIConversationMetadata { @@ -1133,6 +1137,8 @@ fn test_find_by_token_after_insert_forked_conversation_from_tasks() { run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }; let tasks = vec![warp_multi_agent_api::Task { id: "root-task".to_string(), diff --git a/app/src/ai/blocklist/inline_action/mod.rs b/app/src/ai/blocklist/inline_action/mod.rs index b4311a18..9e31f130 100644 --- a/app/src/ai/blocklist/inline_action/mod.rs +++ b/app/src/ai/blocklist/inline_action/mod.rs @@ -11,7 +11,7 @@ pub(crate) mod requested_command_attribution; pub(crate) mod requested_script; pub(super) mod search_codebase; pub(crate) mod search_results_common; -pub(super) mod summarization; pub(crate) mod suggested_unit_tests; +pub(super) mod summarization; pub(super) mod web_fetch; pub(super) mod web_search; diff --git a/app/src/ai/blocklist/inline_action/summarization.rs b/app/src/ai/blocklist/inline_action/summarization.rs index 84536640..d7fca5ba 100644 --- a/app/src/ai/blocklist/inline_action/summarization.rs +++ b/app/src/ai/blocklist/inline_action/summarization.rs @@ -1,8 +1,10 @@ use galaxy_core::ui::appearance::Appearance; -use galaxyui::elements::shimmering_text::{ShimmerConfig, ShimmeringTextElement, ShimmeringTextStateHandle}; +use galaxyui::elements::shimmering_text::{ + ShimmerConfig, ShimmeringTextElement, ShimmeringTextStateHandle, +}; use galaxyui::elements::{ - ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Element, Flex, - MainAxisAlignment, ParentElement, Radius, Shrinkable, Text, + ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Element, Flex, MainAxisAlignment, + ParentElement, Radius, Shrinkable, Text, }; use galaxyui::r#async::{SpawnedFutureHandle, Timer}; use galaxyui::{AppContext, Entity, SingletonEntity, View, ViewContext}; @@ -73,20 +75,14 @@ impl SummarizationView { .with_cross_axis_alignment(CrossAxisAlignment::Center); // Clock loader icon (magenta, matches InProgress convention) - let icon_element = galaxyui::elements::Icon::new( - Icon::ClockLoader.into(), - theme.ansi_fg_magenta(), - ) - .finish(); + let icon_element = + galaxyui::elements::Icon::new(Icon::ClockLoader.into(), theme.ansi_fg_magenta()) + .finish(); let icon_box = ConstrainedBox::new(icon_element) .with_width(icon_size(app)) .with_height(icon_size(app)) .finish(); - header_row.add_child( - Container::new(icon_box) - .with_margin_right(8.) - .finish(), - ); + header_row.add_child(Container::new(icon_box).with_margin_right(8.).finish()); // Shimmering "Summarizing conversation..." text let base_color = theme.disabled_text_color(header_background).into_solid(); @@ -133,20 +129,13 @@ impl SummarizationView { .with_cross_axis_alignment(CrossAxisAlignment::Center); // Checkmark-style icon for completed - let icon_element = galaxyui::elements::Icon::new( - Icon::Check.into(), - theme.ansi_fg_green(), - ) - .finish(); + let icon_element = + galaxyui::elements::Icon::new(Icon::Check.into(), theme.ansi_fg_green()).finish(); let icon_box = ConstrainedBox::new(icon_element) .with_width(icon_size(app)) .with_height(icon_size(app)) .finish(); - header_row.add_child( - Container::new(icon_box) - .with_margin_right(8.) - .finish(), - ); + header_row.add_child(Container::new(icon_box).with_margin_right(8.).finish()); let elapsed = self.start_time.elapsed(); let elapsed_text = format_elapsed(elapsed); diff --git a/app/src/ai/blocklist/orchestration_event_streamer_tests.rs b/app/src/ai/blocklist/orchestration_event_streamer_tests.rs index 20d406c8..f8a28a1b 100644 --- a/app/src/ai/blocklist/orchestration_event_streamer_tests.rs +++ b/app/src/ai/blocklist/orchestration_event_streamer_tests.rs @@ -145,6 +145,8 @@ fn ai_conversation_new_restored_preserves_last_event_sequence() { run_id: None, autoexecute_override: None, last_event_sequence: Some(42), + progressive_summary: None, + messages_summarized_up_to: 0, }; let conversation = AIConversation::new_restored(AIConversationId::new(), vec![task], Some(data)) diff --git a/app/src/ai/blocklist/orchestration_events.rs b/app/src/ai/blocklist/orchestration_events.rs index 831be054..83539bc1 100644 --- a/app/src/ai/blocklist/orchestration_events.rs +++ b/app/src/ai/blocklist/orchestration_events.rs @@ -578,9 +578,15 @@ impl OrchestrationEventService { if let Some(parent_id) = parent_id { // Merge child's token usage and costs into parent BlocklistAIHistoryModel::handle(ctx).update(ctx, |history_model, _ctx| { - let child_usage: Option<(HashMap, crate::ai::agent::RequestCost)> = history_model - .conversation(&conversation_id) - .map(|c| (c.total_token_usage_by_model().clone(), c.total_request_cost())); + let child_usage: Option<( + HashMap, + crate::ai::agent::RequestCost, + )> = history_model.conversation(&conversation_id).map(|c| { + ( + c.total_token_usage_by_model().clone(), + c.total_request_cost(), + ) + }); if let Some((token_usage, request_cost)) = child_usage { if let Some(parent) = history_model.conversation_mut(&parent_id) { parent.merge_child_usage_raw(&token_usage, request_cost); @@ -589,11 +595,7 @@ impl OrchestrationEventService { }); if summary.is_some() { - self.route_subagent_completion_summary( - conversation_id, - parent_id, - ctx, - ); + self.route_subagent_completion_summary(conversation_id, parent_id, ctx); } } } @@ -1271,9 +1273,7 @@ impl OrchestrationEventService { event_id: Uuid::new_v4().to_string(), source_agent_id: "parent".to_string(), attempt_count: 0, - detail: PendingEventDetail::SubagentAnswer { - answer_text, - }, + detail: PendingEventDetail::SubagentAnswer { answer_text }, }; self.pending_events diff --git a/app/src/ai/blocklist/passive_suggestions/maa.rs b/app/src/ai/blocklist/passive_suggestions/maa.rs index 3a2b31ea..ede7ac2d 100644 --- a/app/src/ai/blocklist/passive_suggestions/maa.rs +++ b/app/src/ai/blocklist/passive_suggestions/maa.rs @@ -317,11 +317,7 @@ impl PassiveSuggestionsModel { .lock() .block_list() .block_at(block_completed.index) - .and_then(|block| { - block - .agent_view_visibility() - .agent_view_conversation_id() - }); + .and_then(|block| block.agent_view_visibility().agent_view_conversation_id()); let prompt = format!( "The command `{}` failed. Diagnose the error and suggest a fix.", @@ -436,6 +432,7 @@ impl PassiveSuggestionsModel { ); } } + } impl Entity for PassiveSuggestionsModel { diff --git a/app/src/ai/blocklist/usage/context_window_view.rs b/app/src/ai/blocklist/usage/context_window_view.rs index 459492f6..271093c3 100644 --- a/app/src/ai/blocklist/usage/context_window_view.rs +++ b/app/src/ai/blocklist/usage/context_window_view.rs @@ -2,9 +2,7 @@ use crate::ai::bedrock::convert::{ContentPart, ConversationMessage, MessageConte use crate::appearance::Appearance; use crate::ui_components::blended_colors; use galaxyui::{ - elements::{ - Container, CornerRadius, CrossAxisAlignment, Flex, ParentElement, Radius, Text, - }, + elements::{Container, CornerRadius, CrossAxisAlignment, Flex, ParentElement, Radius, Text}, AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, }; @@ -60,13 +58,9 @@ impl View for ContextWindowView { format_tokens(estimated_tokens as u32), ); column = column.with_child( - Text::new( - header_text, - appearance.ui_font_family(), - font_size + 1.0, - ) - .with_color(label_color) - .finish(), + Text::new(header_text, appearance.ui_font_family(), font_size + 1.0) + .with_color(label_color) + .finish(), ); // Messages — show full content, no truncation. @@ -89,7 +83,11 @@ impl View for ContextWindowView { // Full content let content_text = match &msg.content { MessageContent::Text(t) => t.clone(), - MessageContent::ToolUse { name, tool_use_id, input } => { + MessageContent::ToolUse { + name, + tool_use_id, + input, + } => { format!( "[ToolUse] name={}, id={}\ninput={}", name, tool_use_id, input @@ -112,13 +110,21 @@ impl View for ContextWindowView { ContentPart::Text(t) => { out.push_str(&format!("[Part {} Text] {}\n", pi, t)); } - ContentPart::ToolUse { name, tool_use_id, input } => { + ContentPart::ToolUse { + name, + tool_use_id, + input, + } => { out.push_str(&format!( "[Part {} ToolUse] name={}, id={}, input={}\n", pi, name, tool_use_id, input )); } - ContentPart::ToolResult { tool_use_id, content, is_error } => { + ContentPart::ToolResult { + tool_use_id, + content, + is_error, + } => { out.push_str(&format!( "[Part {} ToolResult] id={}, error={}\n{}\n", pi, tool_use_id, is_error, content diff --git a/app/src/ai/blocklist/usage/conversation_usage_view.rs b/app/src/ai/blocklist/usage/conversation_usage_view.rs index 6f5be533..3d83b95e 100644 --- a/app/src/ai/blocklist/usage/conversation_usage_view.rs +++ b/app/src/ai/blocklist/usage/conversation_usage_view.rs @@ -261,8 +261,8 @@ impl ConversationUsageView { } // Cache usage (cumulative session totals) - let total_cache = self.usage_info.total_cache_read_tokens - + self.usage_info.total_cache_write_tokens; + let total_cache = + self.usage_info.total_cache_read_tokens + self.usage_info.total_cache_write_tokens; if total_cache > 0 { if self.usage_info.total_cache_read_tokens > 0 { labels.push(render_label_text("Cache read", appearance)); @@ -290,10 +290,7 @@ impl ConversationUsageView { / total_cache_ops as f32) * 100.0; labels.push(render_label_text("Cache hit rate", appearance)); - values.push(render_value_text( - format!("{:.1}%", hit_rate), - appearance, - )); + values.push(render_value_text(format!("{:.1}%", hit_rate), appearance)); } } diff --git a/app/src/ai/conversation_details_panel_tests.rs b/app/src/ai/conversation_details_panel_tests.rs index 83e4ec66..3a92684a 100644 --- a/app/src/ai/conversation_details_panel_tests.rs +++ b/app/src/ai/conversation_details_panel_tests.rs @@ -133,6 +133,8 @@ fn test_from_task_includes_linked_directory_when_run_id_matches() { run_id: Some(task_id.to_string()), autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); @@ -248,6 +250,8 @@ fn test_from_task_includes_linked_directory_when_server_token_matches() { run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }, ); diff --git a/app/src/ai/facts/predefined_rules.rs b/app/src/ai/facts/predefined_rules.rs index 36ddcaf9..db1a9a9f 100644 --- a/app/src/ai/facts/predefined_rules.rs +++ b/app/src/ai/facts/predefined_rules.rs @@ -6,8 +6,7 @@ pub struct PredefinedRule { pub const SYSTEM_DEFINED_RULE_PREFIX: &str = "System Defined Rule"; pub fn is_predefined_rule(name: &str) -> bool { - PREDEFINED_RULES.iter().any(|r| r.name == name) - || name.starts_with(SYSTEM_DEFINED_RULE_PREFIX) + PREDEFINED_RULES.iter().any(|r| r.name == name) || name.starts_with(SYSTEM_DEFINED_RULE_PREFIX) } pub fn predefined_rule_index(name: &str) -> Option { diff --git a/app/src/ai/facts/view/rule.rs b/app/src/ai/facts/view/rule.rs index 16f4e556..4b64d22b 100644 --- a/app/src/ai/facts/view/rule.rs +++ b/app/src/ai/facts/view/rule.rs @@ -298,12 +298,7 @@ impl RuleView { content: rule.content.to_string(), suggested_logging_id: None, }); - update_manager.create_ai_fact( - ai_fact, - ClientId::default(), - owner, - ctx, - ); + update_manager.create_ai_fact(ai_fact, ClientId::default(), owner, ctx); } }); } @@ -517,22 +512,10 @@ impl RuleView { suggested_logging_id: None, }); - if let Some((sync_id, revision)) = - existing_system_rules.get(rule.name) - { - update_manager.update_ai_fact( - ai_fact, - *sync_id, - revision.clone(), - ctx, - ); + if let Some((sync_id, revision)) = existing_system_rules.get(rule.name) { + update_manager.update_ai_fact(ai_fact, *sync_id, revision.clone(), ctx); } else { - update_manager.create_ai_fact( - ai_fact, - ClientId::default(), - owner, - ctx, - ); + update_manager.create_ai_fact(ai_fact, ClientId::default(), owner, ctx); } } }); @@ -913,34 +896,30 @@ impl RuleView { if is_delete_allowed(ai_row.fact.clone(), app) { let delete_sync_id = ai_row.fact.sync_id(); - let delete_button = Hoverable::new( - ai_row.mouse_states.delete_hover.clone(), - |state| { - let mut container = Container::new( - ConstrainedBox::new( - Icon::Trash - .to_galaxyui_icon( - appearance - .theme() - .sub_text_color(appearance.theme().background()), - ) - .finish(), - ) - .with_width(16.) - .with_height(16.) - .finish(), + let delete_button = Hoverable::new(ai_row.mouse_states.delete_hover.clone(), |state| { + let mut container = Container::new( + ConstrainedBox::new( + Icon::Trash + .to_galaxyui_icon( + appearance + .theme() + .sub_text_color(appearance.theme().background()), + ) + .finish(), ) - .with_uniform_padding(4.) - .with_corner_radius(CornerRadius::with_all( - galaxyui::elements::Radius::Pixels(4.), - )); - if state.is_hovered() { - container = - container.with_background(appearance.theme().surface_2()); - } - container.finish() - }, - ) + .with_width(16.) + .with_height(16.) + .finish(), + ) + .with_uniform_padding(4.) + .with_corner_radius(CornerRadius::with_all( + galaxyui::elements::Radius::Pixels(4.), + )); + if state.is_hovered() { + container = container.with_background(appearance.theme().surface_2()); + } + container.finish() + }) .with_cursor(Cursor::PointingHand) .on_click(move |ctx, _, _| { ctx.dispatch_typed_action(RuleViewAction::Delete(delete_sync_id)); diff --git a/app/src/ai/llms.rs b/app/src/ai/llms.rs index ab16763b..dc92ba99 100644 --- a/app/src/ai/llms.rs +++ b/app/src/ai/llms.rs @@ -424,11 +424,11 @@ impl Default for ModelsByFeature { fn default() -> Self { Self { agent_mode: AvailableLLMs { - default_id: "anthropic.claude-opus-4-6".to_owned().into(), + default_id: "anthropic.claude-opus-4-6[1m]".to_owned().into(), choices: vec![LLMInfo { display_name: "Claude Opus 4.6".to_owned(), base_model_name: "Claude Opus 4.6".to_owned(), - id: "anthropic.claude-opus-4-6".to_owned().into(), + id: "anthropic.claude-opus-4-6[1m]".to_owned().into(), reasoning_level: None, usage_metadata: LLMUsageMetadata { request_multiplier: 1, @@ -445,11 +445,11 @@ impl Default for ModelsByFeature { preferred_codex_model_id: None, }, coding: AvailableLLMs { - default_id: "anthropic.claude-sonnet-4-6".to_owned().into(), + default_id: "anthropic.claude-sonnet-4-6[1m]".to_owned().into(), choices: vec![LLMInfo { display_name: "Claude Sonnet 4.6".to_owned(), base_model_name: "Claude Sonnet 4.6".to_owned(), - id: "anthropic.claude-sonnet-4-6".to_owned().into(), + id: "anthropic.claude-sonnet-4-6[1m]".to_owned().into(), reasoning_level: None, usage_metadata: LLMUsageMetadata { request_multiplier: 1, @@ -508,8 +508,6 @@ pub struct LLMPreferences { models_by_feature: ModelsByFeature, last_update: Option, base_llm_for_terminal_view: HashMap, - #[cfg(not(target_family = "wasm"))] - bedrock_models_fetched: bool, } impl LLMPreferences { @@ -550,12 +548,6 @@ impl LLMPreferences { | AISettingsChangedEvent::BedrockCrossRegionInference { .. } | AISettingsChangedEvent::BedrockRegion { .. } ) { - if matches!(event, AISettingsChangedEvent::BedrockEnabled { .. }) { - let enabled = *AISettings::as_ref(ctx).bedrock_enabled.value(); - if enabled && !me.bedrock_models_fetched { - me.trigger_bedrock_discovery(ctx); - } - } me.inject_bedrock_models(ctx); ctx.emit(LLMPreferencesEvent::UpdatedAvailableLLMs); } @@ -567,8 +559,6 @@ impl LLMPreferences { models_by_feature, last_update: None, base_llm_for_terminal_view, - #[cfg(not(target_family = "wasm"))] - bedrock_models_fetched: false, }; // In agent mode eval builds, eagerly kick off a fetch of the model list from the server @@ -580,47 +570,46 @@ impl LLMPreferences { #[cfg(not(target_family = "wasm"))] { + Self::ensure_default_models_in_settings(ctx); me.inject_bedrock_models(ctx); - if *AISettings::as_ref(ctx).bedrock_enabled.value() { - me.trigger_bedrock_discovery(ctx); - } } me } #[cfg(not(target_family = "wasm"))] - fn trigger_bedrock_discovery(&mut self, ctx: &mut ModelContext) { - use crate::ai::bedrock::client::BedrockClientConfig; - use crate::ai::bedrock::discovery::discover_inference_profiles; - - self.bedrock_models_fetched = true; + fn ensure_default_models_in_settings(ctx: &mut ModelContext) { + use crate::ai::bedrock::models::DEFAULT_BEDROCK_MODELS; let settings = AISettings::as_ref(ctx); - let config = BedrockClientConfig { - auth_method: settings.bedrock_auth_method.value().clone(), - profile: settings.bedrock_profile.value().clone(), - region: settings.bedrock_region.value().clone(), - access_key_id: settings.bedrock_access_key_id.value().clone(), - secret_access_key: settings.bedrock_secret_access_key.value().clone(), - cross_region_inference: *settings.bedrock_cross_region_inference.value(), - }.with_external_fallbacks(); + let mut current_models: Vec = + settings.bedrock_models.value().clone(); - ctx.spawn( - async move { discover_inference_profiles(&config).await }, - |me, result, ctx| match result { - Ok(models) => { - AISettings::handle(ctx).update(ctx, |settings, ctx| { - let _ = settings.bedrock_models.set_value(models, ctx); - }); - me.inject_bedrock_models(ctx); - ctx.emit(LLMPreferencesEvent::UpdatedAvailableLLMs); - } - Err(e) => { - log::error!("Failed to discover Bedrock inference profiles: {e}"); - } - }, - ); + let existing_ids: std::collections::HashSet = + current_models.iter().map(|m| m.model_id.clone()).collect(); + + let mut added = false; + for default in DEFAULT_BEDROCK_MODELS { + if !existing_ids.contains(default.model_id as &str) { + current_models.push(BedrockModelConfig { + model_id: default.model_id.to_string(), + display_name: default.display_name.to_string(), + vision_supported: default.vision_supported, + context_size: default.context_size, + }); + added = true; + } + } + + if added { + log::info!( + "[bedrock] Added missing default models to settings — now {} total", + current_models.len() + ); + AISettings::handle(ctx).update(ctx, |settings, ctx| { + let _ = settings.bedrock_models.set_value(current_models, ctx); + }); + } } #[cfg(not(target_family = "wasm"))] diff --git a/app/src/ai/mcp/file_based_manager.rs b/app/src/ai/mcp/file_based_manager.rs index ed167c17..5f790720 100644 --- a/app/src/ai/mcp/file_based_manager.rs +++ b/app/src/ai/mcp/file_based_manager.rs @@ -13,8 +13,8 @@ use crate::{ templatable_installation::TemplatableMCPServerInstallation, ParsedTemplatableMCPServerResult, }, - settings::{ai::AISettings, AISettingsChangedEvent}, galaxy_managed_paths_watcher::galaxy_data_dir, + settings::{ai::AISettings, AISettingsChangedEvent}, }; /// Singleton model to manage file-based MCP servers. diff --git a/app/src/ai/mcp/file_based_manager_tests.rs b/app/src/ai/mcp/file_based_manager_tests.rs index 039d2ee2..930910ba 100644 --- a/app/src/ai/mcp/file_based_manager_tests.rs +++ b/app/src/ai/mcp/file_based_manager_tests.rs @@ -2,8 +2,8 @@ use super::{FileBasedMCPManager, FileBasedMCPManagerEvent, MCPProvider}; use crate::ai::mcp::FileMCPWatcher; use crate::ai::mcp::ParsedTemplatableMCPServerResult; use crate::auth::AuthStateProvider; -use crate::settings::{AISettings, FocusedTerminalInfo}; use crate::galaxy_managed_paths_watcher::{galaxy_data_dir, GalaxyManagedPathsWatcher}; +use crate::settings::{AISettings, FocusedTerminalInfo}; use crate::workspaces::user_workspaces::UserWorkspaces; use galaxy_core::features::FeatureFlag; use galaxyui::{App, Entity, ModelHandle, SingletonEntity as _}; diff --git a/app/src/ai/mod.rs b/app/src/ai/mod.rs index f39b6be3..12fbcaf8 100644 --- a/app/src/ai/mod.rs +++ b/app/src/ai/mod.rs @@ -17,8 +17,6 @@ pub(crate) mod attachment_utils; pub mod aws_credentials; #[cfg(not(target_family = "wasm"))] pub mod bedrock; -#[allow(dead_code)] -pub mod prompt_builder; pub(crate) mod block_context; pub(crate) mod blocklist; pub mod control_code_parser; @@ -33,6 +31,8 @@ pub(crate) mod llms; pub mod onboarding; pub(crate) mod persisted_workspace; pub(crate) mod predict; +#[allow(dead_code)] +pub mod prompt_builder; pub mod request_usage_model; pub(crate) mod restored_conversations; pub(crate) mod skills; diff --git a/app/src/ai/prompt_builder/context.rs b/app/src/ai/prompt_builder/context.rs index 8c6042da..fb691efc 100644 --- a/app/src/ai/prompt_builder/context.rs +++ b/app/src/ai/prompt_builder/context.rs @@ -45,4 +45,4 @@ impl PromptContext { self.current_time = time.into(); self } -} \ No newline at end of file +} diff --git a/app/src/ai/prompt_builder/mod.rs b/app/src/ai/prompt_builder/mod.rs index d489092c..d4c1584c 100644 --- a/app/src/ai/prompt_builder/mod.rs +++ b/app/src/ai/prompt_builder/mod.rs @@ -187,4 +187,4 @@ impl PromptBuilder { } #[cfg(test)] -mod tests; \ No newline at end of file +mod tests; diff --git a/app/src/ai/prompt_builder/mode.rs b/app/src/ai/prompt_builder/mode.rs index 269106d5..832f2c52 100644 --- a/app/src/ai/prompt_builder/mode.rs +++ b/app/src/ai/prompt_builder/mode.rs @@ -63,4 +63,4 @@ impl std::fmt::Display for Mode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.label()) } -} \ No newline at end of file +} diff --git a/app/src/ai/prompt_builder/prompts/anthropic.rs b/app/src/ai/prompt_builder/prompts/anthropic.rs index f03a9dc4..31bd0a2f 100644 --- a/app/src/ai/prompt_builder/prompts/anthropic.rs +++ b/app/src/ai/prompt_builder/prompts/anthropic.rs @@ -157,4 +157,4 @@ You have access to tools for examining the code under review: - `search_codebase` — Semantic search for related implementations - `run_shell_command` — For read-only commands (git diff, git log, etc.) -Use these tools to gather context needed for a thorough review. You should read the files being changed and their surrounding context before providing feedback."#; \ No newline at end of file +Use these tools to gather context needed for a thorough review. You should read the files being changed and their surrounding context before providing feedback."#; diff --git a/app/src/ai/prompt_builder/prompts/mod.rs b/app/src/ai/prompt_builder/prompts/mod.rs index 474976dd..d905b39a 100644 --- a/app/src/ai/prompt_builder/prompts/mod.rs +++ b/app/src/ai/prompt_builder/prompts/mod.rs @@ -37,4 +37,4 @@ pub fn tool_usage_guidelines(mode: &Mode, provider: &Provider) -> String { match provider { Provider::Anthropic => anthropic::tool_usage_guidelines(mode), } -} \ No newline at end of file +} diff --git a/app/src/ai/prompt_builder/prompts/provider.rs b/app/src/ai/prompt_builder/prompts/provider.rs index f0077873..a2c186a9 100644 --- a/app/src/ai/prompt_builder/prompts/provider.rs +++ b/app/src/ai/prompt_builder/prompts/provider.rs @@ -25,4 +25,4 @@ impl std::fmt::Display for Provider { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.label()) } -} \ No newline at end of file +} diff --git a/app/src/ai/prompt_builder/tests.rs b/app/src/ai/prompt_builder/tests.rs index 82cc9dda..e39e5e36 100644 --- a/app/src/ai/prompt_builder/tests.rs +++ b/app/src/ai/prompt_builder/tests.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod prompt_builder_tests { - use super::*; + use crate::ai::prompt_builder::*; #[test] fn test_code_mode_builds_successfully() { @@ -87,7 +87,10 @@ mod prompt_builder_tests { .with_mcp_tools(vec![mcp_tool.clone()]) .build(); - assert!(prompt.tools.iter().any(|t| t.name == "mcp__github__create_pr")); + assert!(prompt + .tools + .iter() + .any(|t| t.name == "mcp__github__create_pr")); } #[test] @@ -128,4 +131,4 @@ mod prompt_builder_tests { assert!(prompt.system_prompt.contains("/projects/myapp")); assert!(prompt.system_prompt.contains("feature/new-thing")); } -} \ No newline at end of file +} diff --git a/app/src/ai/prompt_builder/tools.rs b/app/src/ai/prompt_builder/tools.rs index 2bd1f7f4..2023fa19 100644 --- a/app/src/ai/prompt_builder/tools.rs +++ b/app/src/ai/prompt_builder/tools.rs @@ -218,7 +218,8 @@ fn read_mcp_resource() -> ToolDefinition { fn read_documents() -> ToolDefinition { ToolDefinition { name: "read_documents".to_string(), - description: "Read the contents of one or more Galaxy notebook documents by their IDs.".to_string(), + description: "Read the contents of one or more Galaxy notebook documents by their IDs." + .to_string(), input_schema: serde_json::json!({ "type": "object", "properties": { @@ -232,7 +233,8 @@ fn read_documents() -> ToolDefinition { fn create_documents() -> ToolDefinition { ToolDefinition { name: "create_documents".to_string(), - description: "Create new Galaxy notebook documents with the specified title and content.".to_string(), + description: "Create new Galaxy notebook documents with the specified title and content." + .to_string(), input_schema: serde_json::json!({ "type": "object", "properties": { @@ -246,7 +248,8 @@ fn create_documents() -> ToolDefinition { fn edit_documents() -> ToolDefinition { ToolDefinition { name: "edit_documents".to_string(), - description: "Edit existing Galaxy notebook documents using search/replace diffs.".to_string(), + description: "Edit existing Galaxy notebook documents using search/replace diffs." + .to_string(), input_schema: serde_json::json!({ "type": "object", "properties": { @@ -305,7 +308,9 @@ fn ask_user_question() -> ToolDefinition { fn read_skill() -> ToolDefinition { ToolDefinition { name: "read_skill".to_string(), - description: "Read a skill definition to understand available capabilities and how to use them.".to_string(), + description: + "Read a skill definition to understand available capabilities and how to use them." + .to_string(), input_schema: serde_json::json!({ "type": "object", "properties": { @@ -328,4 +333,4 @@ fn fetch_conversation() -> ToolDefinition { "required": ["conversation_id"] }), } -} \ No newline at end of file +} diff --git a/app/src/ai/skills/file_watchers/skill_watcher.rs b/app/src/ai/skills/file_watchers/skill_watcher.rs index 910aa624..b79a8c91 100644 --- a/app/src/ai/skills/file_watchers/skill_watcher.rs +++ b/app/src/ai/skills/file_watchers/skill_watcher.rs @@ -14,11 +14,11 @@ use super::{ }; use watcher::{BulkFilesystemWatcherEvent, HomeDirectoryWatcher, HomeDirectoryWatcherEvent}; -use crate::server::datetime_ext::DateTimeExt; use crate::galaxy_managed_paths_watcher::{ filter_repository_update_by_prefix, galaxy_managed_skill_dirs, GalaxyManagedPathsWatcher, GalaxyManagedPathsWatcherEvent, }; +use crate::server::datetime_ext::DateTimeExt; use ai::skills::{ home_skills_path, parse_skill, ParsedSkill, SkillProvider, SKILL_PROVIDER_DEFINITIONS, }; diff --git a/app/src/appearance.rs b/app/src/appearance.rs index feb8a545..ed4252c1 100644 --- a/app/src/appearance.rs +++ b/app/src/appearance.rs @@ -25,7 +25,7 @@ use crate::{ active_theme_kind, FontSettings, FontSettingsChangedEvent, MonospaceFontSize, Settings, ThemeSettings, }, - themes::theme::{ThemeKind, GalaxyTheme}, + themes::theme::{GalaxyTheme, ThemeKind}, ASSETS, }; @@ -246,7 +246,8 @@ impl AppearanceManager { return; }; - let ns_data: id = msg_send![class!(NSData), dataWithBytes:icon_data.as_ptr() length:icon_data.len()]; + let ns_data: id = + msg_send![class!(NSData), dataWithBytes:icon_data.as_ptr() length:icon_data.len()]; let image: id = msg_send![class!(NSImage), alloc]; let image: id = msg_send![image, initWithData:ns_data]; diff --git a/app/src/bin/oss.rs b/app/src/bin/oss.rs index e14b94c5..1d206898 100644 --- a/app/src/bin/oss.rs +++ b/app/src/bin/oss.rs @@ -29,6 +29,8 @@ fn main() -> Result<()> { state = state.with_additional_features(galaxy_core::features::DEBUG_FLAGS); } state = state.with_additional_features(&[ + FeatureFlag::AgentMode, + FeatureFlag::AgentView, FeatureFlag::LspCompletion, FeatureFlag::LspCodeActions, FeatureFlag::LspRename, diff --git a/app/src/code/code_actions.rs b/app/src/code/code_actions.rs index cd663dc4..e1081d30 100644 --- a/app/src/code/code_actions.rs +++ b/app/src/code/code_actions.rs @@ -23,7 +23,9 @@ const MAX_VISIBLE_ACTIONS: usize = 12; pub(super) enum CodeActionsState { Idle, - Requesting { abort_handle: AbortHandle }, + Requesting { + abort_handle: AbortHandle, + }, Available { actions: Vec, anchor_offset: CharOffset, @@ -118,10 +120,11 @@ impl LocalCodeEditorView { }) .collect(); - let future = match lsp_server - .as_ref(ctx) - .code_actions(file_path.to_path_buf(), lsp_range, diagnostics_at_cursor) - { + let future = match lsp_server.as_ref(ctx).code_actions( + file_path.to_path_buf(), + lsp_range, + diagnostics_at_cursor, + ) { Ok(future) => future, Err(e) => { log::warn!("Failed to call lsp.code_actions: {e}"); diff --git a/app/src/code/completion.rs b/app/src/code/completion.rs index 59ed6159..50f15817 100644 --- a/app/src/code/completion.rs +++ b/app/src/code/completion.rs @@ -6,12 +6,14 @@ use galaxy_core::ui::appearance::Appearance; use galaxy_core::ui::theme::color::internal_colors; use galaxyui::elements::{ Border, ChildAnchor, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox, Container, - CornerRadius, CrossAxisAlignment, Flex, FormattedTextElement, HighlightedHyperlink, - Hoverable, MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement, + CornerRadius, CrossAxisAlignment, Flex, FormattedTextElement, HighlightedHyperlink, Hoverable, + MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Radius, ScrollbarWidth, Shrinkable, Text, }; use galaxyui::{AppContext, Element, SingletonEntity, ViewContext}; -use lsp::{CompletionItem, CompletionItemData, CompletionKind, CompletionResult, CompletionTrigger}; +use lsp::{ + CompletionItem, CompletionItemData, CompletionKind, CompletionResult, CompletionTrigger, +}; use markdown_parser::FormattedText; use pathfinder_geometry::vector::Vector2F; use string_offset::CharOffset; @@ -38,7 +40,9 @@ pub(super) struct ResolvedDocumentation { pub(super) enum CompletionState { Idle, - Requesting { abort_handle: AbortHandle }, + Requesting { + abort_handle: AbortHandle, + }, Showing { items: Vec, filtered_indices: Vec, @@ -281,16 +285,17 @@ impl LocalCodeEditorView { .as_ref(ctx) .offset_to_lsp_position(trigger_offset, ctx); - let future = match lsp_server - .as_ref(ctx) - .completion(file_path.to_path_buf(), lsp_position, trigger) - { - Ok(future) => future, - Err(e) => { - log::warn!("Failed to call lsp.completion: {e}"); - return; - } - }; + let future = + match lsp_server + .as_ref(ctx) + .completion(file_path.to_path_buf(), lsp_position, trigger) + { + Ok(future) => future, + Err(e) => { + log::warn!("Failed to call lsp.completion: {e}"); + return; + } + }; self.completion_state.dismiss(); @@ -588,9 +593,7 @@ impl LocalCodeEditorView { let mut content_column = Flex::column(); - for (display_idx, &item_idx) in - filtered_indices.iter().enumerate().take(visible_count) - { + for (display_idx, &item_idx) in filtered_indices.iter().enumerate().take(visible_count) { let item = &items[item_idx]; let is_selected = display_idx == *selected_index; let mouse_state = item_mouse_states @@ -602,9 +605,9 @@ impl LocalCodeEditorView { let hoverable_item = Hoverable::new(mouse_state, move |_| item_element) .on_hover(move |is_hovered, ctx, _, _| { if is_hovered { - ctx.dispatch_typed_action( - LocalCodeEditorAction::CompletionHoverItem(display_idx), - ); + ctx.dispatch_typed_action(LocalCodeEditorAction::CompletionHoverItem( + display_idx, + )); } }) .on_click(move |ctx, _, _| { @@ -677,15 +680,13 @@ impl LocalCodeEditorView { let row = Flex::row() .with_cross_axis_alignment(CrossAxisAlignment::Start) .with_child(menu_box) - .with_child( - Container::new(docs_element) - .with_padding_left(2.) - .finish(), - ) + .with_child(Container::new(docs_element).with_padding_left(2.).finish()) .finish(); - Some(ConstrainedBox::new(row) - .with_max_height(COMPLETION_MENU_MAX_HEIGHT) - .finish()) + Some( + ConstrainedBox::new(row) + .with_max_height(COMPLETION_MENU_MAX_HEIGHT) + .finish(), + ) } else { Some(menu_box) } diff --git a/app/src/code/local_code_editor.rs b/app/src/code/local_code_editor.rs index a708cb09..52785b86 100644 --- a/app/src/code/local_code_editor.rs +++ b/app/src/code/local_code_editor.rs @@ -85,18 +85,18 @@ const DROP_SHADOW_COLOR: ColorU = ColorU { const HOVER_DEBOUNCE_PERIOD: Duration = Duration::from_millis(500); +use super::code_actions::{CodeActionsState, CODE_ACTIONS_DEBOUNCE_PERIOD}; +use super::completion::{CompletionState, COMPLETION_DEBOUNCE_PERIOD}; use super::diff_viewer::DiffViewer; use super::editor::{ scroll::{ScrollPosition, ScrollTrigger}, view::{CodeEditorEvent, CodeEditorView}, }; -use super::code_actions::{CodeActionsState, CODE_ACTIONS_DEBOUNCE_PERIOD}; -use super::completion::{CompletionState, COMPLETION_DEBOUNCE_PERIOD}; -use super::rename::RenameState; -use super::signature_help::SignatureHelpState; use super::find_references_view::{FindReferencesView, FindReferencesViewEvent}; use super::language_server_extension::ProcessedDiagnostic; use super::lsp_telemetry::LspTelemetryEvent; +use super::rename::RenameState; +use super::signature_help::SignatureHelpState; use super::ImmediateSaveError; use galaxy_core::send_telemetry_from_ctx; diff --git a/app/src/code/mod.rs b/app/src/code/mod.rs index 6a220b35..29692582 100644 --- a/app/src/code/mod.rs +++ b/app/src/code/mod.rs @@ -13,15 +13,15 @@ pub mod completion; #[cfg(not(target_family = "wasm"))] pub mod find_references_view; #[cfg(not(target_family = "wasm"))] -pub mod rename; -#[cfg(not(target_family = "wasm"))] -pub mod signature_help; -#[cfg(not(target_family = "wasm"))] pub mod language_server_extension; #[cfg_attr(not(target_family = "wasm"), path = "local_code_editor.rs")] #[cfg_attr(target_family = "wasm", path = "local_code_editor_wasm.rs")] pub mod local_code_editor; #[cfg(not(target_family = "wasm"))] +pub mod rename; +#[cfg(not(target_family = "wasm"))] +pub mod signature_help; +#[cfg(not(target_family = "wasm"))] pub use local_code_editor::ShowFindReferencesCard; pub mod diff_viewer; pub mod editor; diff --git a/app/src/code/rename.rs b/app/src/code/rename.rs index 8c7d093e..0e4ce794 100644 --- a/app/src/code/rename.rs +++ b/app/src/code/rename.rs @@ -12,12 +12,16 @@ use super::local_code_editor::LocalCodeEditorView; pub(super) enum RenameState { Idle, - Preparing { abort_handle: AbortHandle }, + Preparing { + abort_handle: AbortHandle, + }, InputActive { editor: ViewHandle, anchor_offset: CharOffset, }, - Applying { abort_handle: AbortHandle }, + Applying { + abort_handle: AbortHandle, + }, } impl Default for RenameState { @@ -141,17 +145,11 @@ impl LocalCodeEditorView { ctx.notify(); } - fn handle_rename_editor_event( - &mut self, - event: &EditorEvent, - ctx: &mut ViewContext, - ) { + fn handle_rename_editor_event(&mut self, event: &EditorEvent, ctx: &mut ViewContext) { match event { EditorEvent::Enter => { let new_name = match &self.rename_state { - RenameState::InputActive { editor, .. } => { - editor.as_ref(ctx).buffer_text(ctx) - } + RenameState::InputActive { editor, .. } => editor.as_ref(ctx).buffer_text(ctx), _ => return, }; self.confirm_rename(new_name, ctx); @@ -191,19 +189,20 @@ impl LocalCodeEditorView { .as_ref(ctx) .offset_to_lsp_position(anchor_offset, ctx); - let future = match lsp_server - .as_ref(ctx) - .rename(file_path.to_path_buf(), lsp_position, new_name) - { - Ok(future) => future, - Err(e) => { - log::warn!("Failed to call lsp.rename: {e}"); - self.rename_state = RenameState::Idle; - ctx.focus(self.editor()); - ctx.notify(); - return; - } - }; + let future = + match lsp_server + .as_ref(ctx) + .rename(file_path.to_path_buf(), lsp_position, new_name) + { + Ok(future) => future, + Err(e) => { + log::warn!("Failed to call lsp.rename: {e}"); + self.rename_state = RenameState::Idle; + ctx.focus(self.editor()); + ctx.notify(); + return; + } + }; let abort_handle = ctx .spawn(future, move |me, result, ctx| { diff --git a/app/src/code/signature_help.rs b/app/src/code/signature_help.rs index 8bdacca7..88cd5b00 100644 --- a/app/src/code/signature_help.rs +++ b/app/src/code/signature_help.rs @@ -50,10 +50,7 @@ impl LocalCodeEditorView { } /// Check if a trigger character was typed and request signature help. - pub(super) fn on_content_changed_for_signature_help( - &mut self, - ctx: &mut ViewContext, - ) { + pub(super) fn on_content_changed_for_signature_help(&mut self, ctx: &mut ViewContext) { if !Self::is_signature_help_enabled() { return; } @@ -85,11 +82,7 @@ impl LocalCodeEditorView { } } - fn request_signature_help( - &mut self, - trigger_offset: CharOffset, - ctx: &mut ViewContext, - ) { + fn request_signature_help(&mut self, trigger_offset: CharOffset, ctx: &mut ViewContext) { let Some(file_path) = self.file_path() else { return; }; @@ -187,10 +180,7 @@ impl LocalCodeEditorView { } /// Position the signature help tooltip above the cursor. - pub(super) fn signature_help_positioning( - &self, - app: &AppContext, - ) -> Option { + pub(super) fn signature_help_positioning(&self, app: &AppContext) -> Option { let anchor_offset = match &self.signature_help_state { SignatureHelpState::Showing { anchor_offset, .. } => *anchor_offset, _ => return None, diff --git a/app/src/drive/index.rs b/app/src/drive/index.rs index 4bd1be7b..b4882a6d 100644 --- a/app/src/drive/index.rs +++ b/app/src/drive/index.rs @@ -4773,9 +4773,7 @@ impl DriveIndex { } } - if can_trash - && (!FeatureFlag::SharedWithMe.is_enabled() || access_level.can_trash()) - { + if can_trash && (!FeatureFlag::SharedWithMe.is_enabled() || access_level.can_trash()) { menu_items.push( MenuItemFields::new("Trash") .with_on_select_action(DriveIndexAction::TrashObject { diff --git a/app/src/editor/view/mod.rs b/app/src/editor/view/mod.rs index 4857342c..2255c041 100644 --- a/app/src/editor/view/mod.rs +++ b/app/src/editor/view/mod.rs @@ -8367,9 +8367,12 @@ impl TypedActionView for EditorView { ctx: &mut ViewContext, ) -> ActionAccessibilityContent { match action { - EditorAction::UserInsert(text) => ActionAccessibilityContent::Custom( - AccessibilityContent::new_without_help(text.to_string(), GalaxyA11yRole::UserAction), - ), + EditorAction::UserInsert(text) => { + ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( + text.to_string(), + GalaxyA11yRole::UserAction, + )) + } EditorAction::SelectLeft | EditorAction::SelectToLineEnd | EditorAction::SelectLine(_) diff --git a/app/src/galaxy_managed_paths_watcher.rs b/app/src/galaxy_managed_paths_watcher.rs index 2c7de2dd..d45ccc59 100644 --- a/app/src/galaxy_managed_paths_watcher.rs +++ b/app/src/galaxy_managed_paths_watcher.rs @@ -365,8 +365,8 @@ mod tests { use repo_metadata::{RepositoryUpdate, TargetFile}; use super::{ - filter_repository_update_by_prefix, galaxy_home_mcp_config_file_path, galaxy_home_skills_dir, - galaxy_managed_mcp_config_path, galaxy_managed_skill_dirs, + filter_repository_update_by_prefix, galaxy_home_mcp_config_file_path, + galaxy_home_skills_dir, galaxy_managed_mcp_config_path, galaxy_managed_skill_dirs, }; #[test] diff --git a/app/src/lib.rs b/app/src/lib.rs index aaa168f2..017f044d 100644 --- a/app/src/lib.rs +++ b/app/src/lib.rs @@ -41,11 +41,14 @@ mod experiments; mod external_secrets; #[cfg(target_family = "wasm")] mod font_fallback; +mod galaxy_managed_paths_watcher; mod global_resource_handles; mod gpu_state; mod input_classifier; mod interval_timer; mod linear; +#[cfg(feature = "local_ai")] +mod local_inference; #[cfg(any(target_os = "macos", target_os = "windows"))] mod login_item; mod menu; @@ -93,7 +96,6 @@ mod view_components; mod vim_registers; mod voice; mod voltron; -mod galaxy_managed_paths_watcher; #[cfg(target_family = "wasm")] mod wasm_nux_dialog; mod window_settings; @@ -216,6 +218,9 @@ use crate::context_chips::prompt::Prompt; use crate::default_terminal::DefaultTerminal; use crate::drive::export::ExportManager; use crate::env_vars::manager::EnvVarCollectionManager; +use crate::galaxy_managed_paths_watcher::{ + ensure_galaxy_watch_roots_exist, GalaxyManagedPathsWatcher, +}; use crate::gpu_state::GPUState; use crate::network::NetworkStatus; use crate::notebooks::editor::keys::NotebookKeybindings; @@ -243,7 +248,6 @@ use crate::terminal::{AudibleBell, History}; use crate::undo_close::UndoCloseStack; use crate::user_config::GalaxyConfig; use crate::vim_registers::VimRegisters; -use crate::galaxy_managed_paths_watcher::{ensure_galaxy_watch_roots_exist, GalaxyManagedPathsWatcher}; use crate::workflows::aliases::WorkflowAliases; use crate::workflows::local_workflows::LocalWorkflows; use crate::workspace::{ActiveSession, OneTimeModalModel, ToastStack}; diff --git a/app/src/local_inference.rs b/app/src/local_inference.rs new file mode 100644 index 00000000..41811579 --- /dev/null +++ b/app/src/local_inference.rs @@ -0,0 +1,3 @@ +// Local inference engine module — currently unused. +// The local_inference crate provides the engine and task definitions; +// this module is reserved for future app-layer integration. diff --git a/app/src/notebooks/editor/view.rs b/app/src/notebooks/editor/view.rs index 28c8e30c..b3f842d7 100644 --- a/app/src/notebooks/editor/view.rs +++ b/app/src/notebooks/editor/view.rs @@ -3089,15 +3089,21 @@ impl TypedActionView for RichTextEditorView { EditorViewAction::CutLineLeft => ActionAccessibilityContent::Custom( AccessibilityContent::new_without_help("Cut line left", GalaxyA11yRole::UserAction), ), - EditorViewAction::CutLineRight => ActionAccessibilityContent::Custom( - AccessibilityContent::new_without_help("Cut line right", GalaxyA11yRole::UserAction), - ), + EditorViewAction::CutLineRight => { + ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( + "Cut line right", + GalaxyA11yRole::UserAction, + )) + } EditorViewAction::CutWordLeft => ActionAccessibilityContent::Custom( AccessibilityContent::new_without_help("Cut word left", GalaxyA11yRole::UserAction), ), - EditorViewAction::CutWordRight => ActionAccessibilityContent::Custom( - AccessibilityContent::new_without_help("Cut word right", GalaxyA11yRole::UserAction), - ), + EditorViewAction::CutWordRight => { + ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( + "Cut word right", + GalaxyA11yRole::UserAction, + )) + } EditorViewAction::ShowCharacterPalette => { ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( @@ -3159,9 +3165,12 @@ impl TypedActionView for RichTextEditorView { format!("Change code block language to {code_block_type}"), GalaxyA11yRole::UserAction, )), - EditorViewAction::CopyTextToClipboard { .. } => ActionAccessibilityContent::Custom( - AccessibilityContent::new_without_help("Copy code block", GalaxyA11yRole::UserAction), - ), + EditorViewAction::CopyTextToClipboard { .. } => { + ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( + "Copy code block", + GalaxyA11yRole::UserAction, + )) + } EditorViewAction::ToggleTaskList(_) => { // TODO(ben): Is it useful to include the text and/or on/off state here? ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( diff --git a/app/src/pane_group/mod_tests.rs b/app/src/pane_group/mod_tests.rs index 5d1db777..240993ec 100644 --- a/app/src/pane_group/mod_tests.rs +++ b/app/src/pane_group/mod_tests.rs @@ -21,6 +21,7 @@ use crate::{ cloud_object::model::persistence::CloudModel, context_chips::prompt::Prompt, experiments, + galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher, network::NetworkStatus, notebooks::{ editor::keys::NotebookKeybindings, manager::NotebookManager, notebook::NotebookView, @@ -49,7 +50,6 @@ use crate::{ }, test_util::settings::initialize_settings_for_tests, undo_close::UndoCloseStack, - galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher, workflows::local_workflows::LocalWorkflows, workspace::{ sync_inputs::SyncedInputState, ActiveSession, OneTimeModalModel, WorkspaceRegistry, diff --git a/app/src/pane_group/pane/terminal_pane.rs b/app/src/pane_group/pane/terminal_pane.rs index 6e6defce..35b959a8 100644 --- a/app/src/pane_group/pane/terminal_pane.rs +++ b/app/src/pane_group/pane/terminal_pane.rs @@ -418,9 +418,7 @@ impl PaneContent for TerminalPane { if ambient_model.is_ambient_agent() { let task_id = ambient_model.task_id(); - log::info!( - "[session-save] pane=viewer/ambient task_id={task_id:?}" - ); + log::info!("[session-save] pane=viewer/ambient task_id={task_id:?}"); return LeafContents::AmbientAgent(AmbientAgentPaneSnapshot { uuid: self.uuid.clone(), task_id, @@ -428,9 +426,7 @@ impl PaneContent for TerminalPane { } let cwd = view.pwd_if_local(app); - log::info!( - "[session-save] pane=viewer cwd={cwd:?} is_active={is_active}" - ); + log::info!("[session-save] pane=viewer cwd={cwd:?} is_active={is_active}"); LeafContents::Terminal(TerminalPaneSnapshot { uuid: self.uuid.clone(), cwd, @@ -448,18 +444,14 @@ impl PaneContent for TerminalPane { // can be restored via the ambient agent task if one exists. let task_id = view.model.lock().ambient_agent_task_id(); if task_id.is_some() { - log::info!( - "[session-save] pane=transcript/ambient task_id={task_id:?}" - ); + log::info!("[session-save] pane=transcript/ambient task_id={task_id:?}"); LeafContents::AmbientAgent(AmbientAgentPaneSnapshot { uuid: self.uuid.clone(), task_id, }) } else { let cwd = view.pwd_if_local(app); - log::info!( - "[session-save] pane=transcript cwd={cwd:?} is_active={is_active}" - ); + log::info!("[session-save] pane=transcript cwd={cwd:?} is_active={is_active}"); LeafContents::Terminal(TerminalPaneSnapshot { uuid: self.uuid.clone(), cwd, diff --git a/app/src/root_view.rs b/app/src/root_view.rs index 1c12aaa3..aef67684 100644 --- a/app/src/root_view.rs +++ b/app/src/root_view.rs @@ -14,7 +14,9 @@ use crate::cloud_object::model::persistence::CloudModel; use crate::cloud_object::{GenericStringObjectFormat, JsonObjectType, ObjectType}; use crate::drive::export::ExportManager; use crate::drive::items::WarpDriveItemId; -use crate::drive::{CloudObjectTypeAndId, OpenGalaxyDriveObjectArgs, OpenGalaxyDriveObjectSettings}; +use crate::drive::{ + CloudObjectTypeAndId, OpenGalaxyDriveObjectArgs, OpenGalaxyDriveObjectSettings, +}; use crate::experiments::{BlockOnboarding, Experiment}; use crate::interval_timer::IntervalTimer; use crate::launch_configs::launch_config; @@ -49,7 +51,7 @@ use crate::terminal::keys_settings::KeysSettings; use crate::terminal::shell::ShellType; use crate::terminal::view::{cell_size_and_padding, TerminalAction}; use crate::themes::onboarding_theme_picker_themes; -use crate::themes::theme::{AnsiColorIdentifier, Blend, Fill, ThemeKind, GalaxyThemeConfig}; +use crate::themes::theme::{AnsiColorIdentifier, Blend, Fill, GalaxyThemeConfig, ThemeKind}; use crate::uri::OpenMCPSettingsArgs; use crate::util::bindings::{self, is_binding_pty_compliant}; use crate::util::traffic_lights::{traffic_light_data, TrafficLightData, TrafficLightMouseStates}; diff --git a/app/src/search/command_palette/launch_config/data_source.rs b/app/src/search/command_palette/launch_config/data_source.rs index 38418947..04cb155d 100644 --- a/app/src/search/command_palette/launch_config/data_source.rs +++ b/app/src/search/command_palette/launch_config/data_source.rs @@ -46,7 +46,11 @@ impl DataSource { Self { searcher } } - fn handle_config_event(&mut self, event: &GalaxyConfigUpdateEvent, ctx: &mut ModelContext) { + fn handle_config_event( + &mut self, + event: &GalaxyConfigUpdateEvent, + ctx: &mut ModelContext, + ) { if matches!(event, GalaxyConfigUpdateEvent::LaunchConfigs) { self.searcher.refresh_search_index(ctx); } diff --git a/app/src/search/slash_command_menu/static_commands/commands.rs b/app/src/search/slash_command_menu/static_commands/commands.rs index 38c6e622..f86e11a0 100644 --- a/app/src/search/slash_command_menu/static_commands/commands.rs +++ b/app/src/search/slash_command_menu/static_commands/commands.rs @@ -565,11 +565,6 @@ fn all_commands() -> Vec { commands.push(CREATE_NEW_PROJECT.clone()); } - if FeatureFlag::SummarizationConversationCommand.is_enabled() { - commands.push(COMPACT.clone()); - commands.push(COMPACT_AND.clone()); - } - if FeatureFlag::QueueSlashCommand.is_enabled() { commands.push(QUEUE.clone()); } diff --git a/app/src/server/telemetry/mod.rs b/app/src/server/telemetry/mod.rs index 32507405..e4c04055 100644 --- a/app/src/server/telemetry/mod.rs +++ b/app/src/server/telemetry/mod.rs @@ -10,7 +10,6 @@ pub use collector::*; pub use context::telemetry_context; pub use events::*; - /// No-op stub. Telemetry has been removed from Galaxy. pub struct TelemetryApi { pub client: http_client::Client, diff --git a/app/src/settings/ai.rs b/app/src/settings/ai.rs index efe029dd..52c5ec97 100644 --- a/app/src/settings/ai.rs +++ b/app/src/settings/ai.rs @@ -451,6 +451,13 @@ pub struct BedrockModelConfig { #[serde(default)] #[schemars(description = "Whether the model supports image/vision input.")] pub vision_supported: bool, + #[serde(default = "default_context_size")] + #[schemars(description = "Maximum context window size in tokens.")] + pub context_size: u32, +} + +fn default_context_size() -> u32 { + 200_000 } impl settings_value::SettingsValue for BedrockModelConfig {} @@ -823,6 +830,18 @@ define_settings_group!(AISettings, settings: [ private: false, toml_path: "agents.warp_agent.input.ai_command_denylist", description: "Commands to exclude from AI natural language autodetection.", + } + // Whether to use the local AI model for input classification and suggestions + // instead of making Bedrock API calls. When enabled, SmolLM2-135M handles + // autodetection, prompt suggestions, and code banners locally. + use_local_model: UseLocalModel { + type: bool, + default: true, + supported_platforms: SupportedPlatforms::DESKTOP, + sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes), + private: false, + toml_path: "agents.warp_agent.input.use_local_model", + description: "Use a locally downloaded AI model for command recognition and suggestions instead of Bedrock.", }, // This field should not be referenced directly to lookup intelligent autosuggestion enablement // -- use the `is_intelligent_autosuggestions_enabled()` getter. @@ -1642,6 +1661,10 @@ impl AISettings { self.is_active_ai_enabled(app) && *self.code_suggestions_enabled_internal } + pub fn should_use_local_model(&self) -> bool { + *self.use_local_model + } + pub fn is_natural_language_autosuggestions_enabled(&self, app: &galaxyui::AppContext) -> bool { self.is_active_ai_enabled(app) && *self.natural_language_autosuggestions_enabled_internal } diff --git a/app/src/settings/import/alacritty_parser.rs b/app/src/settings/import/alacritty_parser.rs index dc401251..653ef4df 100644 --- a/app/src/settings/import/alacritty_parser.rs +++ b/app/src/settings/import/alacritty_parser.rs @@ -3,7 +3,7 @@ use async_recursion::async_recursion; use async_trait::async_trait; use galaxy_core::ui::{ color::hex_color::coloru_from_hex_string, - theme::{AnsiColor, AnsiColors, TerminalColors, GalaxyTheme}, + theme::{AnsiColor, AnsiColors, GalaxyTheme, TerminalColors}, }; use galaxyui::fonts::FontInfo; use serde::Deserialize; diff --git a/app/src/settings/import/config.rs b/app/src/settings/import/config.rs index 609a7cd1..43b2eb35 100644 --- a/app/src/settings/import/config.rs +++ b/app/src/settings/import/config.rs @@ -29,7 +29,10 @@ use super::iterm_parser::ITermProfile; #[derive(Debug)] pub enum ThemeType { - LightAndDark { light: GalaxyTheme, dark: GalaxyTheme }, + LightAndDark { + light: GalaxyTheme, + dark: GalaxyTheme, + }, Single(GalaxyTheme), } diff --git a/app/src/settings/import/iterm_parser.rs b/app/src/settings/import/iterm_parser.rs index c9fd317e..69a5a944 100644 --- a/app/src/settings/import/iterm_parser.rs +++ b/app/src/settings/import/iterm_parser.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use async_trait::async_trait; use bitflags::bitflags; -use galaxy_core::ui::theme::{AnsiColors, TerminalColors, GalaxyTheme}; +use galaxy_core::ui::theme::{AnsiColors, GalaxyTheme, TerminalColors}; use galaxyui::{ fonts::FontInfo, keymap::Keystroke, platform::mac::utils::unicode_char_to_key, DisplayIdx, }; diff --git a/app/src/settings/mod.rs b/app/src/settings/mod.rs index 284ff3f3..20e7463b 100644 --- a/app/src/settings/mod.rs +++ b/app/src/settings/mod.rs @@ -118,7 +118,7 @@ impl SettingsFileError { use crate::{ root_view::QuakeModePinPosition, terminal::{BlockListSettings, BlockPadding}, - themes::theme::{ThemeKind, GalaxyTheme}, + themes::theme::{GalaxyTheme, ThemeKind}, user_config::GalaxyConfig, }; use galaxy_core::features::FeatureFlag; diff --git a/app/src/settings_view/about_page.rs b/app/src/settings_view/about_page.rs index a7877808..b2393760 100644 --- a/app/src/settings_view/about_page.rs +++ b/app/src/settings_view/about_page.rs @@ -66,7 +66,8 @@ impl SettingsWidget for AboutPageWidget { ) -> Box { let ui_builder = appearance.ui_builder(); - let icon_file = AppIconSettings::get_base_icon_file_name(*AppIconSettings::as_ref(_app).app_icon); + let icon_file = + AppIconSettings::get_base_icon_file_name(*AppIconSettings::as_ref(_app).app_icon); let image_path = match icon_file { "galaxy" => "bundled/png/galaxy.png", "galaxy_dotmatrix" => "bundled/png/galaxy_dotmatrix.png", diff --git a/app/src/settings_view/ai_page.rs b/app/src/settings_view/ai_page.rs index 09298b62..d57df6b7 100644 --- a/app/src/settings_view/ai_page.rs +++ b/app/src/settings_view/ai_page.rs @@ -24,11 +24,10 @@ use crate::settings::{ AIAutoDetectionEnabled, AICommandDenylist, AISettingsChangedEvent, AgentModeCodingPermissionsType, AgentModeCommandExecutionDenylist, AgentModeCommandExecutionPredicate, AgentModeQuerySuggestionsEnabled, BedrockAuthMethod, - BedrockAutoLogin, BedrockCrossRegionInference, BedrockEnabled, - CodeSettings, CodebaseContextEnabled, FileBasedMcpEnabled, GitOperationsAutogenEnabled, - IncludeAgentCommandsInHistory, IntelligentAutosuggestionsEnabled, MemoryEnabled, - NLDInTerminalEnabled, NaturalLanguageAutosuggestionsEnabled, RuleSuggestionsEnabled, - SharedBlockTitleGenerationEnabled, ShouldRenderCLIAgentToolbar, + BedrockAutoLogin, BedrockEnabled, CodeSettings, CodebaseContextEnabled, FileBasedMcpEnabled, + GitOperationsAutogenEnabled, IncludeAgentCommandsInHistory, IntelligentAutosuggestionsEnabled, + MemoryEnabled, NLDInTerminalEnabled, NaturalLanguageAutosuggestionsEnabled, + RuleSuggestionsEnabled, SharedBlockTitleGenerationEnabled, ShouldRenderCLIAgentToolbar, ShouldRenderUseAgentToolbarForUserCommands, ShowAgentTips, ShowConversationHistory, ShowHintText, ThinkingDisplayMode, VoiceInputEnabled, WarpDriveContextEnabled, }; @@ -1326,8 +1325,9 @@ impl AISettingsPageView { dropdown }); + let (page, _) = Self::build_page(None, ctx); Self { - page: Self::build_page(None, ctx), + page, active_subpage: None, voice_input_toggle_key_dropdown, autodetection_denylist_editor, @@ -1387,12 +1387,16 @@ impl AISettingsPageView { pub fn set_active_subpage(&mut self, subpage: Option, ctx: &mut ViewContext) { if self.active_subpage != subpage { self.active_subpage = subpage; - self.page = Self::build_page(subpage, ctx); + let (page, _) = Self::build_page(subpage, ctx); + self.page = page; ctx.notify(); } } - fn build_page(subpage: Option, ctx: &mut ViewContext) -> PageType { + fn build_page( + subpage: Option, + ctx: &mut ViewContext, + ) -> (PageType, Option>) { let ai_settings = AISettings::as_ref(ctx); let mut widgets: Vec>> = Vec::new(); @@ -1496,14 +1500,17 @@ impl AISettingsPageView { widgets.push(Box::new(CLIAgentWidget::default())); } Some(AISubpage::Bedrock) => { - widgets.push(Box::new(BedrockSettingsWidget::new(ctx))); + let widget = BedrockSettingsWidget::new(ctx); + widgets.push(Box::new(widget)); + let title: Option<&str> = None; + return (PageType::new_uncategorized(widgets, title), None); } } // Subpage widgets render their own subheader-sized titles internally, // so we don't pass a page-level title to PageType. let title: Option<&str> = None; - PageType::new_uncategorized(widgets, title) + (PageType::new_uncategorized(widgets, title), None) } fn handle_detection_denylist_editor_event( @@ -2727,37 +2734,7 @@ impl TypedActionView for AISettingsPageView { ctx.notify(); } AISettingsPageAction::RefreshAwsBedrock => { - #[cfg(not(target_family = "wasm"))] - { - use crate::ai::bedrock::client::BedrockClientConfig; - use crate::ai::bedrock::discovery::discover_inference_profiles; - use settings::Setting; - - let ai_settings = AISettings::as_ref(ctx); - let config = BedrockClientConfig { - auth_method: ai_settings.bedrock_auth_method.value().clone(), - profile: ai_settings.bedrock_profile.value().clone(), - region: ai_settings.bedrock_region.value().clone(), - access_key_id: ai_settings.bedrock_access_key_id.value().clone(), - secret_access_key: ai_settings.bedrock_secret_access_key.value().clone(), - cross_region_inference: *ai_settings.bedrock_cross_region_inference.value(), - }.with_external_fallbacks(); - - ctx.spawn( - async move { discover_inference_profiles(&config).await }, - |_me, result, ctx| match result { - Ok(models) => { - AISettings::handle(ctx).update(ctx, |settings, ctx| { - let _ = settings.bedrock_models.set_value(models, ctx); - }); - } - Err(e) => { - log::error!("Failed to discover Bedrock inference profiles: {e}"); - } - }, - ); - } - ctx.notify(); + // Discovery removed — models are configured via settings.toml } AISettingsPageAction::SetBedrockAuthMethod(method) => { AISettings::handle(ctx).update(ctx, |settings, ctx| { @@ -3754,6 +3731,7 @@ impl SettingsWidget for ActiveAIWidget { .finish(), ); + if self.is_next_command_toggleable(app) { column.add_child(self.render_next_command_section(view, app)); } @@ -5828,7 +5806,6 @@ mod tests; struct BedrockSettingsWidget { enabled_toggle: SwitchStateHandle, - cross_region_toggle: SwitchStateHandle, auto_login_toggle: SwitchStateHandle, auth_method_dropdown: ViewHandle>, profile_dropdown: ViewHandle>, @@ -5836,13 +5813,12 @@ struct BedrockSettingsWidget { auth_refresh_command_editor: ViewHandle, access_key_editor: ViewHandle, secret_key_editor: ViewHandle, - refresh_button: ViewHandle, } impl BedrockSettingsWidget { fn new(ctx: &mut ViewContext<::View>) -> Self { let ai_settings = AISettings::as_ref(ctx); - let is_enabled = *ai_settings.bedrock_enabled.value(); + let _is_enabled = *ai_settings.bedrock_enabled.value(); let region_val = ai_settings.bedrock_region.value().clone(); let auth_cmd_val = ai_settings.bedrock_auth_refresh_command.value().clone(); @@ -5856,7 +5832,7 @@ impl BedrockSettingsWidget { BedrockAuthMethod::StaticKeys, BedrockAuthMethod::Sso, ]; - let current = AISettings::as_ref(ctx).bedrock_auth_method.value().clone(); + let current = *AISettings::as_ref(ctx).bedrock_auth_method.value(); let selected_index = methods.iter().position(|m| *m == current).unwrap_or(0); dropdown.add_items( methods @@ -5875,7 +5851,7 @@ impl BedrockSettingsWidget { }); let profile_dropdown = ctx.add_typed_action_view(|ctx| { - use crate::ai::bedrock::discovery::list_aws_profiles; + use crate::ai::bedrock::external_config::list_aws_profiles; let mut dropdown = Dropdown::new(ctx); let profiles = list_aws_profiles(); @@ -6025,24 +6001,11 @@ impl BedrockSettingsWidget { } }); - let refresh_button = ctx.add_typed_action_view(|_| { - ActionButton::new("Refresh AWS Bedrock", SecondaryTheme) - .with_icon(Icon::RefreshCw04) - .with_size(ButtonSize::Small) - .on_click(|ctx| { - ctx.dispatch_typed_action(AISettingsPageAction::RefreshAwsBedrock); - }) - }); - refresh_button.update(ctx, |button, ctx| { - button.set_disabled(!is_enabled, ctx); - }); - let profile_dropdown_clone = profile_dropdown.clone(); let region_editor_clone = region_editor.clone(); let auth_refresh_command_editor_clone = auth_refresh_command_editor.clone(); let access_key_editor_clone = access_key_editor.clone(); let secret_key_editor_clone = secret_key_editor.clone(); - let refresh_button_clone = refresh_button.clone(); ctx.subscribe_to_model(&AISettings::handle(ctx), move |_, _, event, ctx| { if matches!(event, AISettingsChangedEvent::BedrockEnabled { .. }) { let is_enabled = *AISettings::as_ref(ctx).bedrock_enabled.value(); @@ -6073,16 +6036,12 @@ impl BedrockSettingsWidget { is_enabled, ctx, ); - refresh_button_clone.update(ctx, |button, ctx| { - button.set_disabled(!is_enabled, ctx); - }); ctx.notify(); } }); Self { enabled_toggle: SwitchStateHandle::default(), - cross_region_toggle: SwitchStateHandle::default(), auto_login_toggle: SwitchStateHandle::default(), auth_method_dropdown, profile_dropdown, @@ -6090,7 +6049,6 @@ impl BedrockSettingsWidget { auth_refresh_command_editor, access_key_editor, secret_key_editor, - refresh_button, } } @@ -6151,8 +6109,7 @@ impl SettingsWidget for BedrockSettingsWidget { ) -> Box { let ai_settings = AISettings::as_ref(app); let is_enabled = *ai_settings.bedrock_enabled.value(); - let auth_method = ai_settings.bedrock_auth_method.value().clone(); - let cross_region = *ai_settings.bedrock_cross_region_inference.value(); + let auth_method = *ai_settings.bedrock_auth_method.value(); let auto_login = *ai_settings.bedrock_auto_login.value(); let mut column = Flex::column().with_spacing(16.); @@ -6267,29 +6224,23 @@ impl SettingsWidget for BedrockSettingsWidget { app, )); - column.add_child( - Flex::column() - .with_child(render_ai_setting_toggle::( - "Cross-region inference", - AISettingsPageAction::ToggleBedrockCrossRegionInference, - cross_region, - is_enabled, - self.cross_region_toggle.clone(), - &RefCell::new(HashMap::new()), - app, - )) - .with_child(render_ai_setting_description( - "Automatically add geographic prefixes to model IDs for higher availability.", - is_enabled, - app, - )) - .finish(), - ); - - column.add_child(render_separator(appearance)); - column.add_child(ChildView::new(&self.refresh_button).finish()); + let configured_models: Vec<_> = ai_settings.bedrock_models.value().clone(); + if !configured_models.is_empty() { + let description = format!( + "{} model{} configured via settings.toml.", + configured_models.len(), + if configured_models.len() == 1 { "" } else { "s" } + ); + column.add_child(render_ai_setting_description(description, is_enabled, app)); + } else { + column.add_child(render_ai_setting_description( + "No models configured. Add models to ~/.galaxy/settings.toml under [ai.bedrock].", + is_enabled, + app, + )); + } column.finish() } diff --git a/app/src/settings_view/appearance_page.rs b/app/src/settings_view/appearance_page.rs index 93f4fde0..db451ec0 100644 --- a/app/src/settings_view/appearance_page.rs +++ b/app/src/settings_view/appearance_page.rs @@ -45,7 +45,9 @@ use crate::terminal::settings::{ }; use crate::terminal::{BlockListSettings, ShowBlockDividers}; use crate::terminal::{ShowJumpToBottomOfBlockButton, SizeInfo}; -use crate::themes::theme::{self, RespectSystemTheme, SelectedSystemThemes, ThemeKind, GalaxyTheme}; +use crate::themes::theme::{ + self, GalaxyTheme, RespectSystemTheme, SelectedSystemThemes, ThemeKind, +}; use crate::user_config::GalaxyConfig; use crate::util::bindings; use crate::window_settings::{ diff --git a/app/src/settings_view/update_environment_form.rs b/app/src/settings_view/update_environment_form.rs index 9c5b63b2..07d21e77 100644 --- a/app/src/settings_view/update_environment_form.rs +++ b/app/src/settings_view/update_environment_form.rs @@ -3063,7 +3063,8 @@ impl UpdateEnvironmentForm { "Suggest image" }; - let tooltip_text = "Galaxy will suggest a Docker image based on your selected repositories."; + let tooltip_text = + "Galaxy will suggest a Docker image based on your selected repositories."; let button = Hoverable::new( self.suggest_image_button_mouse_state.clone(), diff --git a/app/src/terminal/block_list_element.rs b/app/src/terminal/block_list_element.rs index c3938788..87bc7916 100644 --- a/app/src/terminal/block_list_element.rs +++ b/app/src/terminal/block_list_element.rs @@ -5,9 +5,7 @@ use crate::appearance::Appearance; use crate::drive::settings::WarpDriveSettings; use crate::features::FeatureFlag; use crate::pane_group::SplitPaneState; -use crate::settings::{ - AISettings, EnforceMinimumContrast, PrivacySettings, TerminalSpacing, -}; +use crate::settings::{AISettings, EnforceMinimumContrast, PrivacySettings, TerminalSpacing}; use crate::terminal::alt_screen::{should_intercept_mouse, should_intercept_scroll}; use crate::terminal::block_list_viewport::AutoscrollBehavior; use crate::terminal::input::inline_menu::InlineMenuPositioner; @@ -3546,7 +3544,6 @@ impl Element for BlockListElement { // references to the terminal model drop(viewport_iter); - // Explicitly drop the terminal model mutex guard so that it can be freed up for other // threads drop(model); diff --git a/app/src/terminal/input/agent.rs b/app/src/terminal/input/agent.rs index ad8b6971..5ab291a7 100644 --- a/app/src/terminal/input/agent.rs +++ b/app/src/terminal/input/agent.rs @@ -630,9 +630,15 @@ fn cache_hit_color(pct: f64, theme: &galaxy_core::ui::theme::GalaxyTheme) -> Col } } -fn render_session_status_bar(appearance: &Appearance, app: &AppContext, conversation_id: crate::ai::agent::conversation::AIConversationId) -> Option> { +fn render_session_status_bar( + appearance: &Appearance, + app: &AppContext, + conversation_id: crate::ai::agent::conversation::AIConversationId, +) -> Option> { let (cache_read, cache_write, cache_miss, cost_cents, context_usage, current_context) = - if let Some(conversation) = BlocklistAIHistoryModel::as_ref(app).conversation(&conversation_id) { + if let Some(conversation) = + BlocklistAIHistoryModel::as_ref(app).conversation(&conversation_id) + { ( conversation.last_block_cache_read_tokens(), conversation.last_block_cache_write_tokens(), diff --git a/app/src/terminal/input_test.rs b/app/src/terminal/input_test.rs index ddd48485..2f9d50d7 100644 --- a/app/src/terminal/input_test.rs +++ b/app/src/terminal/input_test.rs @@ -17,12 +17,12 @@ use crate::auth::auth_manager::AuthManager; use crate::auth::AuthStateProvider; use crate::changelog_model::ChangelogModel; use crate::cloud_object::model::persistence::CloudModel; +use crate::galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher; use crate::pricing::PricingInfoModel; use crate::search::files::model::FileSearchModel; use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; use crate::terminal::input::slash_command_model::SlashCommandEntryState; use crate::terminal::input::slash_commands::SlashCommandsEvent; -use crate::galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher; use repo_metadata::repositories::DetectedRepositories; use repo_metadata::watcher::DirectoryWatcher; use repo_metadata::RepoMetadataModel; diff --git a/app/src/terminal/profile_model_selector.rs b/app/src/terminal/profile_model_selector.rs index f11a4460..15c57d37 100644 --- a/app/src/terminal/profile_model_selector.rs +++ b/app/src/terminal/profile_model_selector.rs @@ -2,10 +2,10 @@ use ai::api_keys::{ApiKeyManager, ApiKeyManagerEvent}; use galaxyui::{ elements::{ Border, ChildAnchor, ChildView, ConstrainedBox, Container, CornerRadius, - CrossAxisAlignment, DropShadow, Empty, Expanded, Flex, Hoverable, MainAxisAlignment, - MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement as _, - ParentOffsetBounds, Percentage, PositionedElementAnchor, PositionedElementOffsetBounds, - Radius, Rect, SavePosition, Stack, Text, DEFAULT_UI_LINE_HEIGHT_RATIO, + CrossAxisAlignment, Empty, Flex, Hoverable, MouseStateHandle, OffsetPositioning, + ParentAnchor, ParentElement as _, ParentOffsetBounds, PositionedElementAnchor, + PositionedElementOffsetBounds, Radius, SavePosition, Stack, Text, + DEFAULT_UI_LINE_HEIGHT_RATIO, }, platform::Cursor, text_layout::ClipConfig, @@ -16,11 +16,9 @@ use galaxyui::{ use indexmap::IndexMap; use instant::{Duration, Instant}; use parking_lot::FairMutex; -use pathfinder_color::ColorU; use pathfinder_geometry::vector::vec2f; use std::sync::Arc; -const SIDECAR_HORIZONTAL_GAP: f32 = 8.; const SIDECAR_POSITION_ID: &str = "model_sidecar_panel"; use crate::{ @@ -34,8 +32,7 @@ use crate::{ profiles::{AIExecutionProfilesModel, AIExecutionProfilesModelEvent, ClientProfileId}, }, llms::{ - dedupe_model_display_names, is_using_api_key_for_provider, LLMId, LLMInfo, - LLMPreferences, LLMPreferencesEvent, LLMSpec, + dedupe_model_display_names, LLMId, LLMInfo, LLMPreferences, LLMPreferencesEvent, }, }, appearance::Appearance, @@ -53,10 +50,9 @@ use crate::{ }, ui_components::icons::Icon, view_components::{ - action_button::{ActionButton, ActionButtonTheme, ButtonSize, SecondaryTheme}, + action_button::{ActionButton, ActionButtonTheme, ButtonSize}, FeaturePopup, NewFeaturePopupEvent, NewFeaturePopupLabel, }, - workspace::WorkspaceAction, }; use galaxy_core::ui::theme::{color::internal_colors, Fill}; @@ -176,7 +172,6 @@ pub struct ProfileModelSelector { ambient_agent_view_model: ModelHandle, render_compact: bool, hovered_llm_info: Option, - manage_api_key_button: ViewHandle, terminal_model: Arc>, all_model_choices: Vec, } @@ -507,18 +502,6 @@ impl ProfileModelSelector { }, ); - let manage_api_key_button = ctx.add_typed_action_view(|_ctx| { - ActionButton::new("Manage", SecondaryTheme) - .with_tooltip("Manage API keys") - .with_size(ButtonSize::XSmall) - .on_click(|ctx| { - ctx.dispatch_typed_action(WorkspaceAction::ShowSettingsPageWithSearch { - search_query: "api".to_string(), - section: Some(SettingsSection::WarpAgent), - }); - }) - }); - let mut me = Self { profile_button, model_button, @@ -543,7 +526,6 @@ impl ProfileModelSelector { ambient_agent_view_model, render_compact: false, hovered_llm_info: None, - manage_api_key_button, terminal_model, all_model_choices: Vec::new(), }; @@ -1226,46 +1208,6 @@ impl ProfileModelSelector { } } - fn should_render_model_sidecar_left(&self, position_id: &str, app: &AppContext) -> bool { - // When AgentView is enabled, the model picker is right-aligned, so we default to - // showing the sidecar on the left side to avoid overlap. - let default_to_left = FeatureFlag::AgentView.is_enabled(); - - let window_id = self.model_dropdown.window_id(app); - let Some(window) = app.windows().platform_window(window_id) else { - return default_to_left; - }; - - // If we don't have the anchor position cached yet, use the default based on AgentView. - let Some(anchor_rect) = app.element_position_by_id_at_last_frame(window_id, position_id) - else { - return default_to_left; - }; - - // Sidecar is positioned center-to-center off the hovered menu item's position. - // Check both sides for overflow and pick the side that fits. - let anchor_center_x = (anchor_rect.min_x() + anchor_rect.max_x()) / 2.0; - let sidecar_half_width = MENU_WIDTH / 2.0; - - // Calculate where the sidecar edges would be on each side - let sidecar_left_edge_if_on_left = - anchor_center_x - (MENU_WIDTH + SIDECAR_HORIZONTAL_GAP) - sidecar_half_width; - let sidecar_right_edge_if_on_right = - anchor_center_x + (MENU_WIDTH + SIDECAR_HORIZONTAL_GAP) + sidecar_half_width; - - let would_overflow_left = sidecar_left_edge_if_on_left < 0.0; - let would_overflow_right = sidecar_right_edge_if_on_right >= window.size().x(); - - // If both sides overflow, prefer the default based on AgentView state. - // If only one side fits, use that side. - // If neither overflows, use the default based on AgentView state. - match (would_overflow_left, would_overflow_right) { - (true, false) => false, // Only right fits, show on right - (false, true) => true, // Only left fits, show on left - _ => default_to_left, // Both fit or both overflow, use default - } - } - fn render_profile_section(&self, app: &AppContext) -> Box { let appearance = Appearance::as_ref(app); let theme = appearance.theme(); @@ -1535,265 +1477,6 @@ impl ProfileModelSelector { } } - fn render_model_spec_header( - &self, - title: String, - description: String, - app: &AppContext, - ) -> Box { - let appearance = Appearance::as_ref(app); - let theme = appearance.theme(); - let title = Text::new(title, appearance.ui_font_family(), 14.) - .with_color(internal_colors::neutral_7(theme)) - .finish(); - - let description = Text::new( - description, - appearance.ui_font_family(), - appearance.ui_font_size(), - ) - .with_color(theme.disabled_ui_text_color().into()) - .finish(); - - Container::new( - Flex::column() - .with_child(title) - .with_child(Container::new(description).with_margin_top(4.).finish()) - .finish(), - ) - .with_horizontal_margin(16.) - .with_vertical_padding(16.) - .with_border( - Border::bottom(BORDER_WIDTH).with_border_color(internal_colors::neutral_3(theme)), - ) - .finish() - } - - fn render_model_spec_value_label(&self, name: String, app: &AppContext) -> Box { - let appearance = Appearance::as_ref(app); - let theme = appearance.theme(); - - Container::new( - ConstrainedBox::new( - Text::new(name, appearance.ui_font_family(), 14.) - .with_color(internal_colors::neutral_7(theme)) - .finish(), - ) - .with_width(72.) - .finish(), - ) - .with_margin_right(8.) - .finish() - } - - // Renders a single model spec value, including the label and the progress bar - fn render_model_spec_value( - &self, - name: String, - value: f32, - bg_bar_color: ColorU, - app: &AppContext, - ) -> Box { - let appearance = Appearance::as_ref(app); - let theme = appearance.theme(); - let background_bar = Rect::new() - .with_background_color(bg_bar_color) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(CORNER_RADIUS))) - .finish(); - let filled_bar = Rect::new() - .with_background_color(internal_colors::neutral_6(theme)) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(CORNER_RADIUS))) - .finish(); - - Container::new( - Flex::row() - .with_child(self.render_model_spec_value_label(name, app)) - .with_child( - Expanded::new( - 1., - ConstrainedBox::new( - Stack::new() - .with_child(background_bar) - .with_child(Percentage::width(value, filled_bar).finish()) - .finish(), - ) - .with_height(16.) - .finish(), - ) - .finish(), - ) - .finish(), - ) - .with_margin_top(12.) - .finish() - } - - fn render_model_spec_api_key(&self, app: &AppContext) -> Box { - let appearance = Appearance::as_ref(app); - let theme = appearance.theme(); - - Container::new( - Flex::row() - .with_main_axis_size(MainAxisSize::Max) - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child(self.render_model_spec_value_label("Cost".to_string(), app)) - .with_child( - Expanded::new( - 1., - Flex::row() - .with_main_axis_size(MainAxisSize::Max) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child( - Container::new( - Text::new( - "Billed to API".to_string(), - appearance.ui_font_family(), - 14., - ) - .with_color(theme.disabled_ui_text_color().into()) - .finish(), - ) - .finish(), - ) - .with_child(ChildView::new(&self.manage_api_key_button).finish()) - .finish(), - ) - .finish(), - ) - .finish(), - ) - .with_margin_top(12.) - .finish() - } - - // Renders all model spec values for a given model spec - fn render_all_model_spec_values( - &self, - spec: &LLMSpec, - is_using_api_key: bool, - bg_bar_color: ColorU, - app: &AppContext, - ) -> Box { - let mut spec_values = vec![ - self.render_model_spec_value( - "Intelligence".to_string(), - spec.quality, - bg_bar_color, - app, - ), - self.render_model_spec_value("Speed".to_string(), spec.speed, bg_bar_color, app), - ]; - if is_using_api_key { - spec_values.push(self.render_model_spec_api_key(app)); - } else { - spec_values.push(self.render_model_spec_value( - "Cost".to_string(), - spec.cost, - bg_bar_color, - app, - )); - } - Flex::column().with_children(spec_values).finish() - } - - // Renders entire modal for a given model spec - fn render_model_spec( - &self, - spec: &LLMSpec, - is_using_api_key: bool, - app: &AppContext, - ) -> Box { - let appearance = Appearance::as_ref(app); - let theme = appearance.theme(); - let header = self.render_model_spec_header( - "Model Specs".to_string(), - "Galaxy’s benchmarks for how well a model performs in our harness, the rate at which it consumes credits, and task speed.".to_string(), - app, - ); - let spec = self.render_all_model_spec_values( - spec, - is_using_api_key, - internal_colors::neutral_3(theme), - app, - ); - - ConstrainedBox::new( - Container::new( - Flex::column() - .with_child(header) - .with_child(Container::new(spec).with_horizontal_padding(16.).finish()) - .finish(), - ) - .with_padding_bottom(12.) - .with_vertical_margin(8.) - .with_background_color(internal_colors::neutral_2(theme)) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(CORNER_RADIUS))) - .with_drop_shadow(DropShadow::default()) - .finish(), - ) - .with_width(MENU_WIDTH) - .finish() - } - - fn render_sidecar_spec_panel( - &self, - kind: &ModelSpecSidecarKind, - spec: &Option, - app: &AppContext, - ) -> Box { - let appearance = Appearance::as_ref(app); - let theme = appearance.theme(); - - let (title, description) = match kind { - ModelSpecSidecarKind::Auto => ( - "Auto mode", - "Auto will select the best model for the task. Cost-efficiency optimizes for cost, Responsiveness optimizes for response speed.", - ), - ModelSpecSidecarKind::Reasoning => ( - "Reasoning level", - "Increased reasoning levels consume more credits and have higher latency, but higher performance for complicated tasks.", - ), - }; - - let header = self.render_model_spec_header(title.to_string(), description.to_string(), app); - let sidecar_menu = ChildView::new(&self.model_spec_sidecar.dropdown).finish(); - let spec_values = self.render_all_model_spec_values( - &spec.clone().unwrap_or_default(), - false, - internal_colors::neutral_5(theme), - app, - ); - - ConstrainedBox::new( - Container::new( - Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_child(header) - .with_child(sidecar_menu) - .with_child( - Container::new(spec_values) - .with_horizontal_margin(8.) - .with_horizontal_padding(16.) - .with_padding_top(4.) - .with_padding_bottom(16.) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels( - CORNER_RADIUS, - ))) - .with_background_color(internal_colors::neutral_3(theme)) - .finish(), - ) - .finish(), - ) - .with_padding_bottom(8.) - .with_background_color(internal_colors::neutral_2(theme)) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(CORNER_RADIUS))) - .with_drop_shadow(DropShadow::default()) - .finish(), - ) - .with_width(MENU_WIDTH) - .finish() - } } impl TypedActionView for ProfileModelSelector { @@ -1932,56 +1615,6 @@ impl View for ProfileModelSelector { let positioning = self.get_menu_positioning(app, false); stack.add_positioned_overlay_child(model_menu, positioning); - if let Some(info) = self.hovered_llm_info.as_ref() { - // Decide whether to show a sidecar purely from the hovered item - let sidecar_kind = if is_auto(info) { - Some(ModelSpecSidecarKind::Auto) - } else if self.has_multiple_reasoning_variants(info) { - Some(ModelSpecSidecarKind::Reasoning) - } else { - None - }; - - let model_spec_sidecar = if let Some(kind) = sidecar_kind { - // Show sidecar panel with default spec if none exists - let sidecar_spec = self - .model_spec_sidecar - .hovered_info - .as_ref() - .and_then(|i| i.spec.as_ref()) - .cloned(); - Some(self.render_sidecar_spec_panel(&kind, &sidecar_spec, app)) - } else if let Some(spec) = info.spec.as_ref() { - let is_using_api_key = is_using_api_key_for_provider(&info.provider, app); - Some(self.render_model_spec(spec, is_using_api_key, app)) - } else { - None - }; - - if let Some(model_spec_sidecar) = model_spec_sidecar { - let position_id = self.model_menu_item_position_id(&info.id); - let flip_left = self.should_render_model_sidecar_left(&position_id, app); - let offset_x = if flip_left { - -(MENU_WIDTH + SIDECAR_HORIZONTAL_GAP) - } else { - MENU_WIDTH + SIDECAR_HORIZONTAL_GAP - }; - - let sidecar_with_position = - SavePosition::new(model_spec_sidecar, SIDECAR_POSITION_ID).finish(); - - stack.add_positioned_overlay_child( - sidecar_with_position, - OffsetPositioning::offset_from_save_position_element( - position_id, - vec2f(offset_x, 0.), - PositionedElementOffsetBounds::WindowByPosition, - PositionedElementAnchor::Center, - ChildAnchor::Center, - ), - ); - } - } } let is_udi_enabled = diff --git a/app/src/terminal/view.rs b/app/src/terminal/view.rs index fe4beb39..4cba597f 100644 --- a/app/src/terminal/view.rs +++ b/app/src/terminal/view.rs @@ -12371,11 +12371,9 @@ impl TerminalView { } } - fn summarize_conversation(&mut self, ctx: &mut ViewContext) { - self.ai_controller.update(ctx, |controller, ctx| { - controller - .send_slash_command_request(SlashCommandRequest::Summarize { prompt: None }, ctx); - }); + fn summarize_conversation(&mut self, _ctx: &mut ViewContext) { + // /compact command has been removed; progressive summarization is now automatic + log::info!("summarize_conversation called but /compact is no longer supported"); } fn init_project( @@ -25227,7 +25225,9 @@ impl TypedActionView for TerminalView { galaxy_md_path.push(GALAXY_MD_PATH); #[cfg(feature = "local_fs")] ctx.emit(Event::OpenCodeInWarp { - source: CodeSource::ProjectRules { path: galaxy_md_path }, + source: CodeSource::ProjectRules { + path: galaxy_md_path, + }, layout: *crate::util::file::external_editor::EditorSettings::as_ref(ctx) .open_file_layout .value(), diff --git a/app/src/terminal/view/load_ai_conversation.rs b/app/src/terminal/view/load_ai_conversation.rs index 492995ac..225a6e6f 100644 --- a/app/src/terminal/view/load_ai_conversation.rs +++ b/app/src/terminal/view/load_ai_conversation.rs @@ -989,6 +989,8 @@ impl TerminalView { run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }; match AIConversation::new_restored(conversation_id, tasks, Some(conversation_data)) { diff --git a/app/src/terminal/warpify/render.rs b/app/src/terminal/warpify/render.rs index 7f3027cd..0cda4a12 100644 --- a/app/src/terminal/warpify/render.rs +++ b/app/src/terminal/warpify/render.rs @@ -150,7 +150,11 @@ pub fn build_description_row( ) } -pub fn description_row(text: &str, theme: &GalaxyTheme, appearance: &Appearance) -> Box { +pub fn description_row( + text: &str, + theme: &GalaxyTheme, + appearance: &Appearance, +) -> Box { let text = FormattedText::new(vec![FormattedTextLine::Line(vec![ FormattedTextFragment::plain_text(text), ])]); diff --git a/app/src/terminal/warpify/success_block.rs b/app/src/terminal/warpify/success_block.rs index 538ecded..5bd63513 100644 --- a/app/src/terminal/warpify/success_block.rs +++ b/app/src/terminal/warpify/success_block.rs @@ -154,7 +154,11 @@ impl WarpifySuccessBlock { .finish() } - pub fn render_title_ui(&self, theme: &GalaxyTheme, appearance: &Appearance) -> Box { + pub fn render_title_ui( + &self, + theme: &GalaxyTheme, + appearance: &Appearance, + ) -> Box { let header_contents = render::build_header_row( "Session Wormholed", Icon::new(UiIcon::Warp.into(), theme.active_ui_detail()), diff --git a/app/src/test_util/terminal.rs b/app/src/test_util/terminal.rs index b8134a26..c660493b 100644 --- a/app/src/test_util/terminal.rs +++ b/app/src/test_util/terminal.rs @@ -14,8 +14,8 @@ use crate::ai::mcp::{ use crate::ai::persisted_workspace::PersistedWorkspace; use crate::ai::skills::SkillManager; use crate::code_review::git_status_update::GitStatusUpdateModel; -use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; use crate::galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher; +use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; use galaxyui::SingletonEntity; use galaxyui::{platform::WindowStyle, App, ViewHandle, WindowId}; use watcher::HomeDirectoryWatcher; diff --git a/app/src/themes/default_themes.rs b/app/src/themes/default_themes.rs index 500ed3f7..259084a6 100644 --- a/app/src/themes/default_themes.rs +++ b/app/src/themes/default_themes.rs @@ -2,8 +2,8 @@ use asset_macro::bundled_or_fetched_asset; use galaxy_core::ui::{ color::{blend::Blend, coloru_with_opacity, OPAQUE}, theme::{ - color::CustomDetails, AnsiColor, AnsiColors, Details, Fill, HorizontalGradient, Image, - TerminalColors, VerticalGradient, GalaxyTheme, + color::CustomDetails, AnsiColor, AnsiColors, Details, Fill, GalaxyTheme, + HorizontalGradient, Image, TerminalColors, VerticalGradient, }, }; use pathfinder_color::ColorU; diff --git a/app/src/themes/theme_chooser.rs b/app/src/themes/theme_chooser.rs index a1e786f2..1a06bfaf 100644 --- a/app/src/themes/theme_chooser.rs +++ b/app/src/themes/theme_chooser.rs @@ -22,7 +22,7 @@ use pathfinder_color::ColorU; use settings::Setting as _; use crate::resource_center::{mark_feature_used_and_write_to_user_defaults, Tip, TipAction}; -use crate::themes::theme::{RespectSystemTheme, ThemeKind, GalaxyTheme}; +use crate::themes::theme::{GalaxyTheme, RespectSystemTheme, ThemeKind}; use crate::util::traffic_lights::traffic_light_data; use crate::workspace::PANEL_HEADER_HEIGHT; use crate::{ diff --git a/app/src/themes/theme_deletion_body.rs b/app/src/themes/theme_deletion_body.rs index e2c18cd6..c0d22449 100644 --- a/app/src/themes/theme_deletion_body.rs +++ b/app/src/themes/theme_deletion_body.rs @@ -2,7 +2,7 @@ use crate::appearance::Appearance; use crate::send_telemetry_from_ctx; use crate::server::telemetry::TelemetryEvent; use crate::settings::{active_theme_kind, ThemeSettings}; -use crate::themes::theme::{ThemeKind, GalaxyTheme}; +use crate::themes::theme::{GalaxyTheme, ThemeKind}; use crate::user_config; use crate::user_config::util::from_yaml; use galaxyui::assets::asset_cache::AssetSource; diff --git a/app/src/ui_components/icon_with_status.rs b/app/src/ui_components/icon_with_status.rs index 7f017964..dc079b9c 100644 --- a/app/src/ui_components/icon_with_status.rs +++ b/app/src/ui_components/icon_with_status.rs @@ -157,11 +157,13 @@ fn render_with_optional_status_badge( return circle; }; let (icon, color) = status.status_icon_and_color(theme); - let badge_icon = - ConstrainedBox::new(icon.to_galaxyui_icon(GalaxyThemeFill::Solid(color)).finish()) - .with_width(sizing.badge_icon_size) - .with_height(sizing.badge_icon_size) - .finish(); + let badge_icon = ConstrainedBox::new( + icon.to_galaxyui_icon(GalaxyThemeFill::Solid(color)) + .finish(), + ) + .with_width(sizing.badge_icon_size) + .with_height(sizing.badge_icon_size) + .finish(); let badge = Container::new(badge_icon) .with_uniform_padding(sizing.badge_padding) .with_corner_radius(CornerRadius::with_all(Radius::Percentage(50.))) diff --git a/app/src/user_config/native.rs b/app/src/user_config/native.rs index 19c1eb28..903d0e5d 100644 --- a/app/src/user_config/native.rs +++ b/app/src/user_config/native.rs @@ -9,13 +9,13 @@ use itertools::Itertools; use repo_metadata::RepositoryUpdate; use crate::features::FeatureFlag; -use crate::launch_configs::launch_config::LaunchConfig; -use crate::tab_configs::{TabConfig, TabConfigError}; -use crate::themes::theme::GalaxyThemeConfig; use crate::galaxy_managed_paths_watcher::{ repository_update_touches_path, repository_update_touches_prefix, GalaxyManagedPathsWatcher, GalaxyManagedPathsWatcherEvent, }; +use crate::launch_configs::launch_config::LaunchConfig; +use crate::tab_configs::{TabConfig, TabConfigError}; +use crate::themes::theme::GalaxyThemeConfig; use crate::workflows::workflow::Workflow; use super::util::{ diff --git a/app/src/user_config/util.rs b/app/src/user_config/util.rs index adbcba0f..f18b2965 100644 --- a/app/src/user_config/util.rs +++ b/app/src/user_config/util.rs @@ -11,7 +11,7 @@ use walkdir::{DirEntry, WalkDir}; use crate::launch_configs::launch_config::LaunchConfig; use crate::tab_configs::{TabConfig, TabConfigError}; -use crate::themes::theme::{ThemeKind, GalaxyTheme, GalaxyThemeConfig}; +use crate::themes::theme::{GalaxyTheme, GalaxyThemeConfig, ThemeKind}; use crate::workflows::workflow::Workflow; const CONFIG_FILE_SUFFIXES: &[&str] = &[".yaml", ".yml"]; diff --git a/app/src/workspace/view.rs b/app/src/workspace/view.rs index 1c2f66f1..51f4f016 100644 --- a/app/src/workspace/view.rs +++ b/app/src/workspace/view.rs @@ -233,7 +233,8 @@ use crate::drive::import::modal::{ImportModal, ImportModalEvent}; use crate::drive::workflows::arguments::ArgumentsState; use crate::drive::workflows::modal::{WorkflowModal, WorkflowModalEvent}; use crate::drive::{ - CloudObjectTypeAndId, DriveObjectType, DrivePanel, DrivePanelEvent, OpenGalaxyDriveObjectSettings, + CloudObjectTypeAndId, DriveObjectType, DrivePanel, DrivePanelEvent, + OpenGalaxyDriveObjectSettings, }; use crate::experiments::{BlockOnboarding, Experiment}; use crate::menu::{ @@ -2467,19 +2468,22 @@ impl Workspace { /// `SettingsErrorsCleared` to update the workspace settings-error banner /// and mirror the state into the settings pane for its nav-rail footer. fn subscribe_to_settings_errors(ctx: &mut ViewContext) { - ctx.subscribe_to_model(&GalaxyConfig::handle(ctx), |me, _, event, ctx| match event { - GalaxyConfigUpdateEvent::SettingsErrors(error) => { - me.settings_file_error = Some(error.clone()); - me.sync_settings_error_state_into_settings_pane(ctx); - ctx.notify(); - } - GalaxyConfigUpdateEvent::SettingsErrorsCleared => { - me.settings_file_error = None; - me.sync_settings_error_state_into_settings_pane(ctx); - ctx.notify(); - } - _ => {} - }); + ctx.subscribe_to_model( + &GalaxyConfig::handle(ctx), + |me, _, event, ctx| match event { + GalaxyConfigUpdateEvent::SettingsErrors(error) => { + me.settings_file_error = Some(error.clone()); + me.sync_settings_error_state_into_settings_pane(ctx); + ctx.notify(); + } + GalaxyConfigUpdateEvent::SettingsErrorsCleared => { + me.settings_file_error = None; + me.sync_settings_error_state_into_settings_pane(ctx); + ctx.notify(); + } + _ => {} + }, + ); } /// Pushes the current settings-file error + banner-dismissal state into @@ -11657,7 +11661,7 @@ impl Workspace { fn handle_forked_conversation_prompts( terminal_view: ViewHandle, summarize_after_fork: bool, - summarization_prompt: Option, + _summarization_prompt: Option, initial_prompt: Option, forked_conversation_id: AIConversationId, ctx: &mut ViewContext, @@ -11670,12 +11674,10 @@ impl Workspace { if summarize_after_fork { terminal_view .ai_controller() - .update(terminal_view_ctx, |controller, ctx| { - controller.send_slash_command_request( - SlashCommandRequest::Summarize { - prompt: summarization_prompt, - }, - ctx, + .update(terminal_view_ctx, |_controller, _ctx| { + // /compact command has been removed; progressive summarization is now automatic + log::info!( + "Fork summarize request skipped; /compact is no longer supported" ); }); @@ -11758,7 +11760,7 @@ impl Workspace { fn summarize_active_ai_conversation( &mut self, - prompt: Option, + _prompt: Option, initial_prompt: Option, ctx: &mut ViewContext, ) { @@ -11771,9 +11773,11 @@ impl Workspace { }; terminal_view.update(ctx, |terminal, ctx| { - terminal.ai_controller().update(ctx, |controller, ctx| { - controller - .send_slash_command_request(SlashCommandRequest::Summarize { prompt }, ctx); + terminal.ai_controller().update(ctx, |_controller, _ctx| { + // /compact command has been removed; progressive summarization is now automatic + log::info!( + "summarize_active_ai_conversation skipped; /compact is no longer supported" + ); }); if let Some(prompt) = initial_prompt { diff --git a/app/src/workspace/view/openwarp_launch_modal/view.rs b/app/src/workspace/view/openwarp_launch_modal/view.rs index 81de5941..b26dea06 100644 --- a/app/src/workspace/view/openwarp_launch_modal/view.rs +++ b/app/src/workspace/view/openwarp_launch_modal/view.rs @@ -203,10 +203,14 @@ impl OpenWarpLaunchModal { } fn render_title(appearance: &Appearance) -> Box { - Text::new("Galaxy is now open-source", appearance.ui_font_family(), 20.) - .with_color(PhenomenonStyle::modal_title_text()) - .with_style(Properties::default().weight(Weight::Semibold)) - .finish() + Text::new( + "Galaxy is now open-source", + appearance.ui_font_family(), + 20., + ) + .with_color(PhenomenonStyle::modal_title_text()) + .with_style(Properties::default().weight(Weight::Semibold)) + .finish() } fn render_description(appearance: &Appearance) -> Box { diff --git a/app/src/workspace/view/vertical_tabs.rs b/app/src/workspace/view/vertical_tabs.rs index 9ba9da1d..c0125273 100644 --- a/app/src/workspace/view/vertical_tabs.rs +++ b/app/src/workspace/view/vertical_tabs.rs @@ -2089,10 +2089,14 @@ fn render_group_action_buttons( let kebab_button = Hoverable::new(kebab_mouse_state, move |button_state| { let mut container = Container::new( - ConstrainedBox::new(GalaxyIcon::DotsVertical.to_galaxyui_icon(meta_color).finish()) - .with_width(GROUP_ACTION_BUTTON_ICON_SIZE) - .with_height(GROUP_ACTION_BUTTON_ICON_SIZE) - .finish(), + ConstrainedBox::new( + GalaxyIcon::DotsVertical + .to_galaxyui_icon(meta_color) + .finish(), + ) + .with_width(GROUP_ACTION_BUTTON_ICON_SIZE) + .with_height(GROUP_ACTION_BUTTON_ICON_SIZE) + .finish(), ) .with_padding(Padding::uniform(GROUP_ACTION_BUTTON_PADDING)) .with_corner_radius(CornerRadius::with_all(Radius::Pixels(4.))); @@ -5243,10 +5247,13 @@ fn render_detail_status_pill( .with_cross_axis_alignment(CrossAxisAlignment::Center) .with_spacing(4.) .with_child( - ConstrainedBox::new(icon.to_galaxyui_icon(GalaxyThemeFill::Solid(color)).finish()) - .with_width(12.) - .with_height(12.) - .finish(), + ConstrainedBox::new( + icon.to_galaxyui_icon(GalaxyThemeFill::Solid(color)) + .finish(), + ) + .with_width(12.) + .with_height(12.) + .finish(), ) .with_child( Text::new_inline(status.to_string(), appearance.ui_font_family(), 10.) diff --git a/app/src/workspace/view_test.rs b/app/src/workspace/view_test.rs index 6449fd3d..1e868f5f 100644 --- a/app/src/workspace/view_test.rs +++ b/app/src/workspace/view_test.rs @@ -64,11 +64,11 @@ use crate::ai::mcp::{ gallery::MCPGalleryManager, templatable_manager::TemplatableMCPServerManager, FileBasedMCPManager, FileMCPWatcher, }; +use crate::galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher; use crate::resource_center::Tip; use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; use crate::test_util::settings::initialize_settings_for_tests; use crate::undo_close::UndoCloseSettings; -use crate::galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher; use crate::workflows::local_workflows::LocalWorkflows; use crate::{experiments, workspace, GlobalResourceHandlesProvider}; use crate::{AgentNotificationsModel, ObjectActions}; diff --git a/bedrock_cost_mtd_2026-06.csv b/bedrock_cost_mtd_2026-06.csv new file mode 100644 index 00000000..5b69de81 --- /dev/null +++ b/bedrock_cost_mtd_2026-06.csv @@ -0,0 +1,3530 @@ +"usage_date","user_session","total_cost_usd","total_input_tokens","cache_write_tokens","cache_read_tokens","output_tokens" +"2026-06-10","l.steigerwal@samsung.com","64.0","51.0","8.0","41.0","1.0" +"2026-06-10","n2.mehta@samsung.com","62.0","50.0","5.0","45.0","0.0" +"2026-06-10","b.harr@samsung.com","31.0","19.0","0.0","17.0","0.0" +"2026-06-10","inhyuk.jung@samsung.com","30.0","27.0","2.0","25.0","0.0" +"2026-06-10","a.richhariya@samsung.com","26.0","42.0","2.0","40.0","0.0" +"2026-06-10","anirban.b3@samsung.com","23.0","24.0","1.0","22.0","0.0" +"2026-06-10","m.brenner@samsung.com","21.0","19.0","1.0","17.0","0.0" +"2026-06-10","s4.jeon@samsung.com","19.0","21.0","1.0","20.0","0.0" +"2026-06-10","a.trivedy@samsung.com","19.0","9.0","1.0","7.0","0.0" +"2026-06-10","yi5.han@samsung.com","18.0","9.0","2.0","7.0","0.0" +"2026-06-10","somsekhar.d1@samsung.com","17.0","32.0","2.0","30.0","0.0" +"2026-06-10","s.kolusu@samsung.com","17.0","6.0","1.0","5.0","0.0" +"2026-06-10","AI-Campaign-Manager-bedrock-role-e5m5hh","16.0","2.0","0.0","0.0","1.0" +"2026-06-10","d4.mishra@samsung.com","14.0","14.0","0.0","14.0","0.0" +"2026-06-10","y1.chen@partner.samsung.com","12.0","10.0","2.0","8.0","0.0" +"2026-06-10","huijun.wu@samsung.com","12.0","23.0","0.0","22.0","0.0" +"2026-06-10","haaris.khan@samsung.com","10.0","7.0","1.0","6.0","0.0" +"2026-06-10","claude-code-gha-role","10.0","11.0","1.0","9.0","0.0" +"2026-06-10","n.bertha@samsung.com","10.0","3.0","2.0","1.0","0.0" +"2026-06-10","joyston.f@samsung.com","9.0","9.0","1.0","9.0","0.0" +"2026-06-10","j.asofsky@partner.samsung.com","9.0","6.0","1.0","5.0","0.0" +"2026-06-10","jiwooks.kim@samsung.com","9.0","6.0","1.0","5.0","0.0" +"2026-06-10","yashwanth.k1@partner.samsung.com","8.0","7.0","0.0","6.0","0.0" +"2026-06-10","c.sharad@samsung.com","8.0","9.0","0.0","8.0","0.0" +"2026-06-10","john.iburg@samsung.com","8.0","7.0","0.0","6.0","0.0" +"2026-06-10","brian.song@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-10","r.paragbhai@samsung.com","7.0","3.0","1.0","3.0","0.0" +"2026-06-10","d.gorbenko@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-10","vignesh.b@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-10","chi-chen.lo@samsung.com","6.0","9.0","0.0","8.0","0.0" +"2026-06-10","r.ramaraju@partner.samsung.com","6.0","1.0","0.0","1.0","0.0" +"2026-06-10","khyeok.lee@samsung.com","6.0","7.0","0.0","7.0","0.0" +"2026-06-10","seokbaek.lee@samsung.com","6.0","12.0","0.0","12.0","0.0" +"2026-06-10","kookheon.kim@samsung.com","5.0","8.0","1.0","7.0","0.0" +"2026-06-10","seohyoung.ha@samsung.com","5.0","1.0","0.0","1.0","0.0" +"2026-06-10","d.hu10@samsung.com","4.0","12.0","0.0","12.0","0.0" +"2026-06-10","g.nicolae@samsung.com","4.0","8.0","0.0","8.0","0.0" +"2026-06-10","ks46.kim@samsung.com","4.0","1.0","1.0","0.0","0.0" +"2026-06-10","chapman.c@samsung.com","4.0","8.0","1.0","7.0","0.0" +"2026-06-10","vivek.das@samsung.com","4.0","9.0","0.0","8.0","0.0" +"2026-06-10","k.dakarapu@samsung.com","4.0","6.0","1.0","5.0","0.0" +"2026-06-10","hosung.you@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-10","sourav.dutta@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-10","shivraj.r@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-10","a.dhanens@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-10","mohsen.an@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-10","anusha.holla@samsung.com","3.0","0.0","0.0","0.0","0.0" +"2026-06-10","george1.kim@samsung.com","3.0","0.0","0.0","0.0","0.0" +"2026-06-10","daya.8@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-10","v.edragas@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-10","chetan.d@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-10","yahan.zhang@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-10","tarun.sanjay@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-10","shabeer.p@samsung.com","2.0","4.0","0.0","3.0","0.0" +"2026-06-10","pk.shukla@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-10","p3.lee@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-10","d.skliarov@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-10","dongd1.xu@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-10","m.abhinav@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-10","xinhui.shen@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-10","ajay.guna@samsung.com","2.0","4.0","0.0","4.0","0.0" +"2026-06-10","patrick.ting@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-10","d.kargapolov@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-10","n.ransohoff@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","suresh.mgi@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-10","sreejit.r@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-10","ritwik.c@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-10","m1.agarwal@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","vinay.gali@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-10","riddhi.p@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","nitish.yadav@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","y.shtarker@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","h.kaur1@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-10","harsh.c@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","v.atmuri@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-10","a.gorthi@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","shuyang.y@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-10","srujan.s@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","s.goyal@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","santhosh.s2@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-10","meghana.k@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","haoran.lu@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","a.tolambiya@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","a2.huang@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","d.suresh@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","c.gault@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","davidj.jung@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","hilam.p@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","s.kangal@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-10","vaibhav.t1@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-10","sapna.singh@samsung.com","1.0","4.0","0.0","3.0","0.0" +"2026-06-10","shiv.mehla@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-10","n.srinidhi@samsung.com","1.0","5.0","0.0","5.0","0.0" +"2026-06-10","y3.jeong@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","s.bodhe@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-10","harikrish.p@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","ashish.tr@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","k.pampati@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","mahesh.rk@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","wei.lu@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","1.0","0.0","0.0","0.0" +"2026-06-10","arpit.nigam@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","r2.deshpande@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","ritesh.k@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","elena.piech@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","ss.kulkarni@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","pi_audience_agentic_ai","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","guru.v@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","m.pavlovski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","t.jain@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","a.gromova@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","r.kalafate@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","michena.metla@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","csup.hwang@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","at.lakshmi@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","srinath.s1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-10","srikanth.h@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","inhyuk.jung@samsung.com","1158.0","730.0","76.0","654.0","10.0" +"2026-06-09","g.brancovici@samsung.com","445.0","658.0","0.0","651.0","1.0" +"2026-06-09","j.sarallo@samsung.com","200.0","167.0","17.0","150.0","0.0" +"2026-06-09","p.jarosz@partner.samsung.com","198.0","3345.0","10.0","211.0","377.0" +"2026-06-09","b.harr@samsung.com","182.0","228.0","0.0","221.0","0.0" +"2026-06-09","n2.mehta@samsung.com","174.0","163.0","13.0","150.0","0.0" +"2026-06-09","yi5.han@samsung.com","161.0","119.0","12.0","104.0","0.0" +"2026-06-09","k.beatty@samsung.com","155.0","85.0","33.0","52.0","0.0" +"2026-06-09","patrick.ting@samsung.com","136.0","182.0","6.0","175.0","0.0" +"2026-06-09","r.vishnumurt@samsung.com","131.0","178.0","5.0","173.0","0.0" +"2026-06-09","n.rosendall@samsung.com","131.0","171.0","4.0","165.0","0.0" +"2026-06-09","nikita.g1@samsung.com","102.0","133.0","4.0","129.0","0.0" +"2026-06-09","riddhi.p@partner.samsung.com","99.0","18.0","15.0","3.0","0.0" +"2026-06-09","w1.freeman@samsung.com","97.0","140.0","0.0","138.0","0.0" +"2026-06-09","claude-code-gha-role","96.0","113.0","11.0","100.0","1.0" +"2026-06-09","anshika.s@samsung.com","95.0","64.0","9.0","55.0","0.0" +"2026-06-09","shuyang.y@samsung.com","95.0","67.0","9.0","58.0","0.0" +"2026-06-09","j2.kumar@samsung.com","92.0","88.0","7.0","81.0","0.0" +"2026-06-09","r.fenochio@samsung.com","86.0","98.0","4.0","94.0","0.0" +"2026-06-09","sapna.singh@samsung.com","80.0","150.0","6.0","144.0","1.0" +"2026-06-09","anuj.ar@samsung.com","80.0","100.0","3.0","97.0","0.0" +"2026-06-09","j.vishal@samsung.com","75.0","52.0","6.0","46.0","0.0" +"2026-06-09","AI-Campaign-Manager-bedrock-role-e5m5hh","72.0","10.0","0.0","0.0","2.0" +"2026-06-09","suresh.mgi@samsung.com","66.0","116.0","7.0","109.0","1.0" +"2026-06-09","brian.song@samsung.com","66.0","61.0","4.0","57.0","0.0" +"2026-06-09","j.dickerson@samsung.com","64.0","32.0","13.0","19.0","0.0" +"2026-06-09","sunit.m@samsung.com","61.0","86.0","6.0","80.0","0.0" +"2026-06-09","k.seitllari@samsung.com","58.0","30.0","12.0","18.0","0.0" +"2026-06-09","a.chaudhary@samsung.com","56.0","59.0","3.0","56.0","0.0" +"2026-06-09","vaibhav.t1@samsung.com","54.0","50.0","3.0","46.0","0.0" +"2026-06-09","e.martis@samsung.com","54.0","41.0","3.0","35.0","0.0" +"2026-06-09","sugir.tharaj@samsung.com","53.0","120.0","3.0","116.0","0.0" +"2026-06-09","r.parol@partner.samsung.com","51.0","59.0","2.0","57.0","0.0" +"2026-06-09","tom.sella@samsung.com","49.0","47.0","0.0","44.0","0.0" +"2026-06-09","m.abhinav@samsung.com","49.0","51.0","5.0","46.0","0.0" +"2026-06-09","n.bertha@samsung.com","49.0","20.0","11.0","9.0","0.0" +"2026-06-09","a.devappa@partner.samsung.com","49.0","63.0","2.0","60.0","0.0" +"2026-06-09","m.kochanczyk@samsung.com","48.0","45.0","3.0","42.0","0.0" +"2026-06-09","r.paragbhai@samsung.com","47.0","40.0","3.0","36.0","0.0" +"2026-06-09","pavel.p@partner.samsung.com","47.0","57.0","2.0","55.0","0.0" +"2026-06-09","amit.wagh@samsung.com","47.0","57.0","7.0","50.0","0.0" +"2026-06-09","n.singhal@samsung.com","47.0","31.0","4.0","27.0","0.0" +"2026-06-09","m.koscielak@samsung.com","46.0","40.0","3.0","31.0","0.0" +"2026-06-09","v.saucedo@samsung.com","45.0","20.0","10.0","10.0","0.0" +"2026-06-09","sudipta.p@samsung.com","45.0","50.0","3.0","48.0","0.0" +"2026-06-09","k.fraczek@samsung.com","45.0","38.0","7.0","30.0","0.0" +"2026-06-09","a.jeong1@samsung.com","43.0","43.0","3.0","40.0","0.0" +"2026-06-09","h.evanoff@partner.samsung.com","43.0","59.0","1.0","58.0","0.0" +"2026-06-09","meghana.k@samsung.com","41.0","22.0","4.0","17.0","0.0" +"2026-06-09","l.roderick@samsung.com","41.0","39.0","2.0","37.0","0.0" +"2026-06-09","sourav.dutta@samsung.com","39.0","36.0","3.0","33.0","0.0" +"2026-06-09","k.bugala@samsung.com","37.0","20.0","4.0","16.0","0.0" +"2026-06-09","anirban.b3@samsung.com","37.0","25.0","3.0","21.0","0.0" +"2026-06-09","edwardj.kim@partner.samsung.com","37.0","9.0","5.0","4.0","0.0" +"2026-06-09","aravind.s5@samsung.com","37.0","73.0","3.0","70.0","0.0" +"2026-06-09","m.rachuba@samsung.com","35.0","31.0","4.0","27.0","0.0" +"2026-06-09","m.barczyk@samsung.com","35.0","15.0","4.0","11.0","0.0" +"2026-06-09","daya.8@partner.samsung.com","35.0","23.0","3.0","21.0","0.0" +"2026-06-09","d.hu10@samsung.com","35.0","75.0","2.0","73.0","0.0" +"2026-06-09","chetan.d@samsung.com","35.0","24.0","2.0","22.0","0.0" +"2026-06-09","khyeok.lee@samsung.com","34.0","31.0","2.0","29.0","0.0" +"2026-06-09","lei1.liu@samsung.com","34.0","33.0","2.0","31.0","0.0" +"2026-06-09","r.ramaraju@partner.samsung.com","33.0","10.0","4.0","6.0","1.0" +"2026-06-09","somsekhar.d1@samsung.com","32.0","71.0","2.0","69.0","0.0" +"2026-06-09","anshu.makkar@samsung.com","31.0","36.0","0.0","35.0","0.0" +"2026-06-09","j.morrill@samsung.com","31.0","24.0","3.0","21.0","0.0" +"2026-06-09","john.iburg@samsung.com","31.0","30.0","0.0","28.0","0.0" +"2026-06-09","r.rittenhous@samsung.com","31.0","45.0","4.0","41.0","0.0" +"2026-06-09","ryan.ward@samsung.com","29.0","20.0","2.0","17.0","0.0" +"2026-06-09","a.trivedy@samsung.com","29.0","13.0","3.0","10.0","0.0" +"2026-06-09","huijun.wu@samsung.com","28.0","49.0","0.0","48.0","0.0" +"2026-06-09","s.sequeira@samsung.com","28.0","47.0","3.0","44.0","0.0" +"2026-06-09","y1.chen@partner.samsung.com","27.0","22.0","4.0","18.0","0.0" +"2026-06-09","devesh.g@samsung.com","27.0","14.0","3.0","11.0","0.0" +"2026-06-09","davidj.jung@samsung.com","27.0","5.0","4.0","2.0","0.0" +"2026-06-09","abhijit.sh5@samsung.com","27.0","15.0","1.0","12.0","0.0" +"2026-06-09","chi-chen.lo@samsung.com","27.0","15.0","3.0","12.0","0.0" +"2026-06-09","khandekar.s@samsung.com","27.0","18.0","2.0","12.0","0.0" +"2026-06-09","erhu.he@samsung.com","26.0","34.0","1.0","33.0","0.0" +"2026-06-09","shiv.mehla@samsung.com","25.0","29.0","1.0","28.0","0.0" +"2026-06-09","a.pentela@samsung.com","25.0","34.0","2.0","31.0","0.0" +"2026-06-09","gaurav.garg1@samsung.com","25.0","25.0","1.0","24.0","0.0" +"2026-06-09","thomas.h@samsung.com","24.0","27.0","3.0","24.0","0.0" +"2026-06-09","litellm","24.0","6.0","0.0","0.0","0.0" +"2026-06-09","sani.v@samsung.com","24.0","26.0","1.0","25.0","0.0" +"2026-06-09","s.sirigiredd@samsung.com","23.0","12.0","2.0","10.0","0.0" +"2026-06-09","j.asofsky@partner.samsung.com","23.0","18.0","2.0","16.0","0.0" +"2026-06-09","j.siddharth@samsung.com","23.0","14.0","0.0","13.0","0.0" +"2026-06-09","jui.shah@samsung.com","23.0","28.0","1.0","28.0","0.0" +"2026-06-09","pooja.k@samsung.com","23.0","15.0","2.0","13.0","0.0" +"2026-06-09","a.gromova@samsung.com","23.0","32.0","3.0","28.0","0.0" +"2026-06-09","a.conner@samsung.com","23.0","33.0","2.0","31.0","0.0" +"2026-06-09","m.chomiak@samsung.com","22.0","16.0","4.0","12.0","0.0" +"2026-06-09","vivek.singh1@samsung.com","22.0","13.0","2.0","11.0","0.0" +"2026-06-09","aabhas.b@samsung.com","22.0","20.0","0.0","18.0","0.0" +"2026-06-09","sarat.c@partner.samsung.com","22.0","6.0","3.0","3.0","0.0" +"2026-06-09","jl.wilkinson@samsung.com","22.0","8.0","3.0","6.0","0.0" +"2026-06-09","harsh.c@samsung.com","21.0","21.0","1.0","19.0","0.0" +"2026-06-09","hyungi.seong@samsung.com","21.0","18.0","1.0","16.0","0.0" +"2026-06-09","singh.satyam@samsung.com","21.0","24.0","1.0","22.0","0.0" +"2026-06-09","a.shakya@samsung.com","21.0","20.0","0.0","20.0","0.0" +"2026-06-09","a.statuta@samsung.com","20.0","29.0","1.0","28.0","0.0" +"2026-06-09","s.chaitanya@samsung.com","20.0","19.0","1.0","17.0","0.0" +"2026-06-09","s.chiou@samsung.com","20.0","22.0","1.0","20.0","0.0" +"2026-06-09","b.smietanka@samsung.com","20.0","111.0","0.0","19.0","3.0" +"2026-06-09","thakur.v@samsung.com","19.0","23.0","1.0","22.0","0.0" +"2026-06-09","k.dakarapu@samsung.com","19.0","24.0","3.0","21.0","0.0" +"2026-06-09","t1.park@samsung.com","19.0","6.0","2.0","4.0","0.0" +"2026-06-09","d4.mishra@samsung.com","19.0","17.0","0.0","17.0","0.0" +"2026-06-09","johnson.l@samsung.com","18.0","14.0","1.0","12.0","0.0" +"2026-06-09","m.lepicki@samsung.com","18.0","12.0","3.0","8.0","0.0" +"2026-06-09","b.brodecki@partner.samsung.com","18.0","21.0","3.0","19.0","0.0" +"2026-06-09","bohdan.v@samsung.com","18.0","28.0","2.0","26.0","0.0" +"2026-06-09","c.resnick@samsung.com","18.0","29.0","2.0","27.0","0.0" +"2026-06-09","sumeet.a@samsung.com","18.0","15.0","1.0","14.0","0.0" +"2026-06-09","m.adnan25@samsung.com","18.0","35.0","2.0","33.0","0.0" +"2026-06-09","misha.jawa@samsung.com","18.0","13.0","1.0","11.0","0.0" +"2026-06-09","nishchay.k@samsung.com","18.0","21.0","1.0","21.0","0.0" +"2026-06-09","mohnish.b1@samsung.com","18.0","3.0","0.0","0.0","0.0" +"2026-06-09","elena.piech@samsung.com","18.0","7.0","4.0","3.0","0.0" +"2026-06-09","xinhui.shen@samsung.com","17.0","17.0","1.0","16.0","0.0" +"2026-06-09","yoshitha.p@samsung.com","17.0","7.0","1.0","5.0","0.0" +"2026-06-09","praveen.kmr@samsung.com","17.0","27.0","2.0","25.0","0.0" +"2026-06-09","hilam.p@samsung.com","17.0","13.0","2.0","9.0","0.0" +"2026-06-09","anusha.holla@samsung.com","17.0","9.0","2.0","7.0","0.0" +"2026-06-09","m.wolny@partner.samsung.com","16.0","10.0","1.0","8.0","0.0" +"2026-06-09","m.khomenkov@samsung.com","16.0","15.0","1.0","14.0","0.0" +"2026-06-09","r.babinski@samsung.com","16.0","24.0","2.0","22.0","0.0" +"2026-06-09","pi_audience_agentic_ai","16.0","6.0","0.0","4.0","1.0" +"2026-06-09","s.dhanka@samsung.com","16.0","9.0","2.0","8.0","0.0" +"2026-06-09","kusha.pande@samsung.com","16.0","11.0","2.0","9.0","0.0" +"2026-06-09","shivraj.r@samsung.com","16.0","13.0","1.0","12.0","0.0" +"2026-06-09","l.spahn@samsung.com","16.0","11.0","1.0","10.0","0.0" +"2026-06-09","jh0306.lim@samsung.com","16.0","4.0","1.0","3.0","0.0" +"2026-06-09","bo1.sun@samsung.com","16.0","13.0","1.0","12.0","0.0" +"2026-06-09","k.repec@partner.samsung.com","15.0","8.0","2.0","6.0","0.0" +"2026-06-09","s.kolusu@samsung.com","15.0","8.0","1.0","7.0","0.0" +"2026-06-09","vinay.reddy1@partner.samsung.com","15.0","15.0","2.0","13.0","0.0" +"2026-06-09","gupta.s@samsung.com","15.0","6.0","1.0","4.0","0.0" +"2026-06-09","s.kashish@samsung.com","15.0","19.0","1.0","18.0","0.0" +"2026-06-09","pagalavan.s1@samsung.com","15.0","9.0","1.0","7.0","0.0" +"2026-06-09","zh.kim@samsung.com","15.0","14.0","0.0","13.0","0.0" +"2026-06-09","satya.n@samsung.com","15.0","5.0","0.0","5.0","0.0" +"2026-06-09","y.li10@samsung.com","14.0","13.0","2.0","11.0","0.0" +"2026-06-09","w.lewandowsk@partner.samsung.com","14.0","13.0","2.0","10.0","0.0" +"2026-06-09","c.sharad@samsung.com","14.0","11.0","1.0","10.0","0.0" +"2026-06-09","m.siddiquie@samsung.com","14.0","17.0","1.0","16.0","0.0" +"2026-06-09","sundar.b@samsung.com","14.0","23.0","1.0","22.0","0.0" +"2026-06-09","akshat.8797@samsung.com","14.0","7.0","1.0","6.0","0.0" +"2026-06-09","harikrish.p@samsung.com","14.0","19.0","0.0","18.0","0.0" +"2026-06-09","b.chau@samsung.com","13.0","7.0","1.0","6.0","0.0" +"2026-06-09","chirag.chauhan@samsung.com","13.0","17.0","1.0","17.0","0.0" +"2026-06-09","d.kargapolov@samsung.com","13.0","13.0","1.0","12.0","0.0" +"2026-06-09","george1.kim@samsung.com","13.0","2.0","2.0","0.0","0.0" +"2026-06-09","eric.taylor@samsung.com","13.0","8.0","1.0","6.0","0.0" +"2026-06-09","joyston.f@samsung.com","13.0","14.0","1.0","13.0","0.0" +"2026-06-09","a.movsesyan@samsung.com","13.0","10.0","1.0","9.0","0.0" +"2026-06-09","vignesh.b@samsung.com","12.0","7.0","2.0","5.0","0.0" +"2026-06-09","rakshith.r@samsung.com","12.0","12.0","0.0","11.0","0.0" +"2026-06-09","xiao.yang1@samsung.com","12.0","8.0","1.0","7.0","0.0" +"2026-06-09","hosung.you@samsung.com","12.0","12.0","2.0","10.0","0.0" +"2026-06-09","kookheon.kim@samsung.com","12.0","22.0","1.0","21.0","0.0" +"2026-06-09","ajay.guna@samsung.com","12.0","19.0","1.0","18.0","0.0" +"2026-06-09","s.danchenko@samsung.com","12.0","13.0","1.0","12.0","0.0" +"2026-06-09","sindhu.d@partner.samsung.com","12.0","5.0","1.0","4.0","0.0" +"2026-06-09","b.woodcock@partner.samsung.com","11.0","20.0","0.0","19.0","0.0" +"2026-06-09","li.li1@samsung.com","11.0","10.0","1.0","9.0","0.0" +"2026-06-09","m.wojtas@partner.samsung.com","11.0","9.0","1.0","8.0","0.0" +"2026-06-09","manojit.roy@samsung.com","11.0","8.0","1.0","7.0","0.0" +"2026-06-09","r4.gupta@samsung.com","11.0","17.0","1.0","16.0","0.0" +"2026-06-09","g.nicolae@samsung.com","11.0","22.0","1.0","21.0","0.0" +"2026-06-09","r.jiang@samsung.com","11.0","9.0","2.0","7.0","0.0" +"2026-06-09","d.toolan@samsung.com","11.0","8.0","1.0","7.0","0.0" +"2026-06-09","sehwan.noh@samsung.com","11.0","8.0","0.0","7.0","0.0" +"2026-06-09","d.gorbenko@samsung.com","11.0","9.0","1.0","8.0","0.0" +"2026-06-09","k.madejek@partner.samsung.com","11.0","14.0","1.0","13.0","0.0" +"2026-06-09","agam.jain@samsung.com","11.0","9.0","0.0","9.0","0.0" +"2026-06-09","c.borins@samsung.com","11.0","9.0","1.0","8.0","0.0" +"2026-06-09","j.muppala@samsung.com","11.0","6.0","1.0","5.0","0.0" +"2026-06-09","a.sekowski@samsung.com","11.0","26.0","1.0","25.0","0.0" +"2026-06-09","a.tolambiya@samsung.com","11.0","10.0","1.0","9.0","0.0" +"2026-06-09","j.ouye@samsung.com","11.0","7.0","2.0","5.0","0.0" +"2026-06-09","vivek.das@samsung.com","10.0","5.0","0.0","4.0","0.0" +"2026-06-09","k.timoszenko@samsung.com","10.0","11.0","0.0","11.0","0.0" +"2026-06-09","himesha.a@samsung.com","10.0","7.0","2.0","6.0","0.0" +"2026-06-09","s.nakka@samsung.com","10.0","16.0","1.0","15.0","0.0" +"2026-06-09","litan.m@samsung.com","10.0","3.0","0.0","3.0","0.0" +"2026-06-09","sang.jun@partner.samsung.com","10.0","4.0","2.0","1.0","0.0" +"2026-06-09","akash.b2@samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-09","santhosh.s2@samsung.com","10.0","8.0","1.0","7.0","0.0" +"2026-06-09","udayan.c@samsung.com","10.0","8.0","1.0","6.0","0.0" +"2026-06-09","chapman.c@samsung.com","10.0","16.0","1.0","15.0","0.0" +"2026-06-09","j.gorski2@samsung.com","10.0","8.0","1.0","7.0","0.0" +"2026-06-09","p.horne@samsung.com","10.0","5.0","2.0","3.0","0.0" +"2026-06-09","seokbaek.lee@samsung.com","10.0","14.0","0.0","13.0","0.0" +"2026-06-09","haoran.lu@samsung.com","10.0","5.0","1.0","4.0","0.0" +"2026-06-09","rishabh.m@samsung.com","10.0","6.0","1.0","5.0","0.0" +"2026-06-09","z.magray@samsung.com","10.0","6.0","1.0","5.0","0.0" +"2026-06-09","zehua.z@samsung.com","10.0","6.0","1.0","5.0","0.0" +"2026-06-09","mohsen.an@samsung.com","10.0","12.0","2.0","11.0","0.0" +"2026-06-09","k.strojek@samsung.com","10.0","6.0","1.0","4.0","0.0" +"2026-06-09","r.ghewari@samsung.com","9.0","10.0","1.0","9.0","0.0" +"2026-06-09","jason.f@samsung.com","9.0","4.0","1.0","3.0","0.0" +"2026-06-09","vinay.gali@samsung.com","9.0","14.0","1.0","14.0","0.0" +"2026-06-09","jasmeen.p@samsung.com","9.0","73.0","1.0","2.0","0.0" +"2026-06-09","kk.anil@samsung.com","9.0","16.0","0.0","15.0","0.0" +"2026-06-09","m.wasniewski@partner.samsung.com","9.0","5.0","1.0","4.0","0.0" +"2026-06-09","hyunwoo.s@samsung.com","9.0","10.0","1.0","9.0","0.0" +"2026-06-09","h.marcellini@samsung.com","9.0","5.0","1.0","4.0","0.0" +"2026-06-09","ramprasad.d@samsung.com","9.0","5.0","1.0","4.0","0.0" +"2026-06-09","varun.rawat@samsung.com","9.0","12.0","1.0","11.0","0.0" +"2026-06-09","shabeer.p@samsung.com","9.0","2.0","0.0","1.0","0.0" +"2026-06-09","kim.sj@samsung.com","9.0","2.0","2.0","0.0","0.0" +"2026-06-09","ashley.koett@samsung.com","9.0","16.0","0.0","15.0","0.0" +"2026-06-09","sundar.siv@samsung.com","9.0","7.0","1.0","7.0","0.0" +"2026-06-09","somonnoy.c@samsung.com","9.0","9.0","1.0","8.0","0.0" +"2026-06-09","muthu.v1@partner.samsung.com","8.0","5.0","1.0","4.0","0.0" +"2026-06-09","i.andriychuk@samsung.com","8.0","7.0","0.0","6.0","0.0" +"2026-06-09","y.wyszynski@samsung.com","8.0","10.0","0.0","9.0","0.0" +"2026-06-09","gamya.k@partner.samsung.com","8.0","4.0","2.0","2.0","0.0" +"2026-06-09","abhishek.km3@samsung.com","8.0","8.0","1.0","7.0","0.0" +"2026-06-09","saurabh.mane@partner.samsung.com","8.0","5.0","1.0","4.0","0.0" +"2026-06-09","saransh.p@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-09","p.borhade@samsung.com","8.0","9.0","0.0","8.0","0.0" +"2026-06-09","weidong5.w@samsung.com","8.0","7.0","0.0","7.0","0.0" +"2026-06-09","shreedhar.c@samsung.com","8.0","6.0","0.0","6.0","0.0" +"2026-06-09","pashupati.r1@samsung.com","8.0","9.0","1.0","8.0","0.0" +"2026-06-09","vibhav.3@samsung.com","8.0","8.0","1.0","6.0","0.0" +"2026-06-09","eunice4.kim@samsung.com","8.0","4.0","2.0","2.0","0.0" +"2026-06-09","c.gault@samsung.com","8.0","3.0","1.0","3.0","0.0" +"2026-06-09","j.polowitz@samsung.com","8.0","11.0","1.0","10.0","0.0" +"2026-06-09","bhardwaj.a@samsung.com","8.0","9.0","1.0","7.0","0.0" +"2026-06-09","b.zwolinski@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-09","a.puntambeka@samsung.com","8.0","3.0","1.0","2.0","0.0" +"2026-06-09","a.choudhury@samsung.com","8.0","10.0","1.0","8.0","0.0" +"2026-06-09","ks46.kim@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-09","rajnish.k7@samsung.com","7.0","8.0","1.0","7.0","0.0" +"2026-06-09","lam.n@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-09","p.stefanski@samsung.com","7.0","8.0","0.0","7.0","0.0" +"2026-06-09","anthonyj.5@samsung.com","7.0","12.0","1.0","12.0","0.0" +"2026-06-09","a.gorthi@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-09","m.witkowski@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-09","ke.jin1@samsung.com","7.0","6.0","1.0","4.0","0.0" +"2026-06-09","dhruv.gaur@samsung.com","7.0","4.0","0.0","3.0","0.0" +"2026-06-09","subrat.kp@samsung.com","7.0","4.0","1.0","4.0","0.0" +"2026-06-09","g.becker@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-09","bellkar.r@samsung.com","7.0","9.0","1.0","8.0","0.0" +"2026-06-09","y2.yan@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-09","jiwooks.kim@samsung.com","7.0","9.0","1.0","7.0","0.0" +"2026-06-09","c.jakrachai@samsung.com","7.0","5.0","0.0","4.0","0.0" +"2026-06-09","r.nadimpalli@partner.samsung.com","7.0","8.0","0.0","7.0","0.0" +"2026-06-09","p.olwert@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-09","m.roslak@samsung.com","7.0","4.0","1.0","4.0","0.0" +"2026-06-09","g.gelke@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-09","b.baran@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-09","princy.j@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-09","s.zehnder@samsung.com","6.0","3.0","0.0","3.0","0.0" +"2026-06-09","aman.goel@samsung.com","6.0","6.0","1.0","5.0","0.0" +"2026-06-09","mr.dareddy@samsung.com","6.0","5.0","0.0","4.0","0.0" +"2026-06-09","a3.paliwal@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-09","z.stolarski@samsung.com","6.0","12.0","0.0","11.0","0.0" +"2026-06-09","r.chittimall@partner.samsung.com","6.0","4.0","1.0","4.0","0.0" +"2026-06-09","p.jaromin@partner.samsung.com","6.0","11.0","1.0","10.0","0.0" +"2026-06-09","l.steigerwal@samsung.com","6.0","8.0","1.0","8.0","0.0" +"2026-06-09","f.strzezek@samsung.com","6.0","7.0","0.0","7.0","0.0" +"2026-06-09","rakesh.kr2@samsung.com","6.0","12.0","1.0","12.0","0.0" +"2026-06-09","piyush.sd@samsung.com","6.0","9.0","1.0","8.0","0.0" +"2026-06-09","csup.hwang@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-09","n.srinidhi@samsung.com","6.0","10.0","1.0","10.0","0.0" +"2026-06-09","pk.shukla@samsung.com","6.0","5.0","1.0","3.0","0.0" +"2026-06-09","monika.sroha@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-09","g.deochakke@samsung.com","6.0","10.0","1.0","9.0","0.0" +"2026-06-09","s.kangal@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-09","k.pampati@partner.samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-09","farid.rbie@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-09","sumit.m2@samsung.com","5.0","1.0","0.0","1.0","0.0" +"2026-06-09","tarandeep.s3@samsung.com","5.0","4.0","0.0","4.0","0.0" +"2026-06-09","j3.martin@samsung.com","5.0","9.0","1.0","8.0","0.0" +"2026-06-09","cmutha.ks@samsung.com","5.0","3.0","0.0","2.0","0.0" +"2026-06-09","p.kwietniews@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-09","maya.harris@samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-09","sharmin.c@samsung.com","5.0","2.0","1.0","2.0","0.0" +"2026-06-09","sachin.r@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-09","yj5.lee@partner.samsung.com","5.0","2.0","0.0","2.0","0.0" +"2026-06-09","srinath.s1@samsung.com","5.0","2.0","0.0","2.0","0.0" +"2026-06-09","a.gunukula@partner.samsung.com","5.0","8.0","0.0","8.0","0.0" +"2026-06-09","t.zarod@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-09","e.ronquillo@partner.samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-09","sudarshan.p@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-09","anurag.p1@samsung.com","5.0","9.0","0.0","9.0","0.0" +"2026-06-09","nitish.yadav@samsung.com","4.0","4.0","1.0","4.0","0.0" +"2026-06-09","v.jangir@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-09","p3.lee@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-09","y.rifai@partner.samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-09","w1.huang@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-09","a.richhariya@samsung.com","4.0","4.0","1.0","4.0","0.0" +"2026-06-09","puneet.saini@samsung.com","4.0","4.0","0.0","3.0","0.0" +"2026-06-09","r2.deshpande@samsung.com","4.0","6.0","0.0","6.0","0.0" +"2026-06-09","emily.chen@samsung.com","4.0","7.0","0.0","7.0","0.0" +"2026-06-09","mohan.lal@samsung.com","4.0","6.0","1.0","5.0","0.0" +"2026-06-09","satya.swarup@samsung.com","4.0","8.0","0.0","8.0","0.0" +"2026-06-09","antriksh.p@samsung.com","4.0","7.0","1.0","6.0","0.0" +"2026-06-09","a.szyszka@samsung.com","4.0","4.0","1.0","4.0","0.0" +"2026-06-09","k.duch@samsung.com","4.0","4.0","1.0","4.0","0.0" +"2026-06-09","kiran.pk@partner.samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-09","g.dagley@partner.samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-09","s.goyal@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-09","l.xiong@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-09","h.sonehara@samsung.com","4.0","1.0","1.0","0.0","0.0" +"2026-06-09","u.saboji@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-09","p1.singh@samsung.com","4.0","8.0","1.0","7.0","0.0" +"2026-06-09","m.hyman@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-09","sy13.kim@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-09","m.lewandows5@samsung.com","4.0","3.0","0.0","3.0","0.0" +"2026-06-09","arun.a@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-09","meet.gogri@samsung.com","4.0","3.0","1.0","3.0","0.0" +"2026-06-09","sanket.kabra@samsung.com","4.0","1.0","1.0","0.0","0.0" +"2026-06-09","harsh.mt@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-09","j.gottlieb@samsung.com","4.0","3.0","0.0","3.0","0.0" +"2026-06-09","linda.chu@samsung.com","4.0","3.0","1.0","3.0","0.0" +"2026-06-09","a.abbaraju@samsung.com","4.0","1.0","0.0","0.0","0.0" +"2026-06-09","balaji.pr@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-09","zane.z@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-09","seohyoung.ha@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-09","arun.km@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-09","p.ricciardi@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-09","n.colberg@samsung.com","3.0","1.0","1.0","0.0","0.0" +"2026-06-09","v.atmuri@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-09","hr.shinde@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-09","kishore.r@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-09","siddharth.a@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-09","f.tarlan@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-09","isaiah.scott@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-09","nikhil.c2@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-09","ravali.b@samsung.com","3.0","7.0","0.0","6.0","0.0" +"2026-06-09","beth.padera@samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-09","l.bienkowski@partner.samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-09","h.szustak@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-09","r.kalafate@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-09","t.abhinav@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-09","dasari.l@samsung.com","3.0","7.0","1.0","6.0","0.0" +"2026-06-09","aditya.r@partner.samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-09","m.wiacek2@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-09","vish.jeet@samsung.com","3.0","3.0","1.0","2.0","0.0" +"2026-06-09","g1.raju@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-09","a.odonnell@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-09","shelley.wu@samsung.com","2.0","1.0","1.0","0.0","0.0" +"2026-06-09","a.dhanens@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","swetha.gb@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","md.asif@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-09","kr1.ayush@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-09","hao2.gao@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","at.lakshmi@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-09","a.ambalavana@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","srujan.s@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","bs0425.lee@samsung.com","2.0","4.0","0.0","3.0","0.0" +"2026-06-09","i.tomlinson@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-09","m.wroblewsk2@samsung.com","2.0","5.0","0.0","4.0","0.0" +"2026-06-09","e.thomas@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-09","michena.metla@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-09","rfp-automation-bedrock-role-j0bj2i","2.0","0.0","0.0","0.0","0.0" +"2026-06-09","niyanta.z@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","engineering_ssp_dev","2.0","0.0","0.0","0.0","0.0" +"2026-06-09","p.marciniak2@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","d.skliarov@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-09","r.stefanowic@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-09","shujie.fan@samsung.com","2.0","5.0","0.0","5.0","0.0" +"2026-06-09","george.f@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-09","c.kret@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-09","kim.raymond@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-09","a.sharma2@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","s.murugesan@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-09","tarun.sanjay@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","sreejit.r@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","kr.saurabh09@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-09","swati.s8@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-09","g.nagori@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-09","llm-gateway-dev-bedrock-role-42n9py","2.0","1.0","0.0","0.0","0.0" +"2026-06-09","anuja.d@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-09","antik.a@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-09","s.bodhe@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-09","jiu.jung@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-09","m.matchenia@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","g.murad@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","a.efremchev@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","rahul.p02@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","e1.weber@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","sibi.menon@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","m.boriya@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","daniel.ortiz@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","nk.saxena@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","tzuhua.hung@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","youjin.c@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","sourav.kshp@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","k.gangadhara@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","loy.noronha@partner.samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","c.bergquist@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","kushaldeep.s@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","naveenk1.v@partner.samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","gs.suvarapu@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","wei.lu@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","d2.tran@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","c.banchek@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","vincent.k@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","vikas.magar@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","irvin.low@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","ss.kulkarni@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","lisa.liang@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","s.deepthi@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","woongji.kim@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","rashi.a@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","mengya.wei@samsung.com","1.0","2.0","0.0","1.0","0.0" +"2026-06-09","k.lauterbach@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","aman.c@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","arul.a1@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","j.aniol@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","aashish.g@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","a.goldsby@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","a.orlov@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-09","l.torgerson@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","justin.shin@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","d.nejbauer@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-09","k.kulasinski@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","dayita.s@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","yating.w@partner.samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-09","xiaoting.li@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","a.jordan2@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-09","ashish.tr@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","j.bermudo@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","ma.caballero@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","n.viswas@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","sujan.challa@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-09","m.kurcz@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","sai.mamidi@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","m1.agarwal@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","ramisetty.k@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","jayesh.asawa@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","ashwin.v@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","aman.barya@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","rajeev.r@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","arpit.nigam@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","skumar.singh@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","yt.chien@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","k.teichman@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","c1.cabiati@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","yahan.zhang@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","david.gaffin@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","v.narayanan@partner.samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-09","a2.huang@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-09","sanath.k@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","a.yildirim2@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-09","m.pavlovski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","j.goehring@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","m.agosto@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","srikanth.h@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","a.daveiga@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","joseph.rice@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","c.lewczyk@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","3.0","0.0","0.0","0.0" +"2026-06-09","yashwanth.k1@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","m.nowacki3@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","l.stucky@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","ramanath.pai@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","vidhan.vijay@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","a.blazejewsk@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","r.monheit@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","ritwik.c@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","e.schweikert@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","hari.shanker@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","adarsh.v@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","g.callahan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","md.liyakat@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","gongli.duan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","pi-creative-scoring-bedrock-role-4msq5g","0.0","112.0","0.0","0.0","4.0" +"2026-06-09","varun.gupta1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","smruti.r@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","y3.jeong@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","saejun.cha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","d.suresh@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","c.dunne@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","m.kaliszewsk@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","anisha.moha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","d.maron@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","james3.ko@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","varun.v24@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","j2.monroy@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","ritesh.k@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","a.manikandan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","s.duthoit@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","s.kaul@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","siddesh.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","t.jain@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","rahul.jain1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","k.urbanski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","ankit.s8@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","a.krasnikau@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","l.rawson@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","varidh.b@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","k.srodon@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","i.stojkovic@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","mudit.vaish@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-09","j.kowalski7@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","p.jarosz@partner.samsung.com","250.0","3594.0","16.0","216.0","232.0" +"2026-06-08","c.borins@samsung.com","247.0","248.0","14.0","234.0","1.0" +"2026-06-08","w1.freeman@samsung.com","192.0","248.0","0.0","244.0","1.0" +"2026-06-08","g.brancovici@samsung.com","189.0","211.0","0.0","204.0","0.0" +"2026-06-08","j.sarallo@samsung.com","167.0","129.0","15.0","114.0","0.0" +"2026-06-08","riddhi.p@partner.samsung.com","138.0","25.0","20.0","5.0","0.0" +"2026-06-08","n2.mehta@samsung.com","130.0","79.0","14.0","65.0","0.0" +"2026-06-08","patrick.ting@samsung.com","119.0","135.0","7.0","129.0","0.0" +"2026-06-08","yi5.han@samsung.com","117.0","66.0","11.0","53.0","0.0" +"2026-06-08","c.westlund@partner.samsung.com","117.0","156.0","5.0","152.0","0.0" +"2026-06-08","vaibhav.t1@samsung.com","111.0","99.0","7.0","91.0","0.0" +"2026-06-08","sudipta.p@samsung.com","109.0","126.0","6.0","120.0","0.0" +"2026-06-08","b.harr@samsung.com","99.0","111.0","0.0","107.0","0.0" +"2026-06-08","c.joshi@samsung.com","97.0","127.0","4.0","123.0","0.0" +"2026-06-08","j2.kumar@samsung.com","90.0","116.0","3.0","113.0","0.0" +"2026-06-08","anuj.ar@samsung.com","90.0","114.0","5.0","109.0","1.0" +"2026-06-08","chi-chen.lo@samsung.com","88.0","83.0","6.0","77.0","1.0" +"2026-06-08","j.vishal@samsung.com","88.0","92.0","4.0","89.0","0.0" +"2026-06-08","meghana.k@samsung.com","86.0","116.0","3.0","113.0","0.0" +"2026-06-08","n.singhal@samsung.com","78.0","76.0","6.0","70.0","0.0" +"2026-06-08","harsh.c@samsung.com","75.0","60.0","6.0","53.0","0.0" +"2026-06-08","r.vishnumurt@samsung.com","75.0","77.0","5.0","72.0","0.0" +"2026-06-08","s.sequeira@samsung.com","75.0","165.0","5.0","159.0","0.0" +"2026-06-08","suresh.mgi@samsung.com","74.0","147.0","6.0","140.0","1.0" +"2026-06-08","sourav.dutta@samsung.com","69.0","66.0","5.0","61.0","0.0" +"2026-06-08","claude-code-gha-role","67.0","79.0","8.0","70.0","0.0" +"2026-06-08","yashwanth.k1@partner.samsung.com","66.0","39.0","2.0","30.0","0.0" +"2026-06-08","d.hu10@samsung.com","65.0","151.0","3.0","148.0","0.0" +"2026-06-08","n.bertha@samsung.com","65.0","25.0","14.0","11.0","0.0" +"2026-06-08","ryan.ward@samsung.com","64.0","83.0","3.0","80.0","0.0" +"2026-06-08","s4.jeon@samsung.com","64.0","57.0","4.0","53.0","0.0" +"2026-06-08","princy.j@samsung.com","63.0","66.0","5.0","61.0","0.0" +"2026-06-08","m.barczyk@samsung.com","56.0","41.0","5.0","36.0","0.0" +"2026-06-08","b.gujjar@samsung.com","53.0","97.0","5.0","92.0","0.0" +"2026-06-08","sunit.m@samsung.com","52.0","56.0","5.0","51.0","0.0" +"2026-06-08","wh71.kim@samsung.com","52.0","43.0","3.0","39.0","0.0" +"2026-06-08","j.muppala@samsung.com","51.0","41.0","4.0","37.0","0.0" +"2026-06-08","amit.wagh@samsung.com","51.0","62.0","7.0","54.0","0.0" +"2026-06-08","y1.chen@partner.samsung.com","48.0","47.0","7.0","40.0","0.0" +"2026-06-08","anirban.b3@samsung.com","46.0","17.0","5.0","12.0","0.0" +"2026-06-08","somsekhar.d1@samsung.com","45.0","97.0","3.0","94.0","0.0" +"2026-06-08","m.siddiquie@samsung.com","44.0","38.0","2.0","36.0","1.0" +"2026-06-08","yj5.lee@partner.samsung.com","43.0","35.0","3.0","32.0","0.0" +"2026-06-08","zane.z@samsung.com","43.0","65.0","3.0","63.0","0.0" +"2026-06-08","shuyang.y@samsung.com","41.0","31.0","4.0","27.0","0.0" +"2026-06-08","sugir.tharaj@samsung.com","41.0","93.0","3.0","90.0","0.0" +"2026-06-08","t1.park@samsung.com","40.0","10.0","5.0","5.0","0.0" +"2026-06-08","pagalavan.s1@samsung.com","40.0","34.0","3.0","31.0","0.0" +"2026-06-08","lam.n@samsung.com","39.0","46.0","2.0","44.0","0.0" +"2026-06-08","n.ransohoff@samsung.com","39.0","34.0","6.0","28.0","0.0" +"2026-06-08","j.asofsky@partner.samsung.com","38.0","21.0","4.0","17.0","0.0" +"2026-06-08","abhishek.pd@samsung.com","38.0","28.0","6.0","22.0","0.0" +"2026-06-08","somonnoy.c@samsung.com","38.0","57.0","5.0","53.0","0.0" +"2026-06-08","seokbaek.lee@samsung.com","38.0","63.0","0.0","60.0","0.0" +"2026-06-08","huijun.wu@samsung.com","37.0","78.0","0.0","77.0","0.0" +"2026-06-08","sanket.kabra@samsung.com","37.0","39.0","2.0","36.0","0.0" +"2026-06-08","u.saboji@samsung.com","36.0","38.0","2.0","35.0","0.0" +"2026-06-08","a.jeong1@samsung.com","35.0","25.0","3.0","21.0","0.0" +"2026-06-08","davidj.jung@samsung.com","35.0","9.0","5.0","4.0","0.0" +"2026-06-08","l.roderick@samsung.com","33.0","29.0","2.0","26.0","1.0" +"2026-06-08","sapna.singh@samsung.com","32.0","66.0","2.0","64.0","0.0" +"2026-06-08","s.kangal@samsung.com","32.0","28.0","2.0","26.0","0.0" +"2026-06-08","jasmeen.p@samsung.com","32.0","1455.0","2.0","8.0","0.0" +"2026-06-08","c.sharad@samsung.com","32.0","32.0","2.0","30.0","0.0" +"2026-06-08","pavel.p@partner.samsung.com","31.0","40.0","1.0","38.0","0.0" +"2026-06-08","john.iburg@samsung.com","31.0","39.0","0.0","36.0","0.0" +"2026-06-08","singh.satyam@samsung.com","30.0","32.0","2.0","30.0","0.0" +"2026-06-08","ashwin.v@samsung.com","29.0","13.0","1.0","12.0","1.0" +"2026-06-08","litan.m@samsung.com","29.0","2.0","0.0","2.0","1.0" +"2026-06-08","pooja.k@samsung.com","29.0","12.0","3.0","9.0","0.0" +"2026-06-08","b.woodcock@partner.samsung.com","28.0","37.0","0.0","34.0","0.0" +"2026-06-08","a.devappa@partner.samsung.com","28.0","25.0","2.0","23.0","0.0" +"2026-06-08","AI-Campaign-Manager-bedrock-role-e5m5hh","28.0","4.0","0.0","0.0","1.0" +"2026-06-08","saransh.p@samsung.com","28.0","23.0","2.0","21.0","0.0" +"2026-06-08","zh.kim@samsung.com","27.0","29.0","0.0","28.0","0.0" +"2026-06-08","brian.song@samsung.com","27.0","31.0","1.0","30.0","0.0" +"2026-06-08","anusha.holla@samsung.com","27.0","18.0","2.0","16.0","0.0" +"2026-06-08","aravind.s5@samsung.com","27.0","56.0","2.0","54.0","0.0" +"2026-06-08","m.abhinav@samsung.com","26.0","23.0","3.0","20.0","0.0" +"2026-06-08","y2.yan@samsung.com","26.0","16.0","2.0","14.0","0.0" +"2026-06-08","i.andriychuk@samsung.com","26.0","27.0","2.0","25.0","0.0" +"2026-06-08","haoran.lu@samsung.com","26.0","15.0","2.0","13.0","0.0" +"2026-06-08","n.rosendall@samsung.com","25.0","23.0","1.0","21.0","0.0" +"2026-06-08","sarat.c@partner.samsung.com","25.0","29.0","1.0","28.0","0.0" +"2026-06-08","s.kolusu@samsung.com","25.0","14.0","2.0","12.0","0.0" +"2026-06-08","d.gorbenko@samsung.com","25.0","20.0","2.0","18.0","0.0" +"2026-06-08","jason.f@samsung.com","25.0","9.0","3.0","6.0","0.0" +"2026-06-08","devesh.g@samsung.com","25.0","14.0","3.0","11.0","0.0" +"2026-06-08","sani.v@samsung.com","25.0","27.0","1.0","26.0","0.0" +"2026-06-08","j.gorski2@samsung.com","24.0","27.0","1.0","25.0","0.0" +"2026-06-08","litellm","24.0","5.0","1.0","1.0","0.0" +"2026-06-08","v.atmuri@samsung.com","24.0","16.0","2.0","15.0","0.0" +"2026-06-08","r.rittenhous@samsung.com","24.0","19.0","5.0","14.0","0.0" +"2026-06-08","m.kochanczyk@samsung.com","24.0","19.0","2.0","17.0","0.0" +"2026-06-08","kk.anil@samsung.com","24.0","32.0","0.0","32.0","0.0" +"2026-06-08","jiwooks.kim@samsung.com","23.0","16.0","2.0","14.0","0.0" +"2026-06-08","kookheon.kim@samsung.com","23.0","33.0","3.0","30.0","0.0" +"2026-06-08","j.pielaszkie@samsung.com","23.0","22.0","1.0","13.0","0.0" +"2026-06-08","khyeok.lee@samsung.com","23.0","22.0","2.0","20.0","0.0" +"2026-06-08","erhu.he@samsung.com","23.0","24.0","1.0","23.0","0.0" +"2026-06-08","vivek.singh1@samsung.com","23.0","15.0","2.0","14.0","0.0" +"2026-06-08","deepesh.d@samsung.com","23.0","24.0","1.0","22.0","0.0" +"2026-06-08","d.toolan@samsung.com","22.0","21.0","1.0","20.0","0.0" +"2026-06-08","chirag.chauhan@samsung.com","22.0","21.0","2.0","20.0","0.0" +"2026-06-08","rajnish.k7@samsung.com","21.0","18.0","2.0","16.0","0.0" +"2026-06-08","m.rachuba@samsung.com","21.0","16.0","1.0","15.0","0.0" +"2026-06-08","r.kalafate@samsung.com","21.0","21.0","2.0","19.0","0.0" +"2026-06-08","j.morrill@samsung.com","21.0","22.0","2.0","20.0","0.0" +"2026-06-08","s.dhanka@samsung.com","21.0","12.0","2.0","10.0","0.0" +"2026-06-08","akshat.8797@samsung.com","20.0","12.0","1.0","10.0","0.0" +"2026-06-08","elena.piech@samsung.com","20.0","7.0","4.0","2.0","0.0" +"2026-06-08","d4.mishra@samsung.com","20.0","26.0","0.0","25.0","0.0" +"2026-06-08","aabhas.b@samsung.com","20.0","20.0","0.0","19.0","0.0" +"2026-06-08","meet.gogri@samsung.com","20.0","22.0","3.0","19.0","0.0" +"2026-06-08","sumeet.a@samsung.com","19.0","21.0","1.0","19.0","0.0" +"2026-06-08","hosung.you@samsung.com","19.0","26.0","2.0","24.0","0.0" +"2026-06-08","hyungi.seong@samsung.com","19.0","16.0","3.0","12.0","0.0" +"2026-06-08","a.chaudhary@samsung.com","18.0","17.0","1.0","16.0","0.0" +"2026-06-08","thomas.h@samsung.com","18.0","25.0","2.0","23.0","0.0" +"2026-06-08","r.nadimpalli@partner.samsung.com","18.0","18.0","1.0","17.0","0.0" +"2026-06-08","manojit.roy@samsung.com","17.0","9.0","2.0","7.0","0.0" +"2026-06-08","l.spahn@samsung.com","17.0","10.0","1.0","9.0","0.0" +"2026-06-08","s.chaitanya@samsung.com","17.0","13.0","1.0","10.0","0.0" +"2026-06-08","vk.deshpande@samsung.com","17.0","10.0","2.0","8.0","0.0" +"2026-06-08","shiv.mehla@samsung.com","16.0","17.0","1.0","16.0","0.0" +"2026-06-08","praveen.kmr@samsung.com","16.0","30.0","2.0","28.0","0.0" +"2026-06-08","t.zarod@samsung.com","16.0","8.0","1.0","6.0","0.0" +"2026-06-08","m.wolny@partner.samsung.com","16.0","6.0","2.0","4.0","0.0" +"2026-06-08","a.shakya@samsung.com","16.0","11.0","0.0","11.0","0.0" +"2026-06-08","s.nakka@samsung.com","16.0","21.0","1.0","19.0","0.0" +"2026-06-08","a9.suarez@partner.samsung.com","16.0","5.0","3.0","2.0","0.0" +"2026-06-08","anshu.makkar@samsung.com","16.0","14.0","0.0","14.0","0.0" +"2026-06-08","rakshith.r@samsung.com","16.0","14.0","0.0","14.0","0.0" +"2026-06-08","kusha.pande@samsung.com","16.0","10.0","1.0","6.0","0.0" +"2026-06-08","misha.jawa@samsung.com","16.0","15.0","1.0","14.0","0.0" +"2026-06-08","s.sirigiredd@samsung.com","16.0","14.0","1.0","13.0","0.0" +"2026-06-08","k.bugala@samsung.com","15.0","6.0","2.0","4.0","0.0" +"2026-06-08","swati.s8@samsung.com","14.0","12.0","2.0","10.0","0.0" +"2026-06-08","a.sekowski@samsung.com","14.0","29.0","1.0","28.0","0.0" +"2026-06-08","bohdan.v@samsung.com","14.0","21.0","2.0","19.0","0.0" +"2026-06-08","ajay.guna@samsung.com","14.0","31.0","1.0","30.0","0.0" +"2026-06-08","khandekar.s@samsung.com","14.0","10.0","2.0","7.0","0.0" +"2026-06-08","cem.yilmaz@samsung.com","14.0","4.0","2.0","1.0","0.0" +"2026-06-08","y3.jeong@samsung.com","14.0","18.0","0.0","18.0","0.0" +"2026-06-08","bo1.sun@samsung.com","14.0","5.0","2.0","3.0","0.0" +"2026-06-08","eunice4.kim@samsung.com","14.0","4.0","3.0","0.0","0.0" +"2026-06-08","dongd1.xu@samsung.com","14.0","3.0","1.0","3.0","0.0" +"2026-06-08","r.ramaraju@partner.samsung.com","14.0","10.0","2.0","8.0","0.0" +"2026-06-08","vish.jeet@samsung.com","13.0","19.0","2.0","17.0","0.0" +"2026-06-08","yahan.zhang@samsung.com","13.0","13.0","1.0","12.0","0.0" +"2026-06-08","w.lewandowsk@partner.samsung.com","13.0","14.0","2.0","12.0","0.0" +"2026-06-08","h.sonehara@samsung.com","13.0","4.0","3.0","1.0","0.0" +"2026-06-08","tom.sella@samsung.com","13.0","8.0","0.0","7.0","0.0" +"2026-06-08","vivek.das@samsung.com","12.0","5.0","1.0","5.0","0.0" +"2026-06-08","johnson.l@samsung.com","12.0","6.0","1.0","4.0","0.0" +"2026-06-08","chetan.d@samsung.com","12.0","6.0","1.0","5.0","0.0" +"2026-06-08","david.gaffin@samsung.com","12.0","4.0","3.0","1.0","0.0" +"2026-06-08","george1.kim@samsung.com","12.0","2.0","2.0","0.0","0.0" +"2026-06-08","vignesh.b@samsung.com","12.0","7.0","2.0","4.0","0.0" +"2026-06-08","s.zehnder@samsung.com","12.0","6.0","0.0","5.0","0.0" +"2026-06-08","thakur.v@samsung.com","11.0","13.0","1.0","12.0","0.0" +"2026-06-08","r.chittimall@partner.samsung.com","11.0","9.0","1.0","8.0","0.0" +"2026-06-08","a.tolambiya@samsung.com","11.0","17.0","1.0","15.0","0.0" +"2026-06-08","tarun.sanjay@samsung.com","11.0","5.0","1.0","3.0","0.0" +"2026-06-08","sameer.dash@samsung.com","11.0","11.0","1.0","11.0","0.0" +"2026-06-08","sehwan.noh@samsung.com","11.0","12.0","0.0","11.0","0.0" +"2026-06-08","shujie.fan@samsung.com","11.0","9.0","1.0","9.0","0.0" +"2026-06-08","vibhav.3@samsung.com","11.0","14.0","2.0","13.0","0.0" +"2026-06-08","a.efremchev@samsung.com","10.0","9.0","1.0","9.0","0.0" +"2026-06-08","n.srinidhi@samsung.com","10.0","20.0","1.0","19.0","0.0" +"2026-06-08","nishchay.k@samsung.com","10.0","11.0","1.0","11.0","0.0" +"2026-06-08","lei1.liu@samsung.com","10.0","8.0","1.0","8.0","0.0" +"2026-06-08","k.timoszenko@samsung.com","10.0","6.0","1.0","5.0","0.0" +"2026-06-08","b.brodecki@partner.samsung.com","10.0","16.0","1.0","15.0","0.0" +"2026-06-08","m.khomenkov@samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-08","jui.shah@samsung.com","10.0","8.0","1.0","8.0","0.0" +"2026-06-08","kr1.ayush@samsung.com","10.0","3.0","0.0","0.0","0.0" +"2026-06-08","anshika.s@samsung.com","10.0","4.0","1.0","3.0","0.0" +"2026-06-08","edwardj.kim@partner.samsung.com","10.0","2.0","1.0","1.0","0.0" +"2026-06-08","hilam.p@samsung.com","10.0","9.0","1.0","7.0","0.0" +"2026-06-08","a.choudhury@samsung.com","10.0","10.0","1.0","8.0","0.0" +"2026-06-08","p.jaromin@partner.samsung.com","10.0","20.0","1.0","19.0","0.0" +"2026-06-08","r.jiang@samsung.com","9.0","6.0","2.0","5.0","0.0" +"2026-06-08","m.lewandows5@samsung.com","9.0","7.0","1.0","6.0","0.0" +"2026-06-08","vinay.reddy1@partner.samsung.com","9.0","6.0","1.0","4.0","0.0" +"2026-06-08","e.martis@samsung.com","9.0","3.0","1.0","2.0","0.0" +"2026-06-08","p.marciniak2@samsung.com","9.0","9.0","1.0","7.0","0.0" +"2026-06-08","vikas.magar@samsung.com","9.0","15.0","1.0","14.0","0.0" +"2026-06-08","sreejit.r@samsung.com","9.0","12.0","1.0","10.0","0.0" +"2026-06-08","r.espinosa@samsung.com","9.0","4.0","2.0","2.0","0.0" +"2026-06-08","h.evanoff@partner.samsung.com","9.0","8.0","0.0","8.0","0.0" +"2026-06-08","akash.b2@samsung.com","9.0","11.0","0.0","10.0","0.0" +"2026-06-08","kushaldeep.s@samsung.com","9.0","14.0","1.0","13.0","0.0" +"2026-06-08","d.kargapolov@samsung.com","9.0","8.0","1.0","7.0","0.0" +"2026-06-08","michena.metla@samsung.com","9.0","5.0","2.0","3.0","0.0" +"2026-06-08","a.gromova@samsung.com","9.0","8.0","2.0","6.0","0.0" +"2026-06-08","k3.song@samsung.com","9.0","6.0","2.0","4.0","0.0" +"2026-06-08","r.ghewari@samsung.com","9.0","6.0","1.0","5.0","0.0" +"2026-06-08","navneeth.n@partner.samsung.com","9.0","14.0","1.0","13.0","0.0" +"2026-06-08","upadhyaya.r@samsung.com","9.0","2.0","1.0","1.0","0.0" +"2026-06-08","k.dakarapu@samsung.com","9.0","12.0","1.0","10.0","0.0" +"2026-06-08","agam.jain@samsung.com","9.0","7.0","0.0","6.0","0.0" +"2026-06-08","rah.agrawal@samsung.com","9.0","9.0","1.0","7.0","0.0" +"2026-06-08","srikanth.h@samsung.com","9.0","7.0","1.0","6.0","0.0" +"2026-06-08","p.stefanski@samsung.com","8.0","7.0","0.0","7.0","0.0" +"2026-06-08","b.chau@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-08","j3.martin@samsung.com","8.0","13.0","1.0","12.0","0.0" +"2026-06-08","domain.kim@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-08","l.bienkowski@partner.samsung.com","8.0","12.0","0.0","12.0","0.0" +"2026-06-08","m.wiacek2@samsung.com","8.0","11.0","1.0","11.0","0.0" +"2026-06-08","k.fraczek@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-08","nitish.yadav@samsung.com","8.0","3.0","1.0","3.0","0.0" +"2026-06-08","t.abhinav@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-08","r.paragbhai@samsung.com","8.0","7.0","1.0","6.0","0.0" +"2026-06-08","sumit.m2@samsung.com","8.0","5.0","0.0","4.0","0.0" +"2026-06-08","weidong5.w@samsung.com","8.0","8.0","0.0","8.0","0.0" +"2026-06-08","ramprasad.d@samsung.com","7.0","3.0","1.0","3.0","0.0" +"2026-06-08","joyston.f@samsung.com","7.0","7.0","1.0","6.0","0.0" +"2026-06-08","p.borhade@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-08","g.nicolae@samsung.com","7.0","14.0","1.0","13.0","0.0" +"2026-06-08","sindhu.d@partner.samsung.com","7.0","4.0","1.0","4.0","0.0" +"2026-06-08","samriddh.g@samsung.com","7.0","8.0","1.0","7.0","0.0" +"2026-06-08","k.beatty@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-08","ritesh.k@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-08","j.shih@samsung.com","7.0","6.0","1.0","6.0","0.0" +"2026-06-08","rfp-automation-bedrock-role-j0bj2i","7.0","1.0","0.0","0.0","0.0" +"2026-06-08","p.olwert@samsung.com","7.0","1.0","0.0","0.0","0.0" +"2026-06-08","rishabh.m@samsung.com","7.0","9.0","0.0","8.0","0.0" +"2026-06-08","pk.shukla@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-08","cost-agent-dev-bedrock-role-bs48gl","7.0","1.0","0.0","0.0","0.0" +"2026-06-08","j.aniol@samsung.com","7.0","2.0","1.0","1.0","0.0" +"2026-06-08","m.hyman@samsung.com","7.0","6.0","1.0","4.0","0.0" +"2026-06-08","arpit.nigam@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-08","rahul.p02@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-08","r4.gupta@samsung.com","6.0","11.0","1.0","10.0","0.0" +"2026-06-08","emily.chen@samsung.com","6.0","8.0","1.0","7.0","0.0" +"2026-06-08","vikas.km@samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-08","jl.wilkinson@samsung.com","6.0","1.0","1.0","0.0","0.0" +"2026-06-08","v.jangir@samsung.com","6.0","5.0","1.0","4.0","0.0" +"2026-06-08","j.siddharth@samsung.com","6.0","5.0","0.0","4.0","0.0" +"2026-06-08","chapman.c@samsung.com","6.0","9.0","0.0","8.0","0.0" +"2026-06-08","david.golan@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-08","subrat.kp@samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-08","l.xiong@samsung.com","6.0","6.0","0.0","5.0","0.0" +"2026-06-08","b.smietanka@samsung.com","6.0","4.0","0.0","4.0","0.0" +"2026-06-08","hyunwoo.s@samsung.com","6.0","3.0","1.0","3.0","0.0" +"2026-06-08","aman.barya@samsung.com","6.0","4.0","0.0","3.0","0.0" +"2026-06-08","g.nagori@samsung.com","5.0","1.0","0.0","1.0","0.0" +"2026-06-08","abhijit.sh5@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-08","l.steigerwal@samsung.com","5.0","5.0","0.0","5.0","0.0" +"2026-06-08","m.wasniewski@partner.samsung.com","5.0","2.0","0.0","1.0","0.0" +"2026-06-08","tianshu.wang@samsung.com","5.0","5.0","0.0","4.0","0.0" +"2026-06-08","c.gault@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-08","arul.a1@partner.samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-08","sang.jun@partner.samsung.com","5.0","2.0","1.0","0.0","0.0" +"2026-06-08","aman.goel@samsung.com","5.0","10.0","1.0","10.0","0.0" +"2026-06-08","k.urbanski@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-08","vincent.k@samsung.com","5.0","9.0","1.0","8.0","0.0" +"2026-06-08","c1.cabiati@samsung.com","5.0","5.0","1.0","4.0","0.0" +"2026-06-08","c.lewczyk@samsung.com","5.0","3.0","0.0","2.0","0.0" +"2026-06-08","george.f@samsung.com","5.0","6.0","0.0","6.0","0.0" +"2026-06-08","d.skliarov@samsung.com","5.0","10.0","1.0","9.0","0.0" +"2026-06-08","onur.y@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-08","pashupati.r1@samsung.com","5.0","6.0","0.0","6.0","0.0" +"2026-06-08","y.wyszynski@samsung.com","5.0","7.0","0.0","6.0","0.0" +"2026-06-08","rajeev.r@samsung.com","5.0","7.0","1.0","6.0","0.0" +"2026-06-08","m.wojtas@partner.samsung.com","5.0","6.0","1.0","5.0","0.0" +"2026-06-08","ke.jin1@samsung.com","5.0","6.0","1.0","5.0","0.0" +"2026-06-08","monika.sroha@samsung.com","4.0","3.0","0.0","3.0","0.0" +"2026-06-08","v.rakshan@samsung.com","4.0","3.0","1.0","2.0","0.0" +"2026-06-08","gaurav.garg1@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-08","li.li1@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-08","mahesh.rk@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-08","priyanshu.j@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-08","mr.dareddy@samsung.com","4.0","4.0","0.0","3.0","0.0" +"2026-06-08","sudarshan.p@samsung.com","4.0","4.0","1.0","4.0","0.0" +"2026-06-08","m.nowacki3@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-08","c.resnick@samsung.com","4.0","9.0","0.0","9.0","0.0" +"2026-06-08","anuj.bhatt@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-08","rohit.garg1@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-08","muthu.v1@partner.samsung.com","4.0","2.0","1.0","2.0","0.0" +"2026-06-08","anthonyj.5@samsung.com","4.0","6.0","0.0","6.0","0.0" +"2026-06-08","a.trivedy@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-08","g.deochakke@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-08","mohsen.an@samsung.com","4.0","7.0","0.0","7.0","0.0" +"2026-06-08","csup.hwang@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-08","b.zwolinski@samsung.com","4.0","4.0","1.0","3.0","0.0" +"2026-06-08","xinhui.shen@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-08","e.ronquillo@partner.samsung.com","4.0","1.0","1.0","1.0","0.0" +"2026-06-08","d.barouski@samsung.com","4.0","6.0","1.0","5.0","0.0" +"2026-06-08","a.krasnikau@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-08","v.saucedo@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-08","z.stolarski@samsung.com","3.0","6.0","0.0","5.0","0.0" +"2026-06-08","m.dabrowski3@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-08","g.dagley@partner.samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-08","a.conner@samsung.com","3.0","7.0","0.0","7.0","0.0" +"2026-06-08","arun.a@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-08","a.odonnell@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-08","paul.k@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-08","m1.agarwal@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-08","s.kolakowski@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-08","sundar.siv@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-08","r.ghewari2@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-08","s.bodhe@partner.samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-08","a.dhanens@samsung.com","3.0","2.0","1.0","2.0","0.0" +"2026-06-08","kr.saurabh09@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-08","dasari.l@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-08","ashley.koett@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-08","lisa.liang@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-08","a.pentela@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-08","k.pietrzyk2@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-08","kim.sj@samsung.com","3.0","1.0","1.0","0.0","0.0" +"2026-06-08","siddesh.a@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-08","mohan.lal@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-08","bellkar.r@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-08","y.shtarker@samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-08","m.boriya@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-08","a.andersson@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-08","a.movsesyan@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-08","isaiah.scott@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","m.ferreira@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","a.sharma2@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","aditya.r@partner.samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-08","r.monheit@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.kandikonda@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-08","sundar.b@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","c.banchek@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-08","s.dynak@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-08","daniel.ortiz@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-08","rashi.a@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","c4.song@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.kurcz@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","hari.shanker@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-08","k.zeng@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","vinay.gali@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-08","harsh.mt@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-08","r.malcomb@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","a3.paliwal@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-08","p.kwietniews@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","y.li10@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","f.tarlan@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","saurabh.mane@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","a.yildirim2@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","g.callahan@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","antriksh.p@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","a12.li@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","xiao.yang1@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","sachin.r@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","r2.deshpande@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","cmutha.ks@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","shabeer.p@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","a.richhariya@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","zhen.jin@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","r.parol@partner.samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-08","l.torgerson@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","m.brenner@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","n.colberg@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-08","udit.smvdu@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-08","tushar.bg@samsung.com","2.0","4.0","0.0","4.0","0.0" +"2026-06-08","shelley.wu@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-08","nikhil.c2@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-08","jared.katz@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-08","n.viswas@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","j.bermudo@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","dhruv.gaur@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","ritwik.c@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","shivraj.r@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","udayan.c@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","irvin.low@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","s.chiou@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","ss.kulkarni@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","s.duthoit@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","c.bergquist@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","r.stefanowic@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","rahul.jain1@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","s.danchenko@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.lepicki@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","mohnish.b1@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","j.gottlieb@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","a.puntambeka@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","ramanath.pai@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","g1.raju@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","l.kaminski2@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","sharmin.c@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","n.bangalore@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","gg.rojas@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","santhosh.s2@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","m.witkowski@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","a.orlov@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","amit.rodge@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","ankitha.r@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","anurag.p1@samsung.com","1.0","2.0","0.0","1.0","0.0" +"2026-06-08","devi.a@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","kiran.pk@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","j.polowitz@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","j.kulesza@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","mengya.wei@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","gongli.duan@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","a.ambalavana@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","bernard.feng@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","k.lauterbach@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","kotipalli.d@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","m.lamba@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-08","k.duch@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.vandewal@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","himesha.a@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","at.lakshmi@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","yating.w@partner.samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-08","b.baran@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","n.ajito@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","pi_audience_agentic_ai","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","sourav.kshp@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-08","gamya.k@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-08","gyeong.song@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","d.nejbauer@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","a.arche@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","r2.jain@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","saejun.cha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","himanshu.t@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","v.narayanan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","seohyoung.ha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","vidhan.vijay@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","4.0","0.0","0.0","0.0" +"2026-06-08","p1.singh@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","a.daveiga@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","k.srodon@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","justin.shin@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","apurvaa.vs@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","josh3.lee@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","y.rifai@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","a.manikandan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","varun.gupta1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","aman.c@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","arun.km@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","woongji.kim@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","ma.caballero@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","j.ouye@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","e.schweikert@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","s.kaul@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","wei.lu@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","sibi.menon@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","o3.martinez@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","k.pampati@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","konni.bunya@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","sunayana.j@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","narendra.p@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","linda.chu@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","a2.huang@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","puneet.saini@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","taehodev.lee@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","nk.saxena@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","d2.tran@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","engineering_ssp_dev","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","e.thomas@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","rakesh.kr2@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","k.teichman@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","j.lovejoy@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","hr.shinde@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.agosto@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","a.szyszka@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","anuja.d@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","hao2.gao@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","chung.jade@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","daniel.cox@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","c.dunne@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.pavlovski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","kushal.m1@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.amjad@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","k.gangadhara@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","g.murad@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","a.abbaraju@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","zeyang.ye@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","k.strojek@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","jiachuan.h@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","semantic_bot","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","joseph.rice@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","m.chomiak@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","j.dickerson@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","tarandeep.s3@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","t2.lee@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-08","pi-model-contextual-bedrock-role-qbwj07","0.0","231.0","0.0","0.0","84.0" +"2026-06-07","p.jarosz@partner.samsung.com","309.0","125065.0","12.0","300.0","15478.0" +"2026-06-07","j2.kumar@samsung.com","237.0","273.0","8.0","265.0","1.0" +"2026-06-07","princy.j@samsung.com","152.0","99.0","15.0","84.0","0.0" +"2026-06-07","vaibhav.t1@samsung.com","101.0","114.0","4.0","110.0","1.0" +"2026-06-07","tom.sella@samsung.com","98.0","100.0","0.0","96.0","0.0" +"2026-06-07","suresh.mgi@samsung.com","71.0","153.0","4.0","149.0","1.0" +"2026-06-07","u.saboji@samsung.com","53.0","45.0","4.0","41.0","0.0" +"2026-06-07","yj5.lee@partner.samsung.com","42.0","31.0","4.0","28.0","0.0" +"2026-06-07","anuj.ar@samsung.com","39.0","41.0","2.0","39.0","0.0" +"2026-06-07","yi5.han@samsung.com","37.0","13.0","4.0","8.0","0.0" +"2026-06-07","sunit.m@samsung.com","36.0","31.0","4.0","27.0","0.0" +"2026-06-07","y3.jeong@samsung.com","36.0","39.0","1.0","38.0","0.0" +"2026-06-07","huijun.wu@samsung.com","31.0","78.0","0.0","77.0","0.0" +"2026-06-07","n2.mehta@samsung.com","30.0","27.0","2.0","25.0","0.0" +"2026-06-07","erhu.he@samsung.com","28.0","18.0","1.0","15.0","0.0" +"2026-06-07","AI-Campaign-Manager-bedrock-role-e5m5hh","24.0","4.0","0.0","0.0","1.0" +"2026-06-07","eunice4.kim@samsung.com","22.0","9.0","5.0","5.0","0.0" +"2026-06-07","c.sharad@samsung.com","20.0","23.0","1.0","22.0","0.0" +"2026-06-07","s.kangal@samsung.com","18.0","14.0","1.0","13.0","0.0" +"2026-06-07","sameer.dash@samsung.com","17.0","16.0","1.0","15.0","0.0" +"2026-06-07","sapna.singh@samsung.com","15.0","37.0","1.0","36.0","0.0" +"2026-06-07","seokbaek.lee@samsung.com","12.0","9.0","0.0","8.0","0.0" +"2026-06-07","rakshith.r@samsung.com","11.0","5.0","0.0","4.0","0.0" +"2026-06-07","s.kolusu@samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-07","a.tolambiya@samsung.com","10.0","8.0","1.0","7.0","0.0" +"2026-06-07","sourav.dutta@samsung.com","9.0","11.0","1.0","10.0","0.0" +"2026-06-07","hilam.p@samsung.com","8.0","7.0","1.0","6.0","0.0" +"2026-06-07","j.kulesza@samsung.com","7.0","7.0","1.0","6.0","0.0" +"2026-06-07","r.nadimpalli@partner.samsung.com","6.0","5.0","0.0","5.0","0.0" +"2026-06-07","michena.metla@samsung.com","6.0","2.0","1.0","1.0","0.0" +"2026-06-07","praveen.kmr@samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-07","shivraj.r@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-07","kiran.pk@partner.samsung.com","5.0","2.0","1.0","2.0","0.0" +"2026-06-07","r.paragbhai@samsung.com","5.0","5.0","0.0","5.0","0.0" +"2026-06-07","a.puntambeka@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-07","rishabh.m@samsung.com","4.0","5.0","1.0","4.0","0.0" +"2026-06-07","s4.jeon@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-07","chi-chen.lo@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-07","vincent.k@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-07","subrat.kp@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-07","sindhu.d@partner.samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-07","gg.rojas@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-07","himesha.a@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-07","claude-code-gha-role","2.0","3.0","0.0","3.0","0.0" +"2026-06-07","r.ghewari@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-07","jared.katz@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-07","n.bertha@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-07","kookheon.kim@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-07","r.ghewari2@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-07","saurabh.mane@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-07","d.hu10@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-07","d.skliarov@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-07","g1.raju@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-07","amit.rodge@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-07","engineering_ssp_dev","0.0","0.0","0.0","0.0","0.0" +"2026-06-07","woongji.kim@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-07","pi_audience_agentic_ai","0.0","0.0","0.0","0.0","0.0" +"2026-06-07","li.li1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","tom.sella@samsung.com","231.0","802.0","0.0","184.0","1.0" +"2026-06-06","p.jarosz@partner.samsung.com","126.0","6481.0","2.0","34.0","41252.0" +"2026-06-06","n.singhal@samsung.com","85.0","97.0","5.0","92.0","0.0" +"2026-06-06","c.westlund@partner.samsung.com","54.0","64.0","3.0","61.0","0.0" +"2026-06-06","huijun.wu@samsung.com","54.0","111.0","0.0","109.0","0.0" +"2026-06-06","j2.kumar@samsung.com","52.0","29.0","5.0","24.0","0.0" +"2026-06-06","udayan.c@samsung.com","51.0","47.0","3.0","43.0","0.0" +"2026-06-06","eunice4.kim@samsung.com","49.0","19.0","10.0","8.0","0.0" +"2026-06-06","suresh.mgi@samsung.com","42.0","82.0","3.0","79.0","0.0" +"2026-06-06","n2.mehta@samsung.com","42.0","47.0","2.0","45.0","0.0" +"2026-06-06","sindhu.d@partner.samsung.com","39.0","20.0","4.0","16.0","0.0" +"2026-06-06","subrat.kp@samsung.com","36.0","48.0","2.0","46.0","0.0" +"2026-06-06","a.tolambiya@samsung.com","32.0","27.0","2.0","24.0","0.0" +"2026-06-06","j.sarallo@samsung.com","32.0","51.0","1.0","51.0","0.0" +"2026-06-06","AI-Campaign-Manager-bedrock-role-e5m5hh","30.0","2.0","0.0","0.0","1.0" +"2026-06-06","u.saboji@samsung.com","30.0","34.0","2.0","32.0","0.0" +"2026-06-06","lam.n@samsung.com","28.0","37.0","1.0","36.0","0.0" +"2026-06-06","sunit.m@samsung.com","25.0","40.0","3.0","37.0","0.0" +"2026-06-06","anshu.makkar@samsung.com","25.0","25.0","0.0","24.0","0.0" +"2026-06-06","anuj.ar@samsung.com","12.0","15.0","0.0","15.0","0.0" +"2026-06-06","j.asofsky@partner.samsung.com","11.0","7.0","1.0","6.0","0.0" +"2026-06-06","yi5.han@samsung.com","11.0","8.0","1.0","7.0","0.0" +"2026-06-06","pk.shukla@samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-06","m.barczyk@samsung.com","10.0","6.0","1.0","6.0","0.0" +"2026-06-06","yj5.lee@partner.samsung.com","8.0","4.0","1.0","4.0","0.0" +"2026-06-06","v.gifford@samsung.com","8.0","10.0","1.0","9.0","0.0" +"2026-06-06","chi-chen.lo@samsung.com","8.0","3.0","1.0","2.0","0.0" +"2026-06-06","erhu.he@samsung.com","7.0","5.0","0.0","5.0","0.0" +"2026-06-06","lee.irene@partner.samsung.com","7.0","2.0","0.0","0.0","0.0" +"2026-06-06","brian.song@samsung.com","7.0","8.0","0.0","8.0","0.0" +"2026-06-06","joyston.f@samsung.com","5.0","6.0","0.0","5.0","0.0" +"2026-06-06","v.gandhi@samsung.com","5.0","4.0","0.0","4.0","0.0" +"2026-06-06","singh.satyam@samsung.com","5.0","4.0","0.0","4.0","0.0" +"2026-06-06","pi-model-contextual-bedrock-role-qbwj07","4.0","3111.0","0.0","0.0","546.0" +"2026-06-06","r.ghewari@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-06","rakshith.r@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-06","y.li10@samsung.com","4.0","3.0","0.0","3.0","0.0" +"2026-06-06","l.xiong@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-06","a.devappa@partner.samsung.com","3.0","4.0","0.0","3.0","0.0" +"2026-06-06","hilam.p@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-06","michena.metla@samsung.com","3.0","1.0","1.0","0.0","0.0" +"2026-06-06","narendra.p@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-06","dongd1.xu@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-06","m1.agarwal@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-06","hyunwoo.s@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-06","chapman.c@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-06","e.strong@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-06","s.kolusu@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-06","bernard.feng@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-06","li.li1@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-06","antik.a@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-06","johnson.l@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-06","d.kargapolov@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","naveenk1.v@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-06","dajung.yoo@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-06","h.sonehara@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","rfp-automation-bedrock-role-j0bj2i","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","c.resnick@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","lei1.liu@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","i.andriychuk@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-06","mr.dareddy@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-06","vincent.k@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-06","sai.mamidi@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-06","gg.rojas@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-06","santhosh.s2@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-06","claude-code-gha-role","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","y2.yan@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-06","vivek.singh1@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-06","onur.y@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","paul.k@samsung.com","1.0","2.0","0.0","1.0","0.0" +"2026-06-06","praveen.kmr@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-06","vibhav.3@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-06","saurabh.mane@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","j.gottlieb@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","kushaldeep.s@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","r.ghewari2@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","a.sekowski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","j.dickerson@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","daniel.cox@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","wei.lu@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-06","hyungi.seong@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-06","engineering_ssp_dev","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","p.jarosz@partner.samsung.com","285.0","183.0","15.0","164.0","4.0" +"2026-06-05","inhyuk.jung@samsung.com","277.0","170.0","19.0","151.0","2.0" +"2026-06-05","gupta.s@samsung.com","158.0","225.0","11.0","214.0","3.0" +"2026-06-05","c.borins@samsung.com","152.0","143.0","10.0","133.0","0.0" +"2026-06-05","g.brancovici@samsung.com","152.0","208.0","0.0","204.0","0.0" +"2026-06-05","yi5.han@samsung.com","145.0","85.0","11.0","68.0","0.0" +"2026-06-05","vaibhav.t1@samsung.com","128.0","115.0","8.0","105.0","1.0" +"2026-06-05","c.westlund@partner.samsung.com","120.0","118.0","8.0","110.0","0.0" +"2026-06-05","huijun.wu@samsung.com","116.0","280.0","0.0","278.0","1.0" +"2026-06-05","harsh.c@samsung.com","113.0","120.0","7.0","112.0","0.0" +"2026-06-05","b.harr@samsung.com","113.0","82.0","0.0","75.0","0.0" +"2026-06-05","j.muppala@samsung.com","106.0","96.0","8.0","88.0","0.0" +"2026-06-05","j.sarallo@samsung.com","92.0","79.0","8.0","72.0","0.0" +"2026-06-05","wh71.kim@samsung.com","91.0","70.0","8.0","62.0","0.0" +"2026-06-05","litellm","88.0","33.0","4.0","21.0","0.0" +"2026-06-05","patrick.ting@samsung.com","84.0","100.0","5.0","95.0","0.0" +"2026-06-05","sourav.dutta@samsung.com","82.0","93.0","5.0","88.0","0.0" +"2026-06-05","v.gandhi@samsung.com","76.0","85.0","4.0","81.0","0.0" +"2026-06-05","w1.freeman@samsung.com","74.0","84.0","0.0","82.0","0.0" +"2026-06-05","u.saboji@samsung.com","74.0","85.0","4.0","81.0","0.0" +"2026-06-05","r.vishnumurt@samsung.com","73.0","83.0","4.0","79.0","0.0" +"2026-06-05","mohnish.b1@samsung.com","69.0","12.0","0.0","0.0","0.0" +"2026-06-05","pagalavan.s1@samsung.com","68.0","55.0","5.0","50.0","0.0" +"2026-06-05","s.sequeira@samsung.com","67.0","155.0","4.0","151.0","0.0" +"2026-06-05","tom.sella@samsung.com","66.0","55.0","0.0","51.0","0.0" +"2026-06-05","sudipta.p@samsung.com","66.0","59.0","5.0","54.0","0.0" +"2026-06-05","m.barczyk@samsung.com","65.0","60.0","4.0","56.0","0.0" +"2026-06-05","claude-code-gha-role","63.0","73.0","7.0","64.0","0.0" +"2026-06-05","cmutha.ks@samsung.com","63.0","57.0","5.0","53.0","0.0" +"2026-06-05","chi-chen.lo@samsung.com","63.0","64.0","5.0","59.0","0.0" +"2026-06-05","anuj.ar@samsung.com","60.0","58.0","4.0","54.0","0.0" +"2026-06-05","shuyang.y@samsung.com","55.0","22.0","7.0","16.0","0.0" +"2026-06-05","erhu.he@samsung.com","54.0","58.0","2.0","55.0","0.0" +"2026-06-05","n.rosendall@samsung.com","53.0","54.0","3.0","50.0","0.0" +"2026-06-05","somsekhar.d1@samsung.com","53.0","91.0","3.0","87.0","0.0" +"2026-06-05","m.abhinav@samsung.com","52.0","70.0","2.0","67.0","0.0" +"2026-06-05","varun.rawat@samsung.com","51.0","77.0","6.0","71.0","0.0" +"2026-06-05","john.iburg@samsung.com","49.0","42.0","2.0","38.0","0.0" +"2026-06-05","brian.song@samsung.com","49.0","42.0","3.0","38.0","0.0" +"2026-06-05","zh.kim@samsung.com","48.0","61.0","0.0","60.0","0.0" +"2026-06-05","sunit.m@samsung.com","47.0","66.0","4.0","61.0","0.0" +"2026-06-05","j.asofsky@partner.samsung.com","47.0","22.0","5.0","17.0","0.0" +"2026-06-05","amit.wagh@samsung.com","46.0","61.0","6.0","55.0","0.0" +"2026-06-05","r.rittenhous@samsung.com","46.0","87.0","4.0","83.0","0.0" +"2026-06-05","udayan.c@samsung.com","46.0","32.0","2.0","30.0","1.0" +"2026-06-05","d.gorbenko@samsung.com","45.0","56.0","2.0","54.0","0.0" +"2026-06-05","a.devappa@partner.samsung.com","45.0","36.0","4.0","32.0","0.0" +"2026-06-05","suresh.mgi@samsung.com","44.0","84.0","4.0","80.0","0.0" +"2026-06-05","j.morrill@samsung.com","43.0","45.0","4.0","41.0","0.0" +"2026-06-05","saransh.p@samsung.com","43.0","30.0","4.0","26.0","0.0" +"2026-06-05","aravind.s5@samsung.com","42.0","85.0","3.0","81.0","0.0" +"2026-06-05","haoran.lu@samsung.com","42.0","22.0","2.0","17.0","0.0" +"2026-06-05","lam.n@samsung.com","40.0","52.0","2.0","50.0","0.0" +"2026-06-05","n.singhal@samsung.com","40.0","33.0","3.0","30.0","0.0" +"2026-06-05","j3.martin@samsung.com","37.0","76.0","3.0","73.0","0.0" +"2026-06-05","pi-model-contextual-bedrock-role-qbwj07","35.0","20936.0","0.0","0.0","7084.0" +"2026-06-05","subrat.kp@samsung.com","34.0","37.0","2.0","35.0","0.0" +"2026-06-05","anshika.s@samsung.com","34.0","18.0","4.0","14.0","0.0" +"2026-06-05","johnson.l@samsung.com","34.0","29.0","3.0","26.0","0.0" +"2026-06-05","a.pentela@samsung.com","32.0","52.0","3.0","48.0","0.0" +"2026-06-05","ramprasad.d@samsung.com","32.0","32.0","3.0","28.0","0.0" +"2026-06-05","shiv.mehla@samsung.com","32.0","37.0","2.0","35.0","0.0" +"2026-06-05","s4.jeon@samsung.com","31.0","35.0","1.0","33.0","0.0" +"2026-06-05","s.dhanka@samsung.com","31.0","15.0","4.0","11.0","0.0" +"2026-06-05","j2.kumar@samsung.com","30.0","19.0","3.0","17.0","0.0" +"2026-06-05","sumeet.a@samsung.com","29.0","27.0","2.0","25.0","0.0" +"2026-06-05","n2.mehta@samsung.com","28.0","18.0","3.0","15.0","0.0" +"2026-06-05","k.dakarapu@samsung.com","28.0","43.0","4.0","39.0","0.0" +"2026-06-05","hyungi.seong@samsung.com","28.0","16.0","3.0","13.0","0.0" +"2026-06-05","r.nadimpalli@partner.samsung.com","27.0","31.0","1.0","30.0","0.0" +"2026-06-05","hayeon.song@samsung.com","27.0","32.0","2.0","30.0","0.0" +"2026-06-05","s.kangal@samsung.com","27.0","29.0","2.0","27.0","0.0" +"2026-06-05","s.aleynikov@samsung.com","27.0","64.0","1.0","62.0","0.0" +"2026-06-05","davidj.jung@samsung.com","26.0","8.0","3.0","5.0","0.0" +"2026-06-05","n.bertha@samsung.com","26.0","11.0","6.0","5.0","0.0" +"2026-06-05","abhishek.pd@samsung.com","26.0","39.0","2.0","37.0","0.0" +"2026-06-05","lei1.liu@samsung.com","25.0","17.0","2.0","15.0","0.0" +"2026-06-05","aabhas.b@samsung.com","25.0","42.0","0.0","40.0","0.0" +"2026-06-05","c.lewczyk@samsung.com","24.0","6.0","3.0","3.0","0.0" +"2026-06-05","aman.barya@samsung.com","24.0","19.0","0.0","18.0","0.0" +"2026-06-05","hyunwoo.s@samsung.com","24.0","16.0","2.0","14.0","0.0" +"2026-06-05","y2.yan@samsung.com","24.0","11.0","2.0","8.0","0.0" +"2026-06-05","d4.mishra@samsung.com","24.0","14.0","0.0","12.0","0.0" +"2026-06-05","a.sekowski@samsung.com","23.0","33.0","2.0","31.0","0.0" +"2026-06-05","seokbaek.lee@samsung.com","22.0","29.0","0.0","27.0","0.0" +"2026-06-05","r.parol@partner.samsung.com","21.0","25.0","2.0","23.0","0.0" +"2026-06-05","j.siddharth@samsung.com","21.0","20.0","0.0","19.0","0.0" +"2026-06-05","pi_audience_agentic_ai","21.0","7.0","0.0","5.0","1.0" +"2026-06-05","nitish.yadav@samsung.com","21.0","10.0","2.0","7.0","0.0" +"2026-06-05","siddesh.a@samsung.com","21.0","8.0","1.0","6.0","0.0" +"2026-06-05","bellkar.r@samsung.com","20.0","36.0","2.0","34.0","0.0" +"2026-06-05","sourav.kshp@samsung.com","19.0","8.0","2.0","6.0","0.0" +"2026-06-05","tushar.bg@samsung.com","19.0","16.0","0.0","15.0","0.0" +"2026-06-05","joyston.f@samsung.com","18.0","12.0","2.0","10.0","0.0" +"2026-06-05","vivek.das@samsung.com","18.0","19.0","1.0","17.0","0.0" +"2026-06-05","sehwan.noh@samsung.com","18.0","22.0","0.0","22.0","0.0" +"2026-06-05","b.woodcock@partner.samsung.com","18.0","25.0","0.0","23.0","0.0" +"2026-06-05","anusha.holla@samsung.com","17.0","9.0","2.0","7.0","0.0" +"2026-06-05","jui.shah@samsung.com","17.0","14.0","1.0","13.0","0.0" +"2026-06-05","pooja.k@samsung.com","17.0","9.0","2.0","7.0","0.0" +"2026-06-05","kotipalli.d@samsung.com","17.0","7.0","0.0","6.0","1.0" +"2026-06-05","bernard.feng@samsung.com","17.0","5.0","0.0","4.0","0.0" +"2026-06-05","meet.gogri@samsung.com","16.0","15.0","3.0","12.0","0.0" +"2026-06-05","thomas.h@samsung.com","16.0","16.0","2.0","13.0","0.0" +"2026-06-05","chirag.chauhan@samsung.com","16.0","15.0","1.0","13.0","0.0" +"2026-06-05","vinay.gali@samsung.com","16.0","23.0","2.0","21.0","0.0" +"2026-06-05","akshat.8797@samsung.com","16.0","7.0","1.0","5.0","0.0" +"2026-06-05","sarat.c@partner.samsung.com","16.0","17.0","1.0","16.0","0.0" +"2026-06-05","ke.jin1@samsung.com","16.0","34.0","1.0","32.0","0.0" +"2026-06-05","singh.satyam@samsung.com","16.0","6.0","2.0","4.0","0.0" +"2026-06-05","satya.n@samsung.com","15.0","9.0","1.0","8.0","0.0" +"2026-06-05","g.nicolae@samsung.com","15.0","32.0","1.0","31.0","0.0" +"2026-06-05","harikrish.p@samsung.com","15.0","15.0","1.0","15.0","0.0" +"2026-06-05","vincent.k@samsung.com","15.0","34.0","1.0","33.0","0.0" +"2026-06-05","s.kolusu@samsung.com","15.0","6.0","1.0","4.0","0.0" +"2026-06-05","md.liyakat@samsung.com","15.0","13.0","2.0","12.0","0.0" +"2026-06-05","s.nakka@samsung.com","14.0","20.0","1.0","19.0","0.0" +"2026-06-05","pavel.p@partner.samsung.com","14.0","20.0","0.0","20.0","0.0" +"2026-06-05","princy.j@samsung.com","14.0","11.0","2.0","9.0","0.0" +"2026-06-05","a.richhariya@samsung.com","14.0","9.0","1.0","7.0","0.0" +"2026-06-05","abhishek.km3@samsung.com","14.0","10.0","1.0","8.0","0.0" +"2026-06-05","l.steigerwal@samsung.com","14.0","5.0","3.0","2.0","0.0" +"2026-06-05","a.choudhury@samsung.com","14.0","13.0","1.0","11.0","0.0" +"2026-06-05","sani.v@samsung.com","14.0","12.0","1.0","12.0","0.0" +"2026-06-05","v.rakshan@samsung.com","13.0","14.0","2.0","13.0","0.0" +"2026-06-05","ritesh.k@samsung.com","13.0","15.0","1.0","14.0","0.0" +"2026-06-05","gaurav.garg1@samsung.com","13.0","7.0","1.0","5.0","0.0" +"2026-06-05","agam.jain@samsung.com","13.0","9.0","0.0","8.0","0.0" +"2026-06-05","t.abhinav@samsung.com","13.0","9.0","1.0","8.0","0.0" +"2026-06-05","li.li1@samsung.com","13.0","10.0","2.0","8.0","0.0" +"2026-06-05","somonnoy.c@samsung.com","13.0","15.0","1.0","14.0","0.0" +"2026-06-05","k.beatty@samsung.com","12.0","4.0","3.0","2.0","0.0" +"2026-06-05","seohyoung.ha@samsung.com","12.0","6.0","2.0","3.0","0.0" +"2026-06-05","ashwin.v@samsung.com","12.0","6.0","1.0","4.0","0.0" +"2026-06-05","shujie.fan@samsung.com","12.0","11.0","1.0","11.0","0.0" +"2026-06-05","nishchay.k@samsung.com","12.0","12.0","1.0","11.0","0.0" +"2026-06-05","vibhav.3@samsung.com","12.0","11.0","2.0","9.0","0.0" +"2026-06-05","pk.shukla@samsung.com","12.0","20.0","1.0","19.0","0.0" +"2026-06-05","eunice4.kim@samsung.com","12.0","7.0","2.0","4.0","0.0" +"2026-06-05","anuj.bhatt@samsung.com","11.0","8.0","1.0","5.0","0.0" +"2026-06-05","bohdan.v@samsung.com","11.0","22.0","1.0","21.0","0.0" +"2026-06-05","yoshitha.p@samsung.com","11.0","3.0","1.0","2.0","0.0" +"2026-06-05","devesh.g@samsung.com","11.0","7.0","1.0","6.0","0.0" +"2026-06-05","b.gujjar@samsung.com","11.0","18.0","1.0","17.0","0.0" +"2026-06-05","hosung.you@samsung.com","11.0","14.0","1.0","13.0","0.0" +"2026-06-05","zehua.z@samsung.com","11.0","9.0","0.0","9.0","0.0" +"2026-06-05","b.chau@samsung.com","11.0","5.0","1.0","3.0","0.0" +"2026-06-05","c.resnick@samsung.com","10.0","12.0","2.0","11.0","0.0" +"2026-06-05","s.goyal@samsung.com","10.0","5.0","1.0","4.0","0.0" +"2026-06-05","r.ghewari2@samsung.com","10.0","6.0","1.0","5.0","0.0" +"2026-06-05","e.martis@samsung.com","10.0","4.0","1.0","3.0","0.0" +"2026-06-05","a.ambalavana@samsung.com","10.0","11.0","0.0","11.0","0.0" +"2026-06-05","kk.anil@samsung.com","10.0","11.0","0.0","11.0","0.0" +"2026-06-05","upadhyaya.r@samsung.com","10.0","5.0","1.0","4.0","0.0" +"2026-06-05","d.kargapolov@samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-05","r.ramaraju@partner.samsung.com","10.0","6.0","1.0","5.0","0.0" +"2026-06-05","r.chittimall@partner.samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-05","d.hu10@samsung.com","9.0","12.0","1.0","11.0","0.0" +"2026-06-05","saurabh.mane@partner.samsung.com","9.0","5.0","2.0","3.0","0.0" +"2026-06-05","v.jangir@samsung.com","9.0","8.0","1.0","7.0","0.0" +"2026-06-05","b.zwolinski@samsung.com","9.0","5.0","1.0","4.0","0.0" +"2026-06-05","kishore.r@samsung.com","9.0","4.0","1.0","3.0","0.0" +"2026-06-05","l.roderick@samsung.com","9.0","7.0","1.0","7.0","0.0" +"2026-06-05","jasmeen.p@samsung.com","9.0","369.0","0.0","3.0","0.0" +"2026-06-05","elena.piech@samsung.com","9.0","3.0","2.0","1.0","0.0" +"2026-06-05","mohit.sh3@samsung.com","9.0","13.0","1.0","11.0","0.0" +"2026-06-05","t.jain@samsung.com","9.0","15.0","1.0","14.0","0.0" +"2026-06-05","n.srinidhi@samsung.com","9.0","13.0","1.0","12.0","0.0" +"2026-06-05","s.chaitanya@samsung.com","9.0","16.0","1.0","15.0","0.0" +"2026-06-05","t1.park@samsung.com","9.0","2.0","1.0","1.0","0.0" +"2026-06-05","rakshith.r@samsung.com","9.0","9.0","0.0","9.0","0.0" +"2026-06-05","yj5.lee@partner.samsung.com","9.0","5.0","1.0","4.0","0.0" +"2026-06-05","h.evanoff@partner.samsung.com","9.0","11.0","1.0","10.0","0.0" +"2026-06-05","yt.chien@partner.samsung.com","8.0","12.0","1.0","11.0","0.0" +"2026-06-05","p.borhade@samsung.com","8.0","7.0","1.0","6.0","0.0" +"2026-06-05","sapna.singh@samsung.com","8.0","10.0","1.0","9.0","0.0" +"2026-06-05","m.khomenkov@samsung.com","8.0","12.0","1.0","11.0","0.0" +"2026-06-05","hilam.p@samsung.com","8.0","8.0","1.0","6.0","0.0" +"2026-06-05","a.puntambeka@samsung.com","8.0","5.0","1.0","4.0","0.0" +"2026-06-05","swetha.gb@samsung.com","8.0","3.0","1.0","2.0","0.0" +"2026-06-05","sugir.tharaj@samsung.com","8.0","15.0","1.0","14.0","0.0" +"2026-06-05","m.amjad@samsung.com","8.0","9.0","1.0","9.0","0.0" +"2026-06-05","g.becker@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-05","p3.lee@samsung.com","8.0","5.0","1.0","4.0","0.0" +"2026-06-05","chetan.d@samsung.com","8.0","1.0","1.0","0.0","0.0" +"2026-06-05","bo1.sun@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-05","m.wojtas@partner.samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-05","anirban.b3@samsung.com","7.0","5.0","1.0","5.0","0.0" +"2026-06-05","gg.rojas@samsung.com","7.0","3.0","1.0","3.0","0.0" +"2026-06-05","mohan.lal@samsung.com","7.0","10.0","1.0","9.0","0.0" +"2026-06-05","k.urbanski@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-05","santhosh.s2@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-05","tarun.sanjay@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-05","k.bugala@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-05","monika.sroha@samsung.com","7.0","11.0","1.0","10.0","0.0" +"2026-06-05","g.dagley@partner.samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-05","xiaoting.li@samsung.com","7.0","2.0","1.0","2.0","0.0" +"2026-06-05","a.chaudhary@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-05","sunayana.j@samsung.com","7.0","5.0","1.0","3.0","0.0" +"2026-06-05","manojit.roy@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-05","rajnish.k7@samsung.com","6.0","6.0","1.0","6.0","0.0" +"2026-06-05","m.brenner@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-05","rashi.a@samsung.com","6.0","11.0","1.0","10.0","0.0" +"2026-06-05","kookheon.kim@samsung.com","6.0","11.0","0.0","11.0","0.0" +"2026-06-05","srujan.s@partner.samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-05","deepesh.d@samsung.com","6.0","5.0","0.0","5.0","0.0" +"2026-06-05","dasari.l@samsung.com","6.0","5.0","1.0","4.0","0.0" +"2026-06-05","misha.jawa@samsung.com","6.0","1.0","1.0","1.0","0.0" +"2026-06-05","m.vandewal@samsung.com","6.0","6.0","1.0","5.0","0.0" +"2026-06-05","c.sharad@samsung.com","6.0","2.0","1.0","1.0","0.0" +"2026-06-05","r.paragbhai@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-05","l.bienkowski@partner.samsung.com","6.0","7.0","0.0","6.0","0.0" +"2026-06-05","a.efremchev@samsung.com","6.0","7.0","0.0","7.0","0.0" +"2026-06-05","praveen.kmr@samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-05","c.gault@samsung.com","5.0","2.0","0.0","2.0","0.0" +"2026-06-05","pashupati.r1@samsung.com","5.0","4.0","0.0","4.0","0.0" +"2026-06-05","kr1.ayush@samsung.com","5.0","1.0","0.0","0.0","0.0" +"2026-06-05","g.deochakke@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-05","v.saucedo@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-05","a.conner@samsung.com","5.0","11.0","0.0","10.0","0.0" +"2026-06-05","m1.agarwal@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-05","y1.chen@partner.samsung.com","5.0","5.0","1.0","4.0","0.0" +"2026-06-05","srikanth.h@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-05","m.adnan25@samsung.com","5.0","7.0","1.0","7.0","0.0" +"2026-06-05","csup.hwang@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-05","m.siddiquie@samsung.com","5.0","4.0","1.0","4.0","0.0" +"2026-06-05","yahan.zhang@samsung.com","5.0","2.0","1.0","2.0","0.0" +"2026-06-05","sundar.siv@samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-05","a.tolambiya@samsung.com","4.0","3.0","0.0","3.0","0.0" +"2026-06-05","emily.chen@samsung.com","4.0","7.0","0.0","7.0","0.0" +"2026-06-05","s.sirigiredd@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-05","v.gifford@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-05","b.brodecki@partner.samsung.com","4.0","5.0","1.0","5.0","0.0" +"2026-06-05","beth.padera@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-05","a.gorthi@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-05","r.babinski@samsung.com","4.0","4.0","1.0","3.0","0.0" +"2026-06-05","jh0306.lim@samsung.com","4.0","1.0","0.0","0.0","0.0" +"2026-06-05","s.deepthi@samsung.com","4.0","2.0","1.0","2.0","0.0" +"2026-06-05","m.kandikonda@samsung.com","4.0","1.0","1.0","0.0","0.0" +"2026-06-05","linda.chu@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-05","kushaldeep.s@samsung.com","4.0","3.0","1.0","2.0","0.0" +"2026-06-05","w1.huang@samsung.com","4.0","5.0","0.0","5.0","0.0" +"2026-06-05","y3.jeong@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-05","thakur.v@samsung.com","4.0","3.0","1.0","3.0","0.0" +"2026-06-05","m.boriya@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-05","rahul.jain1@samsung.com","4.0","1.0","0.0","1.0","0.0" +"2026-06-05","riddhi.p@partner.samsung.com","4.0","1.0","1.0","1.0","0.0" +"2026-06-05","kr.saurabh09@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-05","vish.jeet@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-05","michena.metla@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-05","m.santoro@samsung.com","4.0","5.0","1.0","4.0","0.0" +"2026-06-05","shabeer.p@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-05","akash.b2@samsung.com","4.0","6.0","1.0","6.0","0.0" +"2026-06-05","hari.shanker@samsung.com","4.0","6.0","1.0","5.0","0.0" +"2026-06-05","tarandeep.s3@samsung.com","3.0","5.0","0.0","5.0","0.0" +"2026-06-05","l.torgerson@partner.samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-05","d.toolan@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-05","rakesh.kr2@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-05","j.kulesza@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-05","m.wiacek2@samsung.com","3.0","4.0","0.0","3.0","0.0" +"2026-06-05","konni.bunya@partner.samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-05","anthonyj.5@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-05","nikhil.c2@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-05","aashish.g@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-05","changmin.bae@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-05","rfp-automation-bedrock-role-j0bj2i","3.0","1.0","0.0","0.0","0.0" +"2026-06-05","chung.jade@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-05","a.movsesyan@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-05","j.gottlieb@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-05","george1.kim@samsung.com","3.0","0.0","0.0","0.0","0.0" +"2026-06-05","niyanta.z@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-05","r.ghewari@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-05","e.strong@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-05","sang.jun@partner.samsung.com","3.0","1.0","1.0","0.0","0.0" +"2026-06-05","yashwanth.k1@partner.samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-05","sy13.kim@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-05","d.barouski@samsung.com","3.0","4.0","1.0","4.0","0.0" +"2026-06-05","weidong5.w@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-05","s.zehnder@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-05","anshu.makkar@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-05","khandekar.s@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-05","g.murad@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-05","vikas.magar@samsung.com","2.0","5.0","0.0","5.0","0.0" +"2026-06-05","arun.km@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-05","vignesh.b@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-05","himesha.a@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-05","t.sanyal@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-05","m.wasniewski@partner.samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-05","a.krasnikau@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","c3.lee@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","woongji.kim@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","e1.weber@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","stewart.farr@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","y.li10@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-05","r.jiang@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","xiao.yang1@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-05","anurag.c@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","varun.gupta1@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-05","l.spahn@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","sai.mamidi@partner.samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-05","naveenk1.v@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","s.bodhe@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","antik.a@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-05","jared.katz@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-05","taehodev.lee@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-05","lee.irene@partner.samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-05","k.pietrzyk2@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","n.colberg@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","a3.paliwal@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","s.danchenko@samsung.com","1.0","2.0","0.0","1.0","0.0" +"2026-06-05","harsh.mt@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","r.fenochio@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","zhen.jin@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","c.bergquist@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","r.kalafate@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","m.nowacki3@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","c.joshi@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","sumit.m2@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","sundar.b@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","mr.dareddy@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","r4.gupta@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","xinhui.shen@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","m.agosto@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","irvin.low@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","dhruv.gaur@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","i.andriychuk@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","m.pavlovski@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","mohd.aslam@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","akhil.k@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","ashish.tr@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","a.dhanens@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","udit.smvdu@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","wei.lu@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","sujan.challa@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","w.lewandowsk@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","r2.deshpande@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","arun.a@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","h.sonehara@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","arpit.nigam@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","chapman.c@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","k.stasiuk2@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","sudarshan.p@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","aman.c@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","s.bishayee@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","mohsen.an@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-05","rishabh.m@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","sachin.r@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","priyanshu.j@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-05","a.gruzman@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-05","n.divya@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","a.odonnell@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","jiachuan.h@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","jiu.jung@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","ryan.ward@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","j.shih@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","jl.wilkinson@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","a.gunukula@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-05","d2.tran@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","hao2.gao@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","seth.schortz@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","d.suresh@partner.samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-05","a.sharma2@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","ritwik.c@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","vinay.reddy1@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","sameer.dash@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-05","r.jasina@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","l.sutkowski@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","kim.sj@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","d.nejbauer@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","saejun.cha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","e.ronquillo@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","ankitha.r@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","k.teichman@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","paul.k@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","girija.m1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","yating.w@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","reshmi.k@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","cost-agent-dev-bedrock-role-bs48gl","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","aditya.verma@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","lalit.rawat@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","s.kaul@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","mengya.wei@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","1.0","0.0","0.0","0.0" +"2026-06-05","awesta.wahab@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","david.golan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","zeyang.ye@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","j.polowitz@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","dongd1.xu@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","k.gangadhara@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","a.szyszka@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","engineering_ssp_dev","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","a.abbaraju@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","kushal.m1@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","james3.ko@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","v.atmuri@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","srinath.r@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","jia.xue@samsung.com","0.0","2.0","0.0","0.0","0.0" +"2026-06-05","t2.lee@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","ramisetty.k@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","l.xiong@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","i.stojkovic@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","daniel.cox@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","a.goldsby@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","swarna.roy@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","hr.shinde@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","s.yaddanapud@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","navneeth.n@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","b.anehosur@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","adarsh.v@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","k.zeng@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","vidhan.vijay@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","v.narayanan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","l.rawson@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","prashant.sg@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","v3.chen@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","k.pampati@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-05","ajay.guna@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","c.borins@samsung.com","428.0","411.0","28.0","383.0","1.0" +"2026-06-04","a.devappa@partner.samsung.com","364.0","350.0","32.0","317.0","0.0" +"2026-06-04","brian.song@samsung.com","224.0","215.0","15.0","200.0","1.0" +"2026-06-04","g.brancovici@samsung.com","216.0","255.0","0.0","248.0","0.0" +"2026-06-04","n.singhal@samsung.com","172.0","194.0","9.0","185.0","0.0" +"2026-06-04","tom.sella@samsung.com","132.0","118.0","1.0","110.0","1.0" +"2026-06-04","yi5.han@samsung.com","129.0","86.0","10.0","72.0","0.0" +"2026-06-04","anshika.s@samsung.com","124.0","71.0","13.0","58.0","0.0" +"2026-06-04","amit.wagh@samsung.com","119.0","150.0","5.0","144.0","0.0" +"2026-06-04","hayeon.song@samsung.com","115.0","146.0","6.0","140.0","1.0" +"2026-06-04","hyunwoo.s@samsung.com","115.0","112.0","9.0","103.0","0.0" +"2026-06-04","chi-chen.lo@samsung.com","108.0","111.0","8.0","103.0","0.0" +"2026-06-04","princy.j@samsung.com","92.0","86.0","7.0","80.0","0.0" +"2026-06-04","w1.freeman@samsung.com","91.0","133.0","0.0","132.0","0.0" +"2026-06-04","j.muppala@samsung.com","91.0","79.0","7.0","72.0","0.0" +"2026-06-04","patrick.ting@samsung.com","89.0","103.0","5.0","99.0","0.0" +"2026-06-04","mohnish.b1@samsung.com","88.0","16.0","0.0","0.0","0.0" +"2026-06-04","c.westlund@partner.samsung.com","81.0","76.0","6.0","70.0","0.0" +"2026-06-04","t1.park@samsung.com","76.0","22.0","10.0","12.0","0.0" +"2026-06-04","c.sharad@samsung.com","73.0","72.0","5.0","67.0","0.0" +"2026-06-04","yashwanth.k1@partner.samsung.com","67.0","48.0","6.0","41.0","0.0" +"2026-06-04","ryan.ward@samsung.com","67.0","55.0","6.0","49.0","0.0" +"2026-06-04","haoran.lu@samsung.com","64.0","34.0","3.0","26.0","0.0" +"2026-06-04","wh71.kim@samsung.com","62.0","47.0","5.0","42.0","0.0" +"2026-06-04","j.sarallo@samsung.com","61.0","55.0","5.0","50.0","0.0" +"2026-06-04","somsekhar.d1@samsung.com","59.0","101.0","6.0","95.0","0.0" +"2026-06-04","sarat.c@partner.samsung.com","58.0","86.0","7.0","79.0","0.0" +"2026-06-04","huijun.wu@samsung.com","57.0","119.0","0.0","117.0","1.0" +"2026-06-04","l.roderick@samsung.com","54.0","58.0","3.0","54.0","0.0" +"2026-06-04","abhishek.pd@samsung.com","54.0","94.0","4.0","90.0","0.0" +"2026-06-04","n.bertha@samsung.com","54.0","22.0","11.0","11.0","0.0" +"2026-06-04","v.gandhi@samsung.com","52.0","69.0","2.0","67.0","0.0" +"2026-06-04","claude-code-gha-role","48.0","58.0","6.0","52.0","0.0" +"2026-06-04","n.colberg@samsung.com","48.0","35.0","9.0","25.0","0.0" +"2026-06-04","c.lewczyk@samsung.com","47.0","10.0","6.0","4.0","0.0" +"2026-06-04","yj5.lee@partner.samsung.com","46.0","39.0","3.0","35.0","0.0" +"2026-06-04","sourav.dutta@samsung.com","46.0","34.0","4.0","30.0","0.0" +"2026-06-04","a.trivedy@samsung.com","44.0","23.0","3.0","18.0","0.0" +"2026-06-04","ashley.koett@samsung.com","43.0","48.0","2.0","46.0","0.0" +"2026-06-04","a.jeong1@samsung.com","42.0","29.0","4.0","25.0","0.0" +"2026-06-04","sourav.kshp@samsung.com","42.0","26.0","3.0","21.0","0.0" +"2026-06-04","vivek.singh1@samsung.com","41.0","31.0","4.0","27.0","0.0" +"2026-06-04","vaibhav.t1@samsung.com","40.0","43.0","2.0","41.0","0.0" +"2026-06-04","abhishek.km3@samsung.com","40.0","34.0","3.0","31.0","0.0" +"2026-06-04","sindhu.d@partner.samsung.com","39.0","29.0","3.0","26.0","0.0" +"2026-06-04","litellm","38.0","8.0","1.0","2.0","0.0" +"2026-06-04","r.rittenhous@samsung.com","38.0","66.0","4.0","63.0","0.0" +"2026-06-04","shuyang.y@samsung.com","38.0","25.0","4.0","21.0","0.0" +"2026-06-04","m.hyman@samsung.com","38.0","43.0","5.0","37.0","0.0" +"2026-06-04","s4.jeon@samsung.com","38.0","37.0","3.0","34.0","0.0" +"2026-06-04","jiwooks.kim@samsung.com","37.0","23.0","4.0","20.0","0.0" +"2026-06-04","cmutha.ks@samsung.com","36.0","25.0","3.0","22.0","0.0" +"2026-06-04","lam.n@samsung.com","34.0","37.0","2.0","35.0","0.0" +"2026-06-04","anurag.c@samsung.com","34.0","41.0","1.0","39.0","0.0" +"2026-06-04","s.kangal@samsung.com","34.0","38.0","2.0","36.0","0.0" +"2026-06-04","suresh.mgi@samsung.com","34.0","56.0","3.0","53.0","0.0" +"2026-06-04","l.spahn@samsung.com","32.0","27.0","2.0","25.0","0.0" +"2026-06-04","t.jain@samsung.com","31.0","60.0","2.0","58.0","0.0" +"2026-06-04","daya.8@partner.samsung.com","31.0","31.0","1.0","30.0","0.0" +"2026-06-04","inhyuk.jung@samsung.com","31.0","30.0","2.0","27.0","0.0" +"2026-06-04","aravind.s5@samsung.com","31.0","53.0","3.0","50.0","0.0" +"2026-06-04","vignesh.b@samsung.com","30.0","25.0","5.0","19.0","0.0" +"2026-06-04","pooja.k@samsung.com","30.0","21.0","3.0","18.0","0.0" +"2026-06-04","aabhas.b@samsung.com","30.0","33.0","0.0","32.0","0.0" +"2026-06-04","m.siddiquie@samsung.com","29.0","28.0","2.0","26.0","0.0" +"2026-06-04","misha.jawa@samsung.com","29.0","13.0","3.0","9.0","0.0" +"2026-06-04","sudipta.p@samsung.com","28.0","38.0","1.0","37.0","0.0" +"2026-06-04","s.sirigiredd@samsung.com","27.0","16.0","3.0","13.0","0.0" +"2026-06-04","erhu.he@samsung.com","27.0","35.0","1.0","34.0","0.0" +"2026-06-04","davidj.jung@samsung.com","27.0","7.0","5.0","2.0","0.0" +"2026-06-04","zh.kim@samsung.com","27.0","38.0","0.0","37.0","0.0" +"2026-06-04","khyeok.lee@samsung.com","26.0","20.0","2.0","18.0","0.0" +"2026-06-04","y3.jeong@samsung.com","26.0","41.0","0.0","41.0","0.0" +"2026-06-04","singh.satyam@samsung.com","26.0","22.0","3.0","19.0","0.0" +"2026-06-04","y2.yan@samsung.com","26.0","14.0","2.0","11.0","0.0" +"2026-06-04","a.chaudhary@samsung.com","26.0","21.0","2.0","19.0","0.0" +"2026-06-04","b.harr@samsung.com","25.0","32.0","0.0","31.0","0.0" +"2026-06-04","v.jangir@samsung.com","25.0","17.0","3.0","15.0","0.0" +"2026-06-04","d.kargapolov@samsung.com","25.0","26.0","1.0","25.0","0.0" +"2026-06-04","c1.cabiati@samsung.com","25.0","24.0","4.0","20.0","0.0" +"2026-06-04","harsh.c@samsung.com","25.0","11.0","3.0","8.0","0.0" +"2026-06-04","a.pentela@samsung.com","25.0","45.0","2.0","44.0","0.0" +"2026-06-04","hosung.you@samsung.com","25.0","40.0","3.0","37.0","0.0" +"2026-06-04","harikrish.p@samsung.com","24.0","6.0","1.0","5.0","0.0" +"2026-06-04","k.dakarapu@samsung.com","24.0","38.0","3.0","35.0","0.0" +"2026-06-04","d4.mishra@samsung.com","23.0","19.0","0.0","17.0","0.0" +"2026-06-04","pk.shukla@samsung.com","23.0","38.0","2.0","36.0","0.0" +"2026-06-04","sumeet.a@samsung.com","23.0","20.0","2.0","18.0","0.0" +"2026-06-04","v.saucedo@samsung.com","22.0","15.0","4.0","11.0","0.0" +"2026-06-04","chung.jade@samsung.com","22.0","19.0","4.0","16.0","0.0" +"2026-06-04","p3.lee@samsung.com","22.0","14.0","2.0","12.0","0.0" +"2026-06-04","r.vishnumurt@samsung.com","22.0","25.0","1.0","23.0","0.0" +"2026-06-04","n2.mehta@samsung.com","22.0","19.0","2.0","18.0","0.0" +"2026-06-04","varun.rawat@samsung.com","21.0","26.0","3.0","24.0","0.0" +"2026-06-04","gaurav.garg1@samsung.com","21.0","19.0","1.0","17.0","0.0" +"2026-06-04","t.abhinav@samsung.com","21.0","20.0","1.0","19.0","0.0" +"2026-06-04","john.iburg@samsung.com","21.0","31.0","1.0","29.0","0.0" +"2026-06-04","sasitharan.r@samsung.com","21.0","29.0","2.0","27.0","0.0" +"2026-06-04","swati.s8@samsung.com","21.0","23.0","3.0","21.0","0.0" +"2026-06-04","s.aleynikov@samsung.com","20.0","47.0","1.0","46.0","0.0" +"2026-06-04","akshat.8797@samsung.com","20.0","10.0","1.0","8.0","0.0" +"2026-06-04","lei1.liu@samsung.com","19.0","21.0","1.0","20.0","0.0" +"2026-06-04","r.nadimpalli@partner.samsung.com","19.0","19.0","1.0","18.0","0.0" +"2026-06-04","satya.n@samsung.com","19.0","10.0","1.0","9.0","0.0" +"2026-06-04","riddhi.p@partner.samsung.com","19.0","9.0","2.0","6.0","0.0" +"2026-06-04","vikas.magar@samsung.com","19.0","38.0","2.0","36.0","0.0" +"2026-06-04","sugir.tharaj@samsung.com","19.0","40.0","2.0","38.0","0.0" +"2026-06-04","n.rosendall@samsung.com","18.0","13.0","2.0","10.0","0.0" +"2026-06-04","david.golan@samsung.com","18.0","7.0","4.0","3.0","0.0" +"2026-06-04","j.vishal@samsung.com","18.0","4.0","1.0","3.0","0.0" +"2026-06-04","j.asofsky@partner.samsung.com","18.0","11.0","2.0","10.0","0.0" +"2026-06-04","h.sonehara@samsung.com","18.0","15.0","1.0","14.0","0.0" +"2026-06-04","changmin.bae@samsung.com","18.0","14.0","3.0","11.0","0.0" +"2026-06-04","khandekar.s@samsung.com","18.0","10.0","3.0","7.0","0.0" +"2026-06-04","meet.gogri@samsung.com","17.0","18.0","3.0","16.0","0.0" +"2026-06-04","j.morrill@samsung.com","17.0","17.0","1.0","15.0","0.0" +"2026-06-04","s.chiou@samsung.com","16.0","20.0","1.0","8.0","4.0" +"2026-06-04","saejun.cha@samsung.com","16.0","9.0","3.0","6.0","0.0" +"2026-06-04","chirag.chauhan@samsung.com","16.0","19.0","2.0","17.0","0.0" +"2026-06-04","sapna.singh@samsung.com","15.0","21.0","2.0","19.0","0.0" +"2026-06-04","ramprasad.d@samsung.com","15.0","9.0","1.0","8.0","0.0" +"2026-06-04","s.chaitanya@samsung.com","15.0","15.0","1.0","12.0","0.0" +"2026-06-04","k.seitllari@samsung.com","15.0","5.0","3.0","1.0","0.0" +"2026-06-04","i.barrios1@samsung.com","15.0","13.0","1.0","12.0","0.0" +"2026-06-04","c.resnick@samsung.com","15.0","28.0","2.0","26.0","0.0" +"2026-06-04","j.siddharth@samsung.com","15.0","11.0","0.0","10.0","0.0" +"2026-06-04","e.ronquillo@partner.samsung.com","14.0","12.0","2.0","9.0","0.0" +"2026-06-04","elena.piech@samsung.com","14.0","5.0","3.0","2.0","0.0" +"2026-06-04","s.dhanka@samsung.com","14.0","9.0","1.0","7.0","0.0" +"2026-06-04","r.jiang@samsung.com","14.0","16.0","2.0","14.0","0.0" +"2026-06-04","sameer.dash@samsung.com","14.0","12.0","1.0","12.0","0.0" +"2026-06-04","anuj.ar@samsung.com","14.0","14.0","1.0","13.0","0.0" +"2026-06-04","upadhyaya.r@samsung.com","14.0","7.0","2.0","6.0","0.0" +"2026-06-04","r.espinosa@samsung.com","13.0","5.0","3.0","3.0","0.0" +"2026-06-04","a.richhariya@samsung.com","13.0","14.0","1.0","13.0","0.0" +"2026-06-04","weidong5.w@samsung.com","13.0","8.0","0.0","7.0","0.0" +"2026-06-04","antriksh.p@samsung.com","13.0","24.0","1.0","23.0","0.0" +"2026-06-04","m.santoro@samsung.com","13.0","27.0","1.0","26.0","0.0" +"2026-06-04","george.f@samsung.com","13.0","22.0","1.0","20.0","0.0" +"2026-06-04","j2.kumar@samsung.com","13.0","6.0","1.0","5.0","0.0" +"2026-06-04","sehwan.noh@samsung.com","13.0","14.0","0.0","13.0","0.0" +"2026-06-04","i.andriychuk@samsung.com","13.0","15.0","2.0","13.0","0.0" +"2026-06-04","agam.jain@samsung.com","13.0","11.0","0.0","11.0","0.0" +"2026-06-04","prashant.sg@samsung.com","13.0","16.0","2.0","14.0","0.0" +"2026-06-04","praveen.kmr@samsung.com","13.0","18.0","2.0","16.0","0.0" +"2026-06-04","sunit.m@samsung.com","13.0","14.0","1.0","13.0","0.0" +"2026-06-04","seokbaek.lee@samsung.com","13.0","18.0","0.0","17.0","0.0" +"2026-06-04","siddesh.a@samsung.com","12.0","5.0","1.0","4.0","0.0" +"2026-06-04","xinhui.shen@samsung.com","12.0","13.0","1.0","12.0","0.0" +"2026-06-04","deepesh.d@samsung.com","12.0","12.0","1.0","11.0","0.0" +"2026-06-04","johnson.l@samsung.com","12.0","13.0","1.0","13.0","0.0" +"2026-06-04","n.viswas@samsung.com","12.0","2.0","0.0","0.0","0.0" +"2026-06-04","dasari.l@samsung.com","12.0","7.0","1.0","6.0","0.0" +"2026-06-04","u.saboji@samsung.com","11.0","6.0","1.0","5.0","0.0" +"2026-06-04","george1.kim@samsung.com","11.0","3.0","1.0","1.0","0.0" +"2026-06-04","a.ambalavana@samsung.com","11.0","9.0","0.0","9.0","0.0" +"2026-06-04","a.sharma2@samsung.com","11.0","4.0","2.0","1.0","0.0" +"2026-06-04","j3.martin@samsung.com","11.0","27.0","1.0","26.0","0.0" +"2026-06-04","swetha.gb@samsung.com","11.0","4.0","1.0","3.0","0.0" +"2026-06-04","v.rakshan@samsung.com","11.0","8.0","2.0","7.0","0.0" +"2026-06-04","vinay.gali@samsung.com","11.0","14.0","2.0","12.0","0.0" +"2026-06-04","s.deepthi@samsung.com","11.0","9.0","1.0","8.0","0.0" +"2026-06-04","sundar.siv@samsung.com","11.0","7.0","1.0","6.0","0.0" +"2026-06-04","jl.wilkinson@samsung.com","10.0","2.0","1.0","1.0","0.0" +"2026-06-04","akash.b2@samsung.com","10.0","11.0","1.0","10.0","0.0" +"2026-06-04","nikita.g1@samsung.com","10.0","11.0","0.0","10.0","0.0" +"2026-06-04","e.martis@samsung.com","10.0","5.0","1.0","4.0","0.0" +"2026-06-04","anirban.b3@samsung.com","10.0","5.0","1.0","4.0","0.0" +"2026-06-04","g.dagley@partner.samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-04","ke.jin1@samsung.com","10.0","17.0","1.0","16.0","0.0" +"2026-06-04","kr1.ayush@samsung.com","10.0","3.0","0.0","0.0","0.0" +"2026-06-04","shiv.mehla@samsung.com","10.0","12.0","1.0","11.0","0.0" +"2026-06-04","pavel.p@partner.samsung.com","10.0","9.0","1.0","9.0","0.0" +"2026-06-04","rahul.jain1@samsung.com","9.0","4.0","1.0","2.0","0.0" +"2026-06-04","y1.chen@partner.samsung.com","9.0","7.0","1.0","6.0","0.0" +"2026-06-04","manojit.roy@samsung.com","9.0","5.0","1.0","4.0","0.0" +"2026-06-04","jayesh.asawa@samsung.com","9.0","6.0","1.0","5.0","0.0" +"2026-06-04","rakshith.r@samsung.com","9.0","8.0","0.0","8.0","0.0" +"2026-06-04","m1.agarwal@samsung.com","9.0","7.0","1.0","6.0","0.0" +"2026-06-04","a.sekowski@samsung.com","9.0","15.0","1.0","15.0","0.0" +"2026-06-04","s.nakka@samsung.com","9.0","16.0","1.0","16.0","0.0" +"2026-06-04","p.jarosz@partner.samsung.com","9.0","12.0","1.0","11.0","0.0" +"2026-06-04","umesh.km@samsung.com","9.0","10.0","1.0","9.0","0.0" +"2026-06-04","sani.v@samsung.com","9.0","8.0","0.0","8.0","0.0" +"2026-06-04","b.gujjar@samsung.com","8.0","9.0","1.0","8.0","0.0" +"2026-06-04","a.tolambiya@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-04","shivraj.r@samsung.com","8.0","5.0","1.0","4.0","0.0" +"2026-06-04","kusha.pande@samsung.com","8.0","8.0","0.0","7.0","0.0" +"2026-06-04","linda.chu@samsung.com","8.0","8.0","1.0","6.0","0.0" +"2026-06-04","shabeer.p@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-04","kishore.r@samsung.com","8.0","8.0","1.0","7.0","0.0" +"2026-06-04","gs.suvarapu@samsung.com","8.0","8.0","1.0","7.0","0.0" +"2026-06-04","joyston.f@samsung.com","8.0","6.0","1.0","6.0","0.0" +"2026-06-04","sujan.challa@samsung.com","8.0","11.0","1.0","10.0","0.0" +"2026-06-04","w1.huang@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-04","sumit.m2@samsung.com","8.0","4.0","0.0","3.0","0.0" +"2026-06-04","subrat.kp@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-04","csup.hwang@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-04","l.steigerwal@samsung.com","8.0","14.0","1.0","14.0","0.0" +"2026-06-04","mr.dareddy@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-04","jh0306.lim@samsung.com","7.0","3.0","1.0","3.0","0.0" +"2026-06-04","rfp-automation-bedrock-role-j0bj2i","7.0","1.0","0.0","0.0","0.0" +"2026-06-04","r.fenochio@samsung.com","7.0","5.0","0.0","5.0","0.0" +"2026-06-04","mohit.sh3@samsung.com","7.0","10.0","1.0","9.0","0.0" +"2026-06-04","pi_audience_agentic_ai","7.0","2.0","0.0","1.0","0.0" +"2026-06-04","anshu.makkar@samsung.com","7.0","7.0","0.0","7.0","0.0" +"2026-06-04","c.banchek@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-04","sundar.b@samsung.com","7.0","7.0","1.0","6.0","0.0" +"2026-06-04","anusha.holla@samsung.com","7.0","2.0","1.0","1.0","0.0" +"2026-06-04","m.abhinav@samsung.com","7.0","4.0","0.0","3.0","0.0" +"2026-06-04","p.borhade@samsung.com","7.0","11.0","1.0","11.0","0.0" +"2026-06-04","a.goldsby@partner.samsung.com","6.0","6.0","1.0","4.0","0.0" +"2026-06-04","m.khomenkov@samsung.com","6.0","7.0","0.0","6.0","0.0" +"2026-06-04","k.gangadhara@partner.samsung.com","6.0","4.0","1.0","2.0","0.0" +"2026-06-04","vikas.km@samsung.com","6.0","8.0","1.0","7.0","0.0" +"2026-06-04","xiao.yang1@samsung.com","6.0","4.0","0.0","4.0","0.0" +"2026-06-04","g.nicolae@samsung.com","6.0","10.0","1.0","9.0","0.0" +"2026-06-04","hyungi.seong@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-04","anthonyj.5@samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-04","yt.chien@partner.samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-04","ashwin.v@samsung.com","6.0","5.0","1.0","4.0","0.0" +"2026-06-04","nishchay.k@samsung.com","6.0","4.0","0.0","4.0","0.0" +"2026-06-04","p.furtak@samsung.com","6.0","2.0","0.0","1.0","0.0" +"2026-06-04","vivek.das@samsung.com","6.0","4.0","1.0","4.0","0.0" +"2026-06-04","a.puntambeka@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-04","l.torgerson@partner.samsung.com","6.0","6.0","0.0","6.0","0.0" +"2026-06-04","rajnish.k7@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-04","lee.irene@partner.samsung.com","6.0","2.0","0.0","0.0","0.0" +"2026-06-04","a.gunukula@partner.samsung.com","6.0","12.0","0.0","12.0","0.0" +"2026-06-04","a.efremchev@samsung.com","6.0","9.0","0.0","9.0","0.0" +"2026-06-04","aman.barya@samsung.com","6.0","2.0","0.0","2.0","0.0" +"2026-06-04","niyanta.z@samsung.com","6.0","6.0","1.0","6.0","0.0" +"2026-06-04","a.movsesyan@samsung.com","5.0","5.0","0.0","5.0","0.0" +"2026-06-04","r4.gupta@samsung.com","5.0","9.0","1.0","8.0","0.0" +"2026-06-04","c.jakrachai@samsung.com","5.0","5.0","0.0","4.0","0.0" +"2026-06-04","r.paragbhai@samsung.com","5.0","6.0","0.0","5.0","0.0" +"2026-06-04","sang.jun@partner.samsung.com","5.0","1.0","1.0","0.0","0.0" +"2026-06-04","ritesh.k@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-04","bo1.sun@samsung.com","5.0","1.0","1.0","0.0","0.0" +"2026-06-04","k.beatty@samsung.com","5.0","2.0","1.0","0.0","0.0" +"2026-06-04","d.hu10@samsung.com","5.0","5.0","1.0","4.0","0.0" +"2026-06-04","j.gottlieb@samsung.com","5.0","6.0","1.0","6.0","0.0" +"2026-06-04","a.daveiga@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-04","y.li10@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-04","bellkar.r@samsung.com","5.0","9.0","1.0","8.0","0.0" +"2026-06-04","k.pampati@partner.samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-04","b.woodcock@partner.samsung.com","4.0","8.0","0.0","8.0","0.0" +"2026-06-04","tarun.sanjay@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-04","hao2.gao@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-04","dayita.s@samsung.com","4.0","4.0","1.0","3.0","0.0" +"2026-06-04","zhen.jin@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-04","bernard.feng@samsung.com","4.0","4.0","0.0","3.0","0.0" +"2026-06-04","rishabh.m@samsung.com","4.0","2.0","1.0","2.0","0.0" +"2026-06-04","kr.saurabh09@samsung.com","4.0","1.0","1.0","1.0","0.0" +"2026-06-04","chetan.d@samsung.com","4.0","1.0","0.0","0.0","0.0" +"2026-06-04","onur.y@samsung.com","4.0","1.0","0.0","1.0","0.0" +"2026-06-04","pashupati.r1@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-04","mohan.lal@samsung.com","4.0","6.0","1.0","5.0","0.0" +"2026-06-04","monika.sroha@samsung.com","4.0","5.0","1.0","5.0","0.0" +"2026-06-04","s.sequeira@samsung.com","4.0","9.0","0.0","9.0","0.0" +"2026-06-04","vibhav.3@samsung.com","4.0","3.0","1.0","2.0","0.0" +"2026-06-04","d.gorbenko@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-04","ashish.tr@samsung.com","4.0","1.0","1.0","1.0","0.0" +"2026-06-04","gg.rojas@samsung.com","4.0","1.0","1.0","1.0","0.0" +"2026-06-04","kookheon.kim@samsung.com","4.0","6.0","0.0","6.0","0.0" +"2026-06-04","s.kolusu@samsung.com","4.0","1.0","0.0","1.0","0.0" +"2026-06-04","a3.paliwal@samsung.com","4.0","3.0","1.0","2.0","0.0" +"2026-06-04","sanket.kabra@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-04","santhosh.s2@samsung.com","4.0","6.0","0.0","6.0","0.0" +"2026-06-04","l.bienkowski@partner.samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-04","arpit.nigam@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-04","ramisetty.k@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-04","a.dhanens@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-04","s.bodhe@partner.samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-04","r.sikdar@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-04","d2.tran@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-04","yahan.zhang@samsung.com","3.0","0.0","0.0","0.0","0.0" +"2026-06-04","tarandeep.s3@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-04","r2.deshpande@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-04","d.skliarov@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-04","rahul.p02@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-04","zane.z@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-04","a.shakya@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-04","kim.raymond@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-04","sudarshan.p@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-04","aman.c@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-04","p.ricciardi@samsung.com","3.0","1.0","1.0","0.0","0.0" +"2026-06-04","edwardj.kim@partner.samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-04","m.vandewal@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-04","n.srinidhi@samsung.com","3.0","6.0","0.0","6.0","0.0" +"2026-06-04","vish.jeet@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-04","rakesh.kr2@samsung.com","3.0","7.0","1.0","6.0","0.0" +"2026-06-04","m.amjad@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-04","kk.anil@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-04","nitish.yadav@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-04","gupta.s@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-04","c.gault@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-04","a.conner@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-04","a12.li@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","bhardwaj.a@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","domain.kim@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","ravali.b@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-04","joseph.rice@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","g1.raju@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-04","jui.shah@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-04","r.kalafate@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-04","rah.agrawal@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-04","xiaoting.li@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","h.kaur1@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-04","eunice4.kim@samsung.com","2.0","1.0","1.0","0.0","0.0" +"2026-06-04","irvin.low@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","daniel.ortiz@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","g.deochakke@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-04","l.rawson@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-04","srujan.s@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","m.adnan25@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","sangeeta.1@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-04","vincent.k@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-04","emily.chen@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-04","s.zehnder@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-04","d.toolan@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","r.parol@partner.samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-04","zehua.z@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","mohd.khan@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-04","srikanth.h@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-04","mohd.aslam@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-04","muthu.v1@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","r.ghewari2@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-04","konni.bunya@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","kiran.pk@partner.samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-04","navneeth.n@partner.samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-04","i.stojkovic@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-04","alex.hole@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-04","sreejit.r@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-04","t.sanyal@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-04","jared.katz@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","m.pavlovski@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","sachin.r@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","loy.noronha@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","aditya.r@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","girija.m1@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-04","r.chittimall@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","aman.goel@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","r.neeraj@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","woongji.kim@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","bohdan.v@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-04","t.glorio@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","kushaldeep.s@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-04","h.evanoff@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","salek.b@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","e1.weber@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","udit.smvdu@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","justin.shin@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","sunayana.j@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-04","tushar.bg@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","v.narayanan@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","gamya.k@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","mengya.wei@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","harsh.mt@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","m.kandikonda@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","taehodev.lee@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","jasmeen.p@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","l.stucky@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","devesh.g@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","rajeev.r@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","j.dickerson@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","nikhil.c2@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","rfp_automation_poc","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","saransh.p@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","lalit.rawat@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","r.ghewari@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","t.zarod@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","piyush.sd@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","shelley.wu@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-04","kim.sj@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","srinath.s1@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","v.edragas@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-04","r2.jain@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","k.teichman@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","beth.padera@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-04","anisha.moha@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","g.murad@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-04","arun.km@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","v.gifford@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-04","seohyoung.ha@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","swarna.roy@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","m.ferreira@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-04","m.agosto@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","rashi.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","dongd1.xu@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","r.latortue@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","t2.lee@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","g.callahan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","gongli.duan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","jiachuan.h@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","li.li1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","thakur.v@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","sibi.menon@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","yoshitha.p@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","megan.dotson@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","j.lovejoy@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","wei.lu@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-04","sai.mamidi@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","paul.k@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","varun.gupta1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","blackhole-use1-sandbox","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","l.xiong@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","a.gruzman@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","kshitiz.p@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","adarsh.v@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","amit.rodge@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","m.vigliotti@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","a.orlov@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-04","vidhan.vijay@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","yating.w@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","f.tarlan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","ankitha.r@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","c.bergquist@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","semantic_bot","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","j.polowitz@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","seth.schortz@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","puneet.saini@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","o1.lee@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","mudit.vaish@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","ajay.guna@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","skumar.singh@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","cost-agent-dev-bedrock-role-bs48gl","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","himesha.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","somnath.g@samsung.com","0.0","1.0","0.0","0.0","0.0" +"2026-06-04","mayank.singh@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-04","ma.caballero@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","michena.metla@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","d.suresh@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","hari.shanker@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-04","james3.ko@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","jiu.jung@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","edwin.james@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","jia.xue@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","antik.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","a.odonnell@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-04","pi-model-contextual-bedrock-role-qbwj07","0.0","102.0","0.0","0.0","58.0" +"2026-06-04","a.abbaraju@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","p.jarosz@partner.samsung.com","525.0","675.0","27.0","425.0","8.0" +"2026-06-03","c.borins@samsung.com","428.0","278.0","39.0","239.0","1.0" +"2026-06-03","b.harr@samsung.com","201.0","163.0","0.0","151.0","0.0" +"2026-06-03","tom.sella@samsung.com","199.0","164.0","13.0","147.0","1.0" +"2026-06-03","c.westlund@partner.samsung.com","174.0","205.0","9.0","195.0","0.0" +"2026-06-03","ryan.ward@samsung.com","174.0","118.0","15.0","101.0","0.0" +"2026-06-03","yi5.han@samsung.com","151.0","146.0","9.0","135.0","0.0" +"2026-06-03","k.beatty@samsung.com","150.0","69.0","33.0","36.0","0.0" +"2026-06-03","litellm","147.0","82.0","3.0","60.0","0.0" +"2026-06-03","vaibhav.t1@samsung.com","145.0","159.0","5.0","150.0","1.0" +"2026-06-03","w1.freeman@samsung.com","143.0","192.0","0.0","188.0","0.0" +"2026-06-03","t.teter@partner.samsung.com","127.0","115.0","10.0","106.0","0.0" +"2026-06-03","a.devappa@partner.samsung.com","126.0","131.0","10.0","121.0","0.0" +"2026-06-03","n.singhal@samsung.com","122.0","106.0","9.0","97.0","0.0" +"2026-06-03","g.brancovici@samsung.com","121.0","161.0","0.0","158.0","0.0" +"2026-06-03","b.woodcock@partner.samsung.com","119.0","18.0","0.0","1.0","0.0" +"2026-06-03","brian.song@samsung.com","111.0","97.0","8.0","89.0","0.0" +"2026-06-03","s.dhanka@samsung.com","101.0","79.0","9.0","68.0","0.0" +"2026-06-03","y3.jeong@samsung.com","91.0","119.0","3.0","117.0","0.0" +"2026-06-03","hyunwoo.s@samsung.com","86.0","68.0","7.0","61.0","0.0" +"2026-06-03","sunit.m@samsung.com","84.0","81.0","8.0","73.0","0.0" +"2026-06-03","abhishek.km3@samsung.com","82.0","77.0","6.0","71.0","0.0" +"2026-06-03","j.pielaszkie@samsung.com","78.0","51.0","4.0","17.0","2.0" +"2026-06-03","sourav.dutta@samsung.com","78.0","95.0","4.0","91.0","0.0" +"2026-06-03","j.muppala@samsung.com","78.0","68.0","6.0","61.0","0.0" +"2026-06-03","meghana.k@samsung.com","73.0","62.0","6.0","57.0","0.0" +"2026-06-03","chi-chen.lo@samsung.com","73.0","74.0","5.0","69.0","0.0" +"2026-06-03","anusha.holla@samsung.com","72.0","34.0","8.0","26.0","0.0" +"2026-06-03","c.sharad@samsung.com","69.0","79.0","3.0","76.0","0.0" +"2026-06-03","huijun.wu@samsung.com","67.0","169.0","0.0","167.0","0.0" +"2026-06-03","sudipta.p@samsung.com","65.0","66.0","4.0","62.0","0.0" +"2026-06-03","n.rosendall@samsung.com","63.0","75.0","3.0","70.0","0.0" +"2026-06-03","haoran.lu@samsung.com","62.0","28.0","3.0","21.0","0.0" +"2026-06-03","somsekhar.d1@samsung.com","58.0","110.0","6.0","104.0","0.0" +"2026-06-03","m.koscielak@samsung.com","57.0","55.0","3.0","41.0","0.0" +"2026-06-03","pavel.p@partner.samsung.com","54.0","61.0","3.0","58.0","0.0" +"2026-06-03","d4.mishra@samsung.com","54.0","51.0","0.0","49.0","0.0" +"2026-06-03","amit.wagh@samsung.com","52.0","61.0","2.0","58.0","0.0" +"2026-06-03","sourav.kshp@samsung.com","51.0","27.0","4.0","22.0","0.0" +"2026-06-03","t1.park@samsung.com","49.0","16.0","6.0","9.0","0.0" +"2026-06-03","a.trivedy@samsung.com","48.0","22.0","4.0","16.0","0.0" +"2026-06-03","davidj.jung@samsung.com","48.0","20.0","5.0","15.0","0.0" +"2026-06-03","aravind.s5@samsung.com","48.0","96.0","4.0","91.0","0.0" +"2026-06-03","eunice4.kim@samsung.com","47.0","20.0","10.0","9.0","0.0" +"2026-06-03","khandekar.s@samsung.com","47.0","38.0","6.0","30.0","0.0" +"2026-06-03","a.jeong1@samsung.com","47.0","25.0","5.0","19.0","0.0" +"2026-06-03","zehua.z@samsung.com","46.0","27.0","2.0","21.0","0.0" +"2026-06-03","yj5.lee@partner.samsung.com","45.0","25.0","5.0","19.0","0.0" +"2026-06-03","xinhui.shen@samsung.com","45.0","46.0","3.0","44.0","0.0" +"2026-06-03","kr.saurabh09@samsung.com","41.0","21.0","4.0","17.0","0.0" +"2026-06-03","anirban.b3@samsung.com","38.0","30.0","3.0","27.0","0.0" +"2026-06-03","s.nakka@samsung.com","37.0","47.0","5.0","42.0","0.0" +"2026-06-03","claude-code-gha-role","36.0","44.0","4.0","39.0","0.0" +"2026-06-03","g.deochakke@samsung.com","34.0","43.0","1.0","42.0","0.0" +"2026-06-03","shuyang.y@samsung.com","34.0","23.0","3.0","19.0","0.0" +"2026-06-03","u.saboji@samsung.com","33.0","31.0","3.0","28.0","0.0" +"2026-06-03","t.abhinav@samsung.com","32.0","29.0","2.0","27.0","0.0" +"2026-06-03","daya.8@partner.samsung.com","32.0","37.0","1.0","36.0","0.0" +"2026-06-03","harikrish.p@samsung.com","31.0","29.0","2.0","27.0","0.0" +"2026-06-03","l.roderick@samsung.com","31.0","27.0","2.0","25.0","0.0" +"2026-06-03","j.sarallo@samsung.com","30.0","23.0","3.0","20.0","0.0" +"2026-06-03","m.barczyk@samsung.com","30.0","31.0","2.0","28.0","0.0" +"2026-06-03","agam.jain@samsung.com","30.0","33.0","0.0","32.0","0.0" +"2026-06-03","erhu.he@samsung.com","30.0","36.0","2.0","35.0","0.0" +"2026-06-03","j2.kumar@samsung.com","29.0","19.0","3.0","16.0","0.0" +"2026-06-03","j.ejsmund@samsung.com","28.0","37.0","4.0","33.0","0.0" +"2026-06-03","n2.mehta@samsung.com","28.0","24.0","2.0","22.0","0.0" +"2026-06-03","harsh.c@samsung.com","28.0","18.0","3.0","15.0","0.0" +"2026-06-03","n.bertha@samsung.com","28.0","13.0","5.0","8.0","0.0" +"2026-06-03","v.jangir@samsung.com","27.0","19.0","3.0","16.0","0.0" +"2026-06-03","yoshitha.p@samsung.com","27.0","10.0","2.0","6.0","0.0" +"2026-06-03","manojit.roy@samsung.com","26.0","17.0","3.0","14.0","0.0" +"2026-06-03","lam.n@samsung.com","26.0","27.0","1.0","25.0","0.0" +"2026-06-03","h.sonehara@samsung.com","26.0","20.0","2.0","18.0","0.0" +"2026-06-03","shivraj.r@samsung.com","26.0","17.0","2.0","15.0","0.0" +"2026-06-03","l.steigerwal@samsung.com","25.0","29.0","3.0","27.0","0.0" +"2026-06-03","shujie.fan@samsung.com","25.0","27.0","2.0","25.0","0.0" +"2026-06-03","s.sirigiredd@samsung.com","24.0","16.0","2.0","14.0","0.0" +"2026-06-03","r.fenochio@samsung.com","24.0","21.0","1.0","20.0","0.0" +"2026-06-03","r.parol@partner.samsung.com","24.0","26.0","1.0","25.0","0.0" +"2026-06-03","suresh.mgi@samsung.com","24.0","43.0","3.0","40.0","0.0" +"2026-06-03","santhosh.s2@samsung.com","24.0","29.0","3.0","26.0","0.0" +"2026-06-03","subrat.kp@samsung.com","23.0","24.0","2.0","22.0","0.0" +"2026-06-03","r.vishnumurt@samsung.com","23.0","17.0","3.0","14.0","0.0" +"2026-06-03","deepesh.d@samsung.com","23.0","24.0","1.0","23.0","0.0" +"2026-06-03","b.smietanka@samsung.com","23.0","83.0","0.0","13.0","2.0" +"2026-06-03","y2.yan@samsung.com","22.0","18.0","1.0","16.0","0.0" +"2026-06-03","c1.cabiati@samsung.com","22.0","27.0","3.0","24.0","0.0" +"2026-06-03","b.gujjar@samsung.com","22.0","29.0","3.0","26.0","0.0" +"2026-06-03","gs.suvarapu@samsung.com","21.0","14.0","2.0","9.0","0.0" +"2026-06-03","kishore.r@samsung.com","21.0","11.0","2.0","9.0","0.0" +"2026-06-03","srinath.s1@samsung.com","21.0","6794.0","1.0","13.0","1494.0" +"2026-06-03","john.iburg@samsung.com","21.0","10.0","2.0","7.0","0.0" +"2026-06-03","m.siddiquie@samsung.com","21.0","21.0","1.0","20.0","0.0" +"2026-06-03","cmutha.ks@samsung.com","21.0","10.0","2.0","7.0","0.0" +"2026-06-03","d.kargapolov@samsung.com","20.0","20.0","1.0","19.0","0.0" +"2026-06-03","vinay.gali@samsung.com","20.0","36.0","2.0","34.0","0.0" +"2026-06-03","shabeer.p@samsung.com","20.0","12.0","1.0","11.0","0.0" +"2026-06-03","gupta.s@samsung.com","19.0","9.0","2.0","8.0","0.0" +"2026-06-03","kr1.ayush@samsung.com","19.0","6.0","0.0","0.0","0.0" +"2026-06-03","a.chaudhary@samsung.com","19.0","14.0","1.0","12.0","0.0" +"2026-06-03","princy.j@samsung.com","18.0","8.0","2.0","6.0","0.0" +"2026-06-03","mohit.sh3@samsung.com","18.0","41.0","1.0","40.0","0.0" +"2026-06-03","r.kalafate@samsung.com","18.0","11.0","2.0","9.0","0.0" +"2026-06-03","sanket.kabra@samsung.com","18.0","11.0","1.0","10.0","0.0" +"2026-06-03","s.sequeira@samsung.com","18.0","34.0","1.0","33.0","0.0" +"2026-06-03","m.rachuba@samsung.com","17.0","8.0","2.0","6.0","0.0" +"2026-06-03","c.resnick@samsung.com","17.0","31.0","2.0","29.0","0.0" +"2026-06-03","sapna.singh@samsung.com","17.0","31.0","2.0","29.0","0.0" +"2026-06-03","j.vishal@samsung.com","17.0","16.0","1.0","15.0","0.0" +"2026-06-03","e.martis@samsung.com","17.0","9.0","1.0","7.0","0.0" +"2026-06-03","misha.jawa@samsung.com","17.0","6.0","2.0","4.0","0.0" +"2026-06-03","nikita.g1@samsung.com","16.0","18.0","1.0","17.0","0.0" +"2026-06-03","patrick.ting@samsung.com","16.0","12.0","1.0","10.0","0.0" +"2026-06-03","m.lepicki@samsung.com","16.0","10.0","3.0","8.0","0.0" +"2026-06-03","singh.satyam@samsung.com","16.0","14.0","1.0","12.0","0.0" +"2026-06-03","m.chomiak@samsung.com","16.0","12.0","3.0","9.0","0.0" +"2026-06-03","r.jiang@samsung.com","16.0","16.0","2.0","14.0","0.0" +"2026-06-03","ke.jin1@samsung.com","16.0","21.0","2.0","19.0","0.0" +"2026-06-03","v3.chen@samsung.com","16.0","9.0","3.0","6.0","0.0" +"2026-06-03","kim.sj@samsung.com","16.0","11.0","3.0","8.0","0.0" +"2026-06-03","pooja.k@samsung.com","16.0","6.0","2.0","4.0","0.0" +"2026-06-03","joyston.f@samsung.com","15.0","15.0","1.0","14.0","0.0" +"2026-06-03","anshu.makkar@samsung.com","15.0","16.0","0.0","16.0","0.0" +"2026-06-03","v.saucedo@samsung.com","15.0","6.0","3.0","3.0","0.0" +"2026-06-03","ashwin.v@samsung.com","15.0","9.0","2.0","7.0","0.0" +"2026-06-03","dasari.l@samsung.com","14.0","14.0","1.0","13.0","0.0" +"2026-06-03","aman.barya@samsung.com","14.0","11.0","0.0","11.0","0.0" +"2026-06-03","y.li10@samsung.com","14.0","14.0","2.0","11.0","0.0" +"2026-06-03","cost-agent-dev-bedrock-role-bs48gl","14.0","2.0","0.0","0.0","0.0" +"2026-06-03","j.morrill@samsung.com","14.0","10.0","1.0","9.0","0.0" +"2026-06-03","z.magray@samsung.com","14.0","11.0","1.0","10.0","0.0" +"2026-06-03","chirag.chauhan@samsung.com","14.0","12.0","1.0","12.0","0.0" +"2026-06-03","vivek.das@samsung.com","14.0","6.0","1.0","5.0","0.0" +"2026-06-03","jui.shah@samsung.com","14.0","9.0","1.0","8.0","0.0" +"2026-06-03","lei1.liu@samsung.com","14.0","13.0","1.0","12.0","0.0" +"2026-06-03","sehwan.noh@samsung.com","13.0","19.0","0.0","19.0","0.0" +"2026-06-03","swetha.gb@samsung.com","13.0","6.0","1.0","4.0","0.0" +"2026-06-03","devesh.g@samsung.com","13.0","8.0","1.0","7.0","0.0" +"2026-06-03","d.suresh@partner.samsung.com","13.0","11.0","2.0","9.0","0.0" +"2026-06-03","f.tarlan@samsung.com","13.0","12.0","1.0","11.0","0.0" +"2026-06-03","abhishek.pd@samsung.com","13.0","24.0","1.0","23.0","0.0" +"2026-06-03","p.ricciardi@samsung.com","13.0","15.0","1.0","14.0","0.0" +"2026-06-03","pi_audience_agentic_ai","13.0","5.0","0.0","3.0","0.0" +"2026-06-03","m.hyman@samsung.com","13.0","9.0","2.0","6.0","0.0" +"2026-06-03","j3.martin@samsung.com","13.0","26.0","1.0","25.0","0.0" +"2026-06-03","weidong5.w@samsung.com","12.0","11.0","0.0","11.0","0.0" +"2026-06-03","akash.b2@samsung.com","12.0","11.0","1.0","10.0","0.0" +"2026-06-03","y1.chen@partner.samsung.com","12.0","8.0","2.0","7.0","0.0" +"2026-06-03","a.dhanens@samsung.com","12.0","6.0","1.0","6.0","0.0" +"2026-06-03","emily.chen@samsung.com","12.0","21.0","1.0","20.0","0.0" +"2026-06-03","elena.piech@samsung.com","12.0","5.0","2.0","3.0","0.0" +"2026-06-03","h.evanoff@partner.samsung.com","12.0","15.0","1.0","13.0","0.0" +"2026-06-03","zane.z@samsung.com","12.0","8.0","1.0","7.0","0.0" +"2026-06-03","a.shakya@samsung.com","11.0","9.0","0.0","8.0","0.0" +"2026-06-03","yashwanth.k1@partner.samsung.com","11.0","5.0","1.0","3.0","0.0" +"2026-06-03","k.fraczek@samsung.com","11.0","6.0","2.0","4.0","0.0" +"2026-06-03","xiaoting.li@samsung.com","11.0","3.0","2.0","2.0","0.0" +"2026-06-03","k.dakarapu@samsung.com","11.0","13.0","2.0","11.0","0.0" +"2026-06-03","saransh.p@samsung.com","11.0","10.0","1.0","9.0","0.0" +"2026-06-03","ritesh.k@samsung.com","11.0","10.0","1.0","10.0","0.0" +"2026-06-03","a.gromova@samsung.com","11.0","13.0","2.0","11.0","0.0" +"2026-06-03","r.paragbhai@samsung.com","11.0","12.0","1.0","11.0","0.0" +"2026-06-03","pagalavan.s1@samsung.com","11.0","7.0","1.0","6.0","0.0" +"2026-06-03","sumeet.a@samsung.com","11.0","12.0","0.0","11.0","0.0" +"2026-06-03","g.murad@samsung.com","10.0","11.0","1.0","10.0","0.0" +"2026-06-03","nitish.yadav@samsung.com","10.0","5.0","1.0","4.0","0.0" +"2026-06-03","s.deepthi@samsung.com","10.0","7.0","2.0","5.0","0.0" +"2026-06-03","akshat.8797@samsung.com","10.0","6.0","0.0","5.0","0.0" +"2026-06-03","swati.s8@samsung.com","10.0","12.0","1.0","11.0","0.0" +"2026-06-03","r.nadimpalli@partner.samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-03","a.sekowski@samsung.com","10.0","19.0","1.0","18.0","0.0" +"2026-06-03","a.conner@samsung.com","10.0","13.0","1.0","11.0","0.0" +"2026-06-03","l.rawson@samsung.com","10.0","12.0","1.0","11.0","0.0" +"2026-06-03","i.barrios1@samsung.com","10.0","9.0","1.0","8.0","0.0" +"2026-06-03","m.kandikonda@samsung.com","10.0","5.0","2.0","3.0","0.0" +"2026-06-03","a12.li@partner.samsung.com","10.0","6.0","1.0","4.0","0.0" +"2026-06-03","david.gaffin@samsung.com","10.0","8.0","2.0","6.0","0.0" +"2026-06-03","m.khomenkov@samsung.com","10.0","12.0","1.0","12.0","0.0" +"2026-06-03","m.abhinav@samsung.com","10.0","11.0","2.0","9.0","0.0" +"2026-06-03","sangeeta.1@samsung.com","10.0","6.0","1.0","4.0","0.0" +"2026-06-03","sugir.tharaj@samsung.com","10.0","24.0","1.0","23.0","0.0" +"2026-06-03","aman.goel@samsung.com","10.0","21.0","1.0","20.0","0.0" +"2026-06-03","shiv.mehla@samsung.com","9.0","12.0","1.0","11.0","0.0" +"2026-06-03","varun.rawat@samsung.com","9.0","7.0","1.0","6.0","0.0" +"2026-06-03","niyanta.z@samsung.com","9.0","18.0","1.0","18.0","0.0" +"2026-06-03","sasitharan.r@samsung.com","9.0","13.0","1.0","13.0","0.0" +"2026-06-03","m.adnan25@samsung.com","9.0","11.0","1.0","10.0","0.0" +"2026-06-03","r.babinski@samsung.com","9.0","14.0","1.0","13.0","0.0" +"2026-06-03","a.ambalavana@samsung.com","9.0","10.0","0.0","9.0","0.0" +"2026-06-03","hr.shinde@samsung.com","9.0","14.0","1.0","13.0","0.0" +"2026-06-03","yt.chien@partner.samsung.com","8.0","7.0","1.0","5.0","0.0" +"2026-06-03","ashley.koett@samsung.com","8.0","11.0","0.0","10.0","0.0" +"2026-06-03","rakshith.r@samsung.com","8.0","7.0","0.0","6.0","0.0" +"2026-06-03","shreedhar.c@samsung.com","8.0","6.0","0.0","6.0","0.0" +"2026-06-03","nishchay.k@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-03","ramprasad.d@samsung.com","8.0","8.0","1.0","7.0","0.0" +"2026-06-03","g.dagley@partner.samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-03","j.siddharth@samsung.com","8.0","3.0","1.0","3.0","0.0" +"2026-06-03","s.kangal@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-03","r.sikdar@samsung.com","8.0","3.0","1.0","2.0","0.0" +"2026-06-03","l.bienkowski@partner.samsung.com","8.0","7.0","0.0","7.0","0.0" +"2026-06-03","s.kolusu@samsung.com","8.0","4.0","1.0","4.0","0.0" +"2026-06-03","k.timoszenko@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-03","chloe.koo@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-03","antriksh.p@samsung.com","7.0","13.0","1.0","13.0","0.0" +"2026-06-03","david.golan@samsung.com","7.0","3.0","1.0","1.0","0.0" +"2026-06-03","sudarshan.p@samsung.com","7.0","10.0","1.0","9.0","0.0" +"2026-06-03","n.colberg@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-03","siddesh.a@samsung.com","7.0","2.0","1.0","1.0","0.0" +"2026-06-03","r.ghewari2@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-03","e.strong@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-03","sani.v@samsung.com","7.0","6.0","0.0","6.0","0.0" +"2026-06-03","sarat.c@partner.samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-03","h.marcellini@samsung.com","7.0","4.0","1.0","4.0","0.0" +"2026-06-03","hao2.gao@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-03","onur.y@samsung.com","7.0","2.0","1.0","1.0","0.0" +"2026-06-03","r2.deshpande@samsung.com","7.0","7.0","1.0","7.0","0.0" +"2026-06-03","abhijit.sh5@samsung.com","7.0","4.0","1.0","4.0","0.0" +"2026-06-03","d.skliarov@samsung.com","7.0","9.0","1.0","8.0","0.0" +"2026-06-03","arun.a@samsung.com","7.0","1.0","1.0","1.0","0.0" +"2026-06-03","vignesh.b@samsung.com","7.0","6.0","1.0","4.0","0.0" +"2026-06-03","md.asif@samsung.com","6.0","13.0","0.0","12.0","0.0" +"2026-06-03","sundar.siv@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-03","t.jain@samsung.com","6.0","10.0","1.0","10.0","0.0" +"2026-06-03","aditya.r@partner.samsung.com","6.0","4.0","1.0","2.0","0.0" +"2026-06-03","d.hu10@samsung.com","6.0","8.0","1.0","7.0","0.0" +"2026-06-03","satya.n@samsung.com","6.0","5.0","0.0","5.0","0.0" +"2026-06-03","ajay.guna@samsung.com","6.0","10.0","1.0","9.0","0.0" +"2026-06-03","sy13.kim@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-03","a.gruzman@samsung.com","6.0","8.0","0.0","8.0","0.0" +"2026-06-03","anshika.s@samsung.com","6.0","3.0","1.0","3.0","0.0" +"2026-06-03","zeyang.ye@samsung.com","6.0","5.0","0.0","5.0","0.0" +"2026-06-03","d.gorbenko@samsung.com","6.0","4.0","1.0","4.0","0.0" +"2026-06-03","ashish.tr@samsung.com","6.0","5.0","0.0","5.0","0.0" +"2026-06-03","kusha.pande@samsung.com","6.0","6.0","0.0","5.0","0.0" +"2026-06-03","anurag.c@samsung.com","6.0","5.0","0.0","5.0","0.0" +"2026-06-03","m.brenner@samsung.com","6.0","3.0","1.0","2.0","0.0" +"2026-06-03","rakesh.kr2@samsung.com","6.0","10.0","1.0","9.0","0.0" +"2026-06-03","sumit.m2@samsung.com","6.0","3.0","0.0","2.0","0.0" +"2026-06-03","c.kret@samsung.com","5.0","5.0","1.0","4.0","0.0" +"2026-06-03","m.wiacek2@samsung.com","5.0","1.0","1.0","0.0","0.0" +"2026-06-03","p3.lee@samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-03","g.nicolae@samsung.com","5.0","7.0","1.0","6.0","0.0" +"2026-06-03","chapman.c@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-03","saejun.cha@samsung.com","5.0","3.0","1.0","3.0","0.0" +"2026-06-03","bo1.sun@samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-03","t.zarod@samsung.com","5.0","2.0","1.0","2.0","0.0" +"2026-06-03","rishabh.m@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-03","a.sharma2@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-03","a3.paliwal@samsung.com","5.0","6.0","1.0","5.0","0.0" +"2026-06-03","p.palyska@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-03","praveen.kmr@samsung.com","5.0","5.0","1.0","4.0","0.0" +"2026-06-03","k.kokiec@samsung.com","4.0","1.0","0.0","1.0","0.0" +"2026-06-03","s.bodhe@partner.samsung.com","4.0","3.0","1.0","2.0","0.0" +"2026-06-03","s.zehnder@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-03","meet.gogri@samsung.com","4.0","6.0","1.0","5.0","0.0" +"2026-06-03","m.wolny@partner.samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-03","jared.katz@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-03","pashupati.r1@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-03","i.andriychuk@samsung.com","4.0","5.0","0.0","5.0","0.0" +"2026-06-03","vk.deshpande@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-03","s.kolakowski@samsung.com","4.0","1.0","1.0","0.0","0.0" +"2026-06-03","shelley.wu@samsung.com","4.0","1.0","0.0","0.0","0.0" +"2026-06-03","m.lewandows5@samsung.com","4.0","3.0","0.0","3.0","0.0" +"2026-06-03","m.matyszkowi@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-03","navneeth.n@partner.samsung.com","4.0","6.0","0.0","5.0","0.0" +"2026-06-03","mohnish.b1@samsung.com","4.0","1.0","0.0","0.0","0.0" +"2026-06-03","p1.singh@samsung.com","4.0","4.0","1.0","3.0","0.0" +"2026-06-03","m.nowacki3@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-03","l.stucky@partner.samsung.com","4.0","4.0","1.0","4.0","0.0" +"2026-06-03","p.stefanski@samsung.com","4.0","1.0","0.0","1.0","0.0" +"2026-06-03","piyush.sd@samsung.com","4.0","6.0","0.0","6.0","0.0" +"2026-06-03","r4.gupta@samsung.com","4.0","5.0","1.0","4.0","0.0" +"2026-06-03","sundar.b@samsung.com","4.0","5.0","1.0","4.0","0.0" +"2026-06-03","r.latortue@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-03","arpit.nigam@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-03","m.wojtas@partner.samsung.com","4.0","3.0","0.0","3.0","0.0" +"2026-06-03","alex.hole@samsung.com","4.0","5.0","0.0","4.0","0.0" +"2026-06-03","puneet.saini@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","zhen.jin@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-03","r.chittimall@partner.samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","nikhil.c2@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-03","varun.gupta1@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","m.aljawahiri@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-03","udit.smvdu@samsung.com","3.0","3.0","1.0","3.0","0.0" +"2026-06-03","vivek.singh1@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","sang.jun@partner.samsung.com","3.0","1.0","1.0","0.0","0.0" +"2026-06-03","a.gunukula@partner.samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-03","a.pentela@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-03","hilam.p@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","a.tolambiya@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-03","george1.kim@samsung.com","3.0","0.0","0.0","0.0","0.0" +"2026-06-03","chetan.d@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-03","e.ronquillo@partner.samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-03","g1.raju@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-03","t.sanyal@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-03","arun.km@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","r.monheit@samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-03","d.barouski@samsung.com","3.0","3.0","1.0","3.0","0.0" +"2026-06-03","j.dickerson@samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-03","debra2.es@samsung.com","3.0","6.0","0.0","5.0","0.0" +"2026-06-03","m.roslak@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-03","thakur.v@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","ramisetty.k@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-03","kiran.pk@partner.samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-03","priyanshu.j@samsung.com","3.0","5.0","0.0","4.0","0.0" +"2026-06-03","j.foltyn@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-03","sai.mamidi@partner.samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-03","kim.raymond@samsung.com","3.0","0.0","0.0","0.0","0.0" +"2026-06-03","anuj.ar@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-03","p.jaromin@partner.samsung.com","3.0","5.0","0.0","4.0","0.0" +"2026-06-03","rahul.jain1@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-03","g.becker@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-03","a2.huang@partner.samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-03","c.gault@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-03","j.gottlieb@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","rfp-automation-bedrock-role-j0bj2i","2.0","0.0","0.0","0.0","0.0" +"2026-06-03","loy.noronha@partner.samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-03","swarna.roy@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","edwardj.kim@partner.samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-03","g.callahan@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","k.pampati@partner.samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-03","lee.irene@partner.samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-03","sameer.dash@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","xiao.yang1@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","upadhyaya.r@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","haaris.khan@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","sreejit.r@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-03","adarsh.v@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","jia.xue@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","p.furtak@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","himanshu.t@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-03","ma.caballero@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","a.efremchev@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-03","vikas.magar@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-03","n.viswas@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-03","k.urbanski@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","irvin.low@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","a.daveiga@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-03","pk.shukla@samsung.com","2.0","4.0","0.0","3.0","0.0" +"2026-06-03","c.joshi@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-03","a.krasnikau@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","a.jordan2@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","n.srinidhi@samsung.com","2.0","5.0","0.0","5.0","0.0" +"2026-06-03","gongli.duan@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","z.stolarski@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-03","vincent.k@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-03","n.divya@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","w.lewandowsk@partner.samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","rajnish.k7@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-03","rajeev.r@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-03","m.kaliszewsk@partner.samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-03","rahul.p02@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-03","csup.hwang@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","k.repec@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","paul.k@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","e.thomas@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-03","p.borhade@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","vidhan.vijay@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","jasmeen.p@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","h.kaur1@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","aashish.g@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","e1.weber@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","khanna.amit@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","s.chaitanya@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-03","pi-model-contextual-bedrock-role-qbwj07","1.0","1578.0","0.0","0.0","290.0" +"2026-06-03","m.ferreira@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","v.narayanan@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","j.shih@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","johnson.l@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","li.li1@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","c4.song@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-03","neeraj.pk@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","b.borawski@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","rashi.a@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","dilraj.kaur@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","soni.sandeep@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","gamya.k@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","monika.sroha@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","anthonyj.5@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","mohan.lal@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","v.gifford@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","joseph.rice@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","ravali.b@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","a.andersson@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","s.kaul@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","a.yildirim2@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","srikanth.h@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-03","harsh.mt@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","mohd.aslam@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","mohd.khan@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","vish.jeet@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","kushaldeep.s@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","guru.v@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","thomas.h@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","c3.lee@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","tushar.bg@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","a.movsesyan@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","muthu.v1@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","litan.m@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","kk.anil@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","taehodev.lee@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","n.bangalore@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","tarun.sanjay@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","sibi.menon@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","antik.a@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","c.jakrachai@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","somnath.g@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","smruti.r@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","w1.huang@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","github-adgear-pacingd-go-pr-review","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","kim.daeun@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","sharmin.c@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","jayesh.asawa@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","bernard.feng@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","b.chau@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","a.choudhury@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-03","m.pavlovski@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","seokbaek.lee@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-03","y.rifai@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-03","k.gangadhara@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","jiu.jung@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","s.megha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","k.teichman@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","j.lovejoy@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","d2.tran@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","e.schweikert@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","daniel.ortiz@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","lalit.rawat@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","udayan.c@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","a.goldsby@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","j.bermudo@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","yahan.zhang@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","a.abbaraju@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","t2.lee@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","g.gelke@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","d.nejbauer@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","ss.kulkarni@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","5.0","0.0","0.0","0.0" +"2026-06-03","tarandeep.s3@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","r.ghewari@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","v.edragas@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","p.marciniak2@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","mahesh.rk@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","himanshu.b@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-03","daniel.cox@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","m1.agarwal@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","amit.rodge@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","g.nagori@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","bhardwaj.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","aditya.verma@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","l.haynes@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","l.sutkowski@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","semantic_bot","0.0","1.0","0.0","1.0","0.0" +"2026-06-03","umesh.km@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","m.agosto@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","skumar.singh@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","linda.chu@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","dayita.s@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","m.kurcz@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","ankitha.r@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","seohyoung.ha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","aman.c@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","girija.m1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","mengya.wei@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","c.banchek@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","pi-creative-scoring-bedrock-role-4msq5g","0.0","22.0","0.0","0.0","1.0" +"2026-06-03","juchieh.l@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-03","megan.dotson@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","ryan.ward@samsung.com","453.0","72.0","64.0","7.0","0.0" +"2026-06-02","p.jarosz@partner.samsung.com","325.0","628.0","9.0","193.0","12.0" +"2026-06-02","b.woodcock@partner.samsung.com","279.0","43.0","0.0","1.0","0.0" +"2026-06-02","a.jeong1@samsung.com","278.0","359.0","12.0","347.0","0.0" +"2026-06-02","yashwanth.k1@partner.samsung.com","219.0","186.0","19.0","164.0","0.0" +"2026-06-02","amit.wagh@samsung.com","200.0","249.0","9.0","240.0","0.0" +"2026-06-02","c.borins@samsung.com","197.0","146.0","17.0","129.0","0.0" +"2026-06-02","g.brancovici@samsung.com","194.0","224.0","0.0","218.0","0.0" +"2026-06-02","yi5.han@samsung.com","186.0","133.0","16.0","115.0","0.0" +"2026-06-02","tom.sella@samsung.com","162.0","114.0","14.0","95.0","1.0" +"2026-06-02","vaibhav.t1@samsung.com","152.0","157.0","6.0","147.0","1.0" +"2026-06-02","b.harr@samsung.com","148.0","221.0","0.0","218.0","0.0" +"2026-06-02","mohnish.b1@samsung.com","142.0","26.0","0.0","0.0","0.0" +"2026-06-02","onur.y@samsung.com","136.0","61.0","16.0","45.0","0.0" +"2026-06-02","c.westlund@partner.samsung.com","131.0","146.0","8.0","139.0","0.0" +"2026-06-02","litellm","127.0","24.0","1.0","1.0","0.0" +"2026-06-02","huijun.wu@samsung.com","126.0","337.0","0.0","336.0","0.0" +"2026-06-02","j.muppala@samsung.com","118.0","118.0","8.0","110.0","0.0" +"2026-06-02","k.beatty@samsung.com","108.0","59.0","22.0","37.0","0.0" +"2026-06-02","yj5.lee@partner.samsung.com","103.0","79.0","9.0","71.0","0.0" +"2026-06-02","w1.freeman@samsung.com","98.0","128.0","0.0","126.0","0.0" +"2026-06-02","a.devappa@partner.samsung.com","96.0","106.0","7.0","98.0","0.0" +"2026-06-02","somsekhar.d1@samsung.com","93.0","197.0","7.0","190.0","0.0" +"2026-06-02","k.fraczek@samsung.com","80.0","79.0","9.0","64.0","0.0" +"2026-06-02","anusha.holla@samsung.com","79.0","36.0","9.0","27.0","0.0" +"2026-06-02","n.bertha@samsung.com","79.0","43.0","16.0","27.0","0.0" +"2026-06-02","j.pielaszkie@samsung.com","78.0","61.0","4.0","22.0","1.0" +"2026-06-02","t.judice@samsung.com","78.0","72.0","14.0","58.0","0.0" +"2026-06-02","brian.song@samsung.com","74.0","69.0","5.0","64.0","0.0" +"2026-06-02","v.saucedo@samsung.com","74.0","46.0","15.0","31.0","0.0" +"2026-06-02","c.sharad@samsung.com","72.0","51.0","7.0","45.0","0.0" +"2026-06-02","singh.satyam@samsung.com","70.0","40.0","5.0","33.0","0.0" +"2026-06-02","wh71.kim@samsung.com","70.0","64.0","5.0","59.0","0.0" +"2026-06-02","s.kashish@samsung.com","67.0","76.0","3.0","72.0","0.0" +"2026-06-02","sudipta.p@samsung.com","66.0","62.0","4.0","58.0","0.0" +"2026-06-02","t.teter@partner.samsung.com","65.0","35.0","7.0","28.0","0.0" +"2026-06-02","rajnish.k7@samsung.com","63.0","58.0","2.0","49.0","1.0" +"2026-06-02","s4.jeon@samsung.com","63.0","62.0","4.0","58.0","0.0" +"2026-06-02","sunit.m@samsung.com","62.0","35.0","6.0","29.0","0.0" +"2026-06-02","s.nakka@samsung.com","57.0","75.0","8.0","68.0","0.0" +"2026-06-02","princy.j@samsung.com","54.0","36.0","5.0","31.0","0.0" +"2026-06-02","AI-Campaign-Manager-bedrock-role-e5m5hh","54.0","4.0","0.0","0.0","2.0" +"2026-06-02","n.singhal@samsung.com","53.0","39.0","5.0","35.0","0.0" +"2026-06-02","r.fenochio@samsung.com","50.0","40.0","3.0","37.0","0.0" +"2026-06-02","b.smietanka@samsung.com","49.0","327.0","0.0","49.0","11.0" +"2026-06-02","j2.kumar@samsung.com","48.0","23.0","5.0","18.0","0.0" +"2026-06-02","meghana.k@samsung.com","46.0","60.0","2.0","59.0","0.0" +"2026-06-02","misha.jawa@samsung.com","46.0","34.0","4.0","30.0","0.0" +"2026-06-02","sourav.kshp@samsung.com","45.0","22.0","3.0","17.0","0.0" +"2026-06-02","t.abhinav@samsung.com","45.0","39.0","2.0","37.0","0.0" +"2026-06-02","ke.jin1@samsung.com","43.0","84.0","4.0","80.0","0.0" +"2026-06-02","davidj.jung@samsung.com","43.0","14.0","5.0","9.0","0.0" +"2026-06-02","j.sarallo@samsung.com","41.0","31.0","4.0","27.0","0.0" +"2026-06-02","c1.cabiati@samsung.com","41.0","61.0","5.0","56.0","0.0" +"2026-06-02","john.iburg@samsung.com","38.0","41.0","2.0","37.0","0.0" +"2026-06-02","rakesh.kr2@samsung.com","38.0","77.0","3.0","74.0","0.0" +"2026-06-02","aman.barya@samsung.com","38.0","42.0","0.0","41.0","0.0" +"2026-06-02","xinhui.shen@samsung.com","37.0","48.0","1.0","47.0","0.0" +"2026-06-02","j.ejsmund@samsung.com","37.0","39.0","5.0","34.0","0.0" +"2026-06-02","m.siddiquie@samsung.com","37.0","34.0","3.0","32.0","0.0" +"2026-06-02","zehua.z@samsung.com","36.0","26.0","1.0","22.0","0.0" +"2026-06-02","pavel.p@partner.samsung.com","36.0","45.0","2.0","43.0","0.0" +"2026-06-02","j3.martin@samsung.com","35.0","70.0","3.0","67.0","0.0" +"2026-06-02","yoshitha.p@samsung.com","35.0","22.0","2.0","17.0","0.0" +"2026-06-02","a.trivedy@samsung.com","34.0","16.0","3.0","12.0","0.0" +"2026-06-02","sourav.dutta@samsung.com","33.0","26.0","3.0","22.0","0.0" +"2026-06-02","chirag.chauhan@samsung.com","33.0","33.0","2.0","31.0","0.0" +"2026-06-02","c.joshi@samsung.com","33.0","39.0","2.0","37.0","0.0" +"2026-06-02","chi-chen.lo@samsung.com","33.0","28.0","3.0","24.0","0.0" +"2026-06-02","zane.z@samsung.com","33.0","55.0","2.0","52.0","0.0" +"2026-06-02","harsh.c@samsung.com","32.0","33.0","2.0","31.0","0.0" +"2026-06-02","shiv.mehla@samsung.com","32.0","36.0","2.0","33.0","0.0" +"2026-06-02","sugir.tharaj@samsung.com","32.0","63.0","4.0","59.0","0.0" +"2026-06-02","bo1.sun@samsung.com","31.0","8.0","4.0","4.0","0.0" +"2026-06-02","a.chaudhary@samsung.com","31.0","33.0","2.0","31.0","0.0" +"2026-06-02","m.barczyk@samsung.com","31.0","30.0","2.0","28.0","0.0" +"2026-06-02","s.sirigiredd@samsung.com","30.0","15.0","3.0","12.0","0.0" +"2026-06-02","a.tolambiya@samsung.com","30.0","31.0","2.0","28.0","0.0" +"2026-06-02","abhishek.pd@samsung.com","30.0","38.0","3.0","35.0","0.0" +"2026-06-02","jh0306.lim@samsung.com","29.0","11.0","2.0","7.0","0.0" +"2026-06-02","u.saboji@samsung.com","27.0","21.0","2.0","18.0","0.0" +"2026-06-02","a.pentela@samsung.com","27.0","53.0","2.0","51.0","0.0" +"2026-06-02","s.kolakowski@samsung.com","27.0","14.0","2.0","11.0","0.0" +"2026-06-02","a.gorthi@samsung.com","27.0","17.0","2.0","14.0","0.0" +"2026-06-02","saransh.p@samsung.com","27.0","17.0","2.0","15.0","0.0" +"2026-06-02","r.sikdar@samsung.com","26.0","12.0","3.0","10.0","0.0" +"2026-06-02","m.rachuba@samsung.com","26.0","17.0","2.0","15.0","0.0" +"2026-06-02","m.kochanczyk@samsung.com","26.0","22.0","2.0","20.0","0.0" +"2026-06-02","abhishek.km3@samsung.com","26.0","23.0","2.0","21.0","0.0" +"2026-06-02","s.dhanka@samsung.com","26.0","20.0","2.0","17.0","0.0" +"2026-06-02","r.nadimpalli@partner.samsung.com","25.0","21.0","2.0","19.0","0.0" +"2026-06-02","aravind.s5@samsung.com","25.0","48.0","2.0","46.0","0.0" +"2026-06-02","n.viswas@samsung.com","24.0","5.0","0.0","0.0","0.0" +"2026-06-02","r.vishnumurt@samsung.com","24.0","27.0","1.0","26.0","0.0" +"2026-06-02","shuyang.y@samsung.com","24.0","12.0","3.0","9.0","0.0" +"2026-06-02","sachin.r@samsung.com","24.0","31.0","2.0","29.0","0.0" +"2026-06-02","anshika.s@samsung.com","23.0","13.0","3.0","10.0","0.0" +"2026-06-02","sehwan.noh@samsung.com","23.0","34.0","0.0","33.0","0.0" +"2026-06-02","a.goldsby@partner.samsung.com","23.0","43.0","3.0","39.0","0.0" +"2026-06-02","pashupati.r1@samsung.com","22.0","13.0","2.0","11.0","0.0" +"2026-06-02","yahan.zhang@samsung.com","22.0","9.0","3.0","6.0","0.0" +"2026-06-02","varun.rawat@samsung.com","22.0","23.0","3.0","19.0","0.0" +"2026-06-02","anshu.makkar@samsung.com","22.0","22.0","0.0","22.0","0.0" +"2026-06-02","hyunwoo.s@samsung.com","21.0","20.0","2.0","19.0","0.0" +"2026-06-02","devesh.g@samsung.com","21.0","17.0","2.0","15.0","0.0" +"2026-06-02","k.seitllari@samsung.com","21.0","10.0","4.0","5.0","0.0" +"2026-06-02","m.wiacek2@samsung.com","21.0","17.0","2.0","15.0","0.0" +"2026-06-02","pooja.k@samsung.com","20.0","16.0","2.0","14.0","0.0" +"2026-06-02","v.jangir@samsung.com","20.0","12.0","2.0","10.0","0.0" +"2026-06-02","kr1.ayush@samsung.com","20.0","6.0","0.0","0.0","0.0" +"2026-06-02","thakur.v@samsung.com","20.0","18.0","1.0","17.0","0.0" +"2026-06-02","j.morrill@samsung.com","19.0","17.0","2.0","15.0","0.0" +"2026-06-02","m.khomenkov@samsung.com","19.0","23.0","1.0","22.0","0.0" +"2026-06-02","d.hu10@samsung.com","19.0","38.0","1.0","37.0","0.0" +"2026-06-02","r.parol@partner.samsung.com","19.0","19.0","1.0","18.0","0.0" +"2026-06-02","jui.shah@samsung.com","19.0","20.0","2.0","18.0","0.0" +"2026-06-02","s.sequeira@samsung.com","18.0","31.0","1.0","30.0","0.0" +"2026-06-02","s.kolusu@samsung.com","18.0","10.0","1.0","9.0","0.0" +"2026-06-02","kr.saurabh09@samsung.com","18.0","10.0","1.0","8.0","0.0" +"2026-06-02","vinay.gali@samsung.com","17.0","27.0","2.0","26.0","0.0" +"2026-06-02","m.wolny@partner.samsung.com","17.0","4.0","2.0","2.0","0.0" +"2026-06-02","a.movsesyan@samsung.com","17.0","14.0","1.0","13.0","0.0" +"2026-06-02","m.brenner@samsung.com","17.0","15.0","1.0","14.0","0.0" +"2026-06-02","edwardj.kim@partner.samsung.com","17.0","5.0","2.0","3.0","0.0" +"2026-06-02","y3.jeong@samsung.com","17.0","17.0","1.0","16.0","0.0" +"2026-06-02","gs.suvarapu@samsung.com","16.0","12.0","1.0","9.0","0.0" +"2026-06-02","vignesh.b@samsung.com","16.0","10.0","2.0","7.0","0.0" +"2026-06-02","h.sonehara@samsung.com","16.0","10.0","1.0","8.0","0.0" +"2026-06-02","yt.chien@partner.samsung.com","16.0","23.0","2.0","21.0","0.0" +"2026-06-02","a.gromova@samsung.com","16.0","21.0","3.0","18.0","0.0" +"2026-06-02","siddesh.a@samsung.com","16.0","9.0","1.0","8.0","0.0" +"2026-06-02","lam.n@samsung.com","15.0","17.0","1.0","16.0","0.0" +"2026-06-02","v.atmuri@samsung.com","15.0","11.0","1.0","10.0","0.0" +"2026-06-02","l.spahn@samsung.com","15.0","10.0","1.0","9.0","0.0" +"2026-06-02","m.abhinav@samsung.com","15.0","12.0","1.0","10.0","0.0" +"2026-06-02","sani.v@samsung.com","15.0","15.0","1.0","14.0","0.0" +"2026-06-02","anirban.b3@samsung.com","15.0","18.0","1.0","17.0","0.0" +"2026-06-02","lee.irene@partner.samsung.com","15.0","4.0","0.0","0.0","0.0" +"2026-06-02","zeyang.ye@samsung.com","15.0","8.0","1.0","7.0","0.0" +"2026-06-02","harikrish.p@samsung.com","15.0","17.0","1.0","16.0","0.0" +"2026-06-02","thomas.h@samsung.com","14.0","15.0","2.0","13.0","0.0" +"2026-06-02","r.ghewari2@samsung.com","14.0","7.0","2.0","5.0","0.0" +"2026-06-02","j.dickerson@samsung.com","14.0","7.0","3.0","4.0","0.0" +"2026-06-02","cmutha.ks@samsung.com","14.0","6.0","2.0","5.0","0.0" +"2026-06-02","gg.rojas@samsung.com","14.0","3.0","2.0","1.0","0.0" +"2026-06-02","shabeer.p@samsung.com","14.0","8.0","1.0","7.0","0.0" +"2026-06-02","michena.metla@samsung.com","14.0","11.0","1.0","9.0","0.0" +"2026-06-02","r.jiang@samsung.com","14.0","11.0","2.0","9.0","0.0" +"2026-06-02","m.chomiak@samsung.com","14.0","17.0","2.0","15.0","0.0" +"2026-06-02","vikas.magar@samsung.com","14.0","33.0","1.0","32.0","0.0" +"2026-06-02","s.kangal@samsung.com","14.0","12.0","1.0","11.0","0.0" +"2026-06-02","daya.8@partner.samsung.com","13.0","12.0","1.0","11.0","0.0" +"2026-06-02","sapna.singh@samsung.com","13.0","18.0","2.0","16.0","0.0" +"2026-06-02","srinath.s1@samsung.com","13.0","1542.0","1.0","10.0","528.0" +"2026-06-02","m.wojtas@partner.samsung.com","13.0","10.0","1.0","9.0","0.0" +"2026-06-02","j.vishal@samsung.com","13.0","11.0","1.0","9.0","0.0" +"2026-06-02","subrat.kp@samsung.com","13.0","21.0","1.0","19.0","0.0" +"2026-06-02","m.wasniewski@partner.samsung.com","13.0","12.0","1.0","11.0","0.0" +"2026-06-02","j.siddharth@samsung.com","13.0","13.0","0.0","13.0","0.0" +"2026-06-02","saejun.cha@samsung.com","13.0","12.0","2.0","10.0","0.0" +"2026-06-02","e.martis@samsung.com","13.0","7.0","1.0","5.0","0.0" +"2026-06-02","taehodev.lee@samsung.com","13.0","4.0","0.0","0.0","0.0" +"2026-06-02","g.nicolae@samsung.com","12.0","17.0","1.0","16.0","0.0" +"2026-06-02","k.pampati@partner.samsung.com","12.0","4.0","2.0","2.0","0.0" +"2026-06-02","xiaoting.li@samsung.com","12.0","5.0","1.0","4.0","0.0" +"2026-06-02","swetha.gb@samsung.com","12.0","6.0","1.0","4.0","0.0" +"2026-06-02","m.koscielak@samsung.com","12.0","26.0","2.0","19.0","0.0" +"2026-06-02","khyeok.lee@samsung.com","12.0","11.0","1.0","10.0","0.0" +"2026-06-02","bernard.feng@samsung.com","12.0","2.0","0.0","2.0","0.0" +"2026-06-02","deepesh.d@samsung.com","12.0","12.0","1.0","11.0","0.0" +"2026-06-02","m.lewandows5@samsung.com","12.0","14.0","0.0","14.0","0.0" +"2026-06-02","vivek.das@samsung.com","12.0","5.0","1.0","4.0","0.0" +"2026-06-02","debra2.es@samsung.com","12.0","16.0","2.0","14.0","0.0" +"2026-06-02","g.nagori@samsung.com","12.0","4.0","0.0","3.0","0.0" +"2026-06-02","a.conner@samsung.com","12.0","19.0","1.0","18.0","0.0" +"2026-06-02","y1.chen@partner.samsung.com","11.0","8.0","2.0","7.0","0.0" +"2026-06-02","sindhu.d@partner.samsung.com","11.0","6.0","1.0","5.0","0.0" +"2026-06-02","navneeth.n@partner.samsung.com","11.0","16.0","1.0","15.0","0.0" +"2026-06-02","z.stolarski@samsung.com","11.0","17.0","1.0","16.0","0.0" +"2026-06-02","i.andriychuk@samsung.com","11.0","14.0","1.0","13.0","0.0" +"2026-06-02","ramprasad.d@samsung.com","11.0","8.0","1.0","7.0","0.0" +"2026-06-02","b.brodecki@partner.samsung.com","11.0","15.0","1.0","13.0","0.0" +"2026-06-02","a.yildirim2@samsung.com","11.0","11.0","0.0","11.0","0.0" +"2026-06-02","sundar.siv@samsung.com","11.0","8.0","1.0","7.0","0.0" +"2026-06-02","seokbaek.lee@samsung.com","11.0","11.0","0.0","10.0","0.0" +"2026-06-02","rakshith.r@samsung.com","10.0","9.0","0.0","8.0","0.0" +"2026-06-02","j.asofsky@partner.samsung.com","10.0","7.0","1.0","6.0","0.0" +"2026-06-02","akshat.8797@samsung.com","10.0","6.0","0.0","5.0","0.0" +"2026-06-02","k.dakarapu@samsung.com","10.0","14.0","1.0","12.0","0.0" +"2026-06-02","yeonju.ann@samsung.com","10.0","7.0","2.0","5.0","0.0" +"2026-06-02","sangeeta.1@samsung.com","10.0","7.0","1.0","5.0","0.0" +"2026-06-02","t1.park@samsung.com","10.0","5.0","1.0","4.0","0.0" +"2026-06-02","anurag.c@samsung.com","10.0","9.0","1.0","9.0","0.0" +"2026-06-02","patrick.ting@samsung.com","9.0","9.0","1.0","8.0","0.0" +"2026-06-02","sy13.kim@samsung.com","9.0","5.0","2.0","3.0","0.0" +"2026-06-02","lei1.liu@samsung.com","9.0","8.0","1.0","8.0","0.0" +"2026-06-02","meet.gogri@samsung.com","9.0","9.0","2.0","7.0","0.0" +"2026-06-02","r.paragbhai@samsung.com","9.0","9.0","1.0","8.0","0.0" +"2026-06-02","y.li10@samsung.com","9.0","7.0","2.0","5.0","0.0" +"2026-06-02","agam.jain@samsung.com","9.0","6.0","0.0","6.0","0.0" +"2026-06-02","vivek.singh1@samsung.com","9.0","8.0","1.0","7.0","0.0" +"2026-06-02","daniel.cox@samsung.com","8.0","12.0","1.0","11.0","0.0" +"2026-06-02","l.steigerwal@samsung.com","8.0","8.0","1.0","7.0","0.0" +"2026-06-02","l.bienkowski@partner.samsung.com","8.0","3.0","0.0","3.0","0.0" +"2026-06-02","z.magray@samsung.com","8.0","8.0","1.0","7.0","0.0" +"2026-06-02","d4.mishra@samsung.com","8.0","5.0","0.0","4.0","0.0" +"2026-06-02","jl.wilkinson@samsung.com","8.0","4.0","2.0","2.0","0.0" +"2026-06-02","shreedhar.c@samsung.com","8.0","9.0","0.0","8.0","0.0" +"2026-06-02","sarat.c@partner.samsung.com","8.0","7.0","1.0","6.0","0.0" +"2026-06-02","sumit.m2@samsung.com","8.0","5.0","0.0","4.0","0.0" +"2026-06-02","d.skliarov@samsung.com","8.0","18.0","1.0","17.0","0.0" +"2026-06-02","haoran.lu@samsung.com","8.0","3.0","1.0","2.0","0.0" +"2026-06-02","khandekar.s@samsung.com","8.0","3.0","1.0","2.0","0.0" +"2026-06-02","p.ricciardi@samsung.com","8.0","8.0","1.0","7.0","0.0" +"2026-06-02","inhyuk.jung@samsung.com","7.0","3.0","1.0","3.0","0.0" +"2026-06-02","kushaldeep.s@samsung.com","7.0","7.0","1.0","6.0","0.0" +"2026-06-02","r.rittenhous@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-02","george1.kim@samsung.com","7.0","1.0","1.0","0.0","0.0" +"2026-06-02","hilam.p@samsung.com","7.0","9.0","1.0","8.0","0.0" +"2026-06-02","girija.m1@samsung.com","7.0","15.0","1.0","14.0","0.0" +"2026-06-02","shujie.fan@samsung.com","7.0","4.0","1.0","4.0","0.0" +"2026-06-02","i.barrios1@samsung.com","7.0","7.0","0.0","6.0","0.0" +"2026-06-02","weidong5.w@samsung.com","7.0","6.0","0.0","6.0","0.0" +"2026-06-02","gupta.s@samsung.com","7.0","2.0","0.0","1.0","0.0" +"2026-06-02","vk.deshpande@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-02","h.marcellini@samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-02","m.adnan25@samsung.com","7.0","7.0","1.0","6.0","0.0" +"2026-06-02","mohit.sh3@samsung.com","7.0","11.0","1.0","10.0","0.0" +"2026-06-02","arun.km@samsung.com","7.0","2.0","1.0","1.0","0.0" +"2026-06-02","d.barouski@samsung.com","7.0","11.0","1.0","9.0","0.0" +"2026-06-02","vincent.k@samsung.com","7.0","13.0","1.0","12.0","0.0" +"2026-06-02","gaurav.garg1@samsung.com","7.0","3.0","1.0","3.0","0.0" +"2026-06-02","lingaraj.s@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-02","l.roderick@samsung.com","7.0","9.0","0.0","9.0","0.0" +"2026-06-02","p.olwert@samsung.com","7.0","2.0","0.0","1.0","0.0" +"2026-06-02","swati.s8@samsung.com","6.0","12.0","0.0","12.0","0.0" +"2026-06-02","priyanshu.j@samsung.com","6.0","11.0","1.0","10.0","0.0" +"2026-06-02","a.sekowski@samsung.com","6.0","10.0","1.0","9.0","0.0" +"2026-06-02","a.shakya@samsung.com","6.0","3.0","0.0","3.0","0.0" +"2026-06-02","seth.schortz@samsung.com","6.0","2.0","1.0","1.0","0.0" +"2026-06-02","n.colberg@samsung.com","6.0","2.0","1.0","1.0","0.0" +"2026-06-02","changmin.bae@samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-02","suresh.mgi@samsung.com","6.0","11.0","1.0","10.0","0.0" +"2026-06-02","konni.bunya@partner.samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-02","santhosh.s2@samsung.com","6.0","5.0","1.0","4.0","0.0" +"2026-06-02","n.srinidhi@samsung.com","6.0","15.0","0.0","14.0","0.0" +"2026-06-02","k.strojek@samsung.com","6.0","3.0","0.0","2.0","0.0" +"2026-06-02","manojit.roy@samsung.com","6.0","2.0","1.0","1.0","0.0" +"2026-06-02","a.efremchev@samsung.com","6.0","10.0","0.0","10.0","0.0" +"2026-06-02","a.choudhury@samsung.com","6.0","7.0","0.0","6.0","0.0" +"2026-06-02","samriddh.g@samsung.com","6.0","11.0","1.0","10.0","0.0" +"2026-06-02","dilraj.kaur@samsung.com","6.0","6.0","0.0","5.0","0.0" +"2026-06-02","nikhil.c2@samsung.com","6.0","5.0","1.0","4.0","0.0" +"2026-06-02","hayeon.song@samsung.com","6.0","6.0","1.0","6.0","0.0" +"2026-06-02","l.torgerson@partner.samsung.com","5.0","2.0","1.0","2.0","0.0" +"2026-06-02","r.babinski@samsung.com","5.0","9.0","0.0","9.0","0.0" +"2026-06-02","o3.martinez@partner.samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-02","e.persky@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-02","j.aniol@samsung.com","5.0","3.0","0.0","2.0","0.0" +"2026-06-02","erhu.he@samsung.com","5.0","4.0","0.0","4.0","0.0" +"2026-06-02","csup.hwang@samsung.com","5.0","3.0","1.0","2.0","0.0" +"2026-06-02","zh.kim@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-02","johnson.l@samsung.com","5.0","6.0","1.0","5.0","0.0" +"2026-06-02","elena.piech@samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-02","antriksh.p@samsung.com","5.0","11.0","0.0","10.0","0.0" +"2026-06-02","j.gottlieb@samsung.com","5.0","7.0","0.0","6.0","0.0" +"2026-06-02","c3.lee@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-02","kim.raymond@samsung.com","4.0","1.0","0.0","0.0","0.0" +"2026-06-02","a.sharma2@samsung.com","4.0","3.0","1.0","2.0","0.0" +"2026-06-02","nishchay.k@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-02","chetan.d@samsung.com","4.0","1.0","0.0","1.0","0.0" +"2026-06-02","y.rifai@partner.samsung.com","4.0","4.0","1.0","3.0","0.0" +"2026-06-02","t.zarod@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-02","t.sanyal@samsung.com","4.0","5.0","1.0","4.0","0.0" +"2026-06-02","satya.n@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-02","rahul.jain1@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-02","emily.chen@samsung.com","4.0","8.0","0.0","7.0","0.0" +"2026-06-02","d.kargapolov@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-02","nitish.yadav@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-02","m.agosto@samsung.com","4.0","4.0","0.0","4.0","0.0" +"2026-06-02","mohd.khan@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-02","m.roslak@samsung.com","4.0","5.0","0.0","4.0","0.0" +"2026-06-02","paul.k@samsung.com","4.0","8.0","0.0","8.0","0.0" +"2026-06-02","gongli.duan@samsung.com","4.0","4.0","1.0","3.0","0.0" +"2026-06-02","p.furtak@samsung.com","4.0","1.0","0.0","0.0","0.0" +"2026-06-02","k.urbanski@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-02","hosung.you@samsung.com","4.0","7.0","0.0","7.0","0.0" +"2026-06-02","m.witkowski@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-02","jiachuan.h@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-02","antik.a@samsung.com","4.0","5.0","1.0","4.0","0.0" +"2026-06-02","p.stefanski@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-02","d.toolan@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-02","l.kaminski2@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-02","shivraj.r@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-02","irvin.low@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-02","b.chau@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-02","n.bangalore@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","kiran.pk@partner.samsung.com","3.0","2.0","1.0","1.0","0.0" +"2026-06-02","a.ambalavana@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","aman.c@samsung.com","3.0","3.0","1.0","2.0","0.0" +"2026-06-02","aashish.g@samsung.com","3.0","2.0","1.0","2.0","0.0" +"2026-06-02","kookheon.kim@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-02","r4.gupta@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-02","e.ronquillo@partner.samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-02","a12.li@partner.samsung.com","3.0","4.0","1.0","3.0","0.0" +"2026-06-02","d2.tran@samsung.com","3.0","0.0","0.0","0.0","0.0" +"2026-06-02","e1.weber@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-02","srikanth.h@samsung.com","3.0","5.0","0.0","5.0","0.0" +"2026-06-02","nikita.g1@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-02","kk.anil@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","kim.daeun@partner.samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-02","somnath.g@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-02","m.nowacki3@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-02","woongji.kim@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-02","sreejit.r@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","r.chittimall@partner.samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-02","ashish.tr@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","s.danchenko@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-02","w1.huang@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","xiao.yang1@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","kusha.pande@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","l.rawson@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-02","tarun.sanjay@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-02","tarandeep.s3@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-02","rishabh.m@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-02","khanna.amit@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","g1.raju@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","muthu.v1@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","a.daveiga@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","riddhi.p@partner.samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-02","shelley.wu@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-02","akash.b2@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","linda.chu@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-02","i.stojkovic@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-02","litan.m@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","rahul.p02@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-02","n.ransohoff@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","udit.smvdu@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-02","pooja.sharma@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","ramisetty.k@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-02","hr.shinde@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","rah.agrawal@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","mahesh.rk@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","hao2.gao@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-02","niyanta.z@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","m.hyman@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","a3.paliwal@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","praveen.kmr@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","anuj.ar@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","g.dagley@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","kim.sj@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-02","a.puntambeka@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-02","hyungi.seong@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","r2.deshpande@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","y2.yan@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","c.gault@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","aditya.r@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","n2.mehta@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-02","sibi.menon@samsung.com","2.0","0.0","0.0","0.0","0.0" +"2026-06-02","ritesh.k@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","dasari.l@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","md.liyakat@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-02","l.xiong@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-02","kshitiz.p@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-02","eunice4.kim@samsung.com","2.0","1.0","1.0","1.0","0.0" +"2026-06-02","aditya.verma@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-02","a.orlov@samsung.com","2.0","4.0","0.0","4.0","0.0" +"2026-06-02","a.gunukula@partner.samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-02","k.gangadhara@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-02","sang.jun@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","mohan.lal@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","s.chiou@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","somonnoy.c@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","varun.gupta1@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","ns.tikare@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","at.lakshmi@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","guru.v@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","j.kowalski7@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","github-adgear-pacingd-go-pr-review","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","sundar.b@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","md.asif@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-02","dayita.s@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","a.richhariya@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","daniel.ortiz@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","e.thomas@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","v3.chen@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","rajeev.r@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","p3.lee@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","anthonyj.5@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","cost-agent-dev-bedrock-role-bs48gl","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","a.andersson@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","c.lewczyk@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","k.kulasinski@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","m.kandikonda@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","j.ouye@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","sameer.dash@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","m.aljawahiri@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","sudarshan.p@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","s.dynak@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","m.ferreira@partner.samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","satya.swarup@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","ankitha.r@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","a.arche@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","sharmin.c@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","vidhan.vijay@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","n.rosendall@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","ss.kulkarni@samsung.com","1.0","3.0","0.0","3.0","0.0" +"2026-06-02","s.kaul@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","d.gorbenko@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","g.murad@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","mengya.wei@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","j.bermudo@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","skumar.singh@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","monika.sroha@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","bhardwaj.a@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","mayank.singh@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","abhijit.sh5@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","dongd1.xu@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","jia.xue@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","chung.jade@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","haaris.khan@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","k.kokiec@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","f.tarlan@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","p.kwietniews@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","george.f@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","hari.shanker@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","jongyoun.han@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","p.borhade@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","a.jordan2@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","prashant.sg@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","pk.shukla@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","t.glorio@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","adarsh.v@samsung.com","1.0","0.0","0.0","0.0","1.0" +"2026-06-02","h.evanoff@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","pi_audience_agentic_ai","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","v.edragas@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","austine.m@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","gyeong.song@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-02","c.banchek@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-02","arul.a1@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-02","a.krasnikau@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-02","c.kret@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","akshay.5@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","himesha.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","4.0","0.0","0.0","0.0" +"2026-06-02","kishore.r@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","b.zwolinski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","li.li1@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","semantic_bot","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","k.pietrzyk2@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","mr.dareddy@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","p.marciniak2@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","m.vigliotti@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","k3.song@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","mirza.daud@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","engineering_ssp_dev","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","a.manikandan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","a.abbaraju@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","g.callahan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","upadhyaya.r@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","b.anehosur@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","apurvaa.vs@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","r.kalafate@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","bs0425.lee@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","m.wroblewsk2@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","lalit.rawat@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","r.stefanowic@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","k.madejek@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","justin.shin@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","jiu.jung@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","r.latortue@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","naveenk1.v@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","t.jain@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","c.jakrachai@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","tushar.bg@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","udayan.c@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","umesh.km@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","aman.goel@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","vikas.km@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","ma.caballero@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","g.deochakke@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","neeraj.pk@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","w.lewandowsk@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","swarna.roy@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","m.vandewal@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","g.gelke@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","joseph.rice@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","m.pavlovski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","s.bodhe@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","pi-creative-scoring-bedrock-role-4msq5g","0.0","337.0","0.0","0.0","11.0" +"2026-06-02","srujan.s@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","v.narayanan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-02","p.jaromin@partner.samsung.com","0.0","1.0","0.0","0.0","0.0" +"2026-06-02","h.szustak@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","c.borins@samsung.com","390.0","274.0","34.0","239.0","1.0" +"2026-06-01","onur.y@samsung.com","296.0","139.0","33.0","106.0","1.0" +"2026-06-01","p.jarosz@partner.samsung.com","254.0","288.0","14.0","274.0","0.0" +"2026-06-01","j.sarallo@samsung.com","206.0","201.0","15.0","186.0","0.0" +"2026-06-01","yi5.han@samsung.com","172.0","140.0","13.0","125.0","0.0" +"2026-06-01","ryan.ward@samsung.com","168.0","74.0","19.0","55.0","0.0" +"2026-06-01","b.woodcock@partner.samsung.com","167.0","67.0","22.0","42.0","0.0" +"2026-06-01","w1.freeman@samsung.com","158.0","237.0","0.0","235.0","0.0" +"2026-06-01","g.brancovici@samsung.com","149.0","181.0","0.0","177.0","0.0" +"2026-06-01","sourav.dutta@samsung.com","146.0","148.0","10.0","139.0","0.0" +"2026-06-01","n.rosendall@samsung.com","137.0","99.0","6.0","91.0","2.0" +"2026-06-01","litellm","133.0","44.0","1.0","23.0","0.0" +"2026-06-01","tom.sella@samsung.com","129.0","87.0","12.0","72.0","0.0" +"2026-06-01","a.jeong1@samsung.com","121.0","132.0","7.0","125.0","0.0" +"2026-06-01","s4.jeon@samsung.com","115.0","112.0","8.0","104.0","0.0" +"2026-06-01","brian.song@samsung.com","103.0","98.0","7.0","91.0","0.0" +"2026-06-01","mohnish.b1@samsung.com","102.0","18.0","0.0","0.0","0.0" +"2026-06-01","vaibhav.t1@samsung.com","97.0","98.0","6.0","91.0","0.0" +"2026-06-01","c.sharad@samsung.com","94.0","80.0","8.0","72.0","0.0" +"2026-06-01","yashwanth.k1@partner.samsung.com","89.0","63.0","9.0","53.0","0.0" +"2026-06-01","haoran.lu@samsung.com","87.0","35.0","3.0","16.0","0.0" +"2026-06-01","somsekhar.d1@samsung.com","83.0","164.0","7.0","157.0","0.0" +"2026-06-01","k.dakarapu@samsung.com","81.0","137.0","9.0","128.0","1.0" +"2026-06-01","chi-chen.lo@samsung.com","75.0","87.0","4.0","82.0","0.0" +"2026-06-01","s.dhanka@samsung.com","74.0","61.0","6.0","54.0","0.0" +"2026-06-01","singh.satyam@samsung.com","71.0","33.0","4.0","26.0","1.0" +"2026-06-01","anirban.b3@samsung.com","70.0","70.0","5.0","66.0","0.0" +"2026-06-01","j.muppala@samsung.com","70.0","70.0","5.0","65.0","0.0" +"2026-06-01","yj5.lee@partner.samsung.com","67.0","65.0","4.0","61.0","0.0" +"2026-06-01","wh71.kim@samsung.com","67.0","50.0","6.0","44.0","0.0" +"2026-06-01","seokbaek.lee@samsung.com","65.0","77.0","0.0","74.0","0.0" +"2026-06-01","aman.barya@samsung.com","64.0","63.0","1.0","59.0","0.0" +"2026-06-01","a.devappa@partner.samsung.com","55.0","24.0","7.0","17.0","0.0" +"2026-06-01","nikita.g1@samsung.com","54.0","52.0","3.0","48.0","0.0" +"2026-06-01","b.harr@samsung.com","53.0","54.0","0.0","51.0","0.0" +"2026-06-01","anuj.ar@samsung.com","51.0","70.0","1.0","69.0","0.0" +"2026-06-01","amit.wagh@samsung.com","50.0","64.0","2.0","62.0","0.0" +"2026-06-01","yoshitha.p@samsung.com","50.0","27.0","5.0","21.0","0.0" +"2026-06-01","shivraj.r@samsung.com","49.0","33.0","4.0","29.0","0.0" +"2026-06-01","sani.v@samsung.com","47.0","48.0","1.0","45.0","0.0" +"2026-06-01","anusha.holla@samsung.com","45.0","28.0","4.0","24.0","0.0" +"2026-06-01","hayeon.song@samsung.com","44.0","40.0","3.0","37.0","0.0" +"2026-06-01","m.kochanczyk@samsung.com","43.0","44.0","2.0","41.0","0.0" +"2026-06-01","a.trivedy@samsung.com","43.0","18.0","3.0","12.0","0.0" +"2026-06-01","davidj.jung@samsung.com","43.0","12.0","5.0","6.0","0.0" +"2026-06-01","c.westlund@partner.samsung.com","41.0","37.0","3.0","34.0","0.0" +"2026-06-01","t.judice@samsung.com","41.0","33.0","7.0","25.0","0.0" +"2026-06-01","j2.kumar@samsung.com","39.0","19.0","4.0","15.0","0.0" +"2026-06-01","r.paragbhai@samsung.com","39.0","41.0","2.0","39.0","0.0" +"2026-06-01","a.skurzewski@samsung.com","38.0","62.0","3.0","59.0","0.0" +"2026-06-01","chetan.d@samsung.com","38.0","21.0","4.0","17.0","0.0" +"2026-06-01","s.kashish@samsung.com","37.0","45.0","1.0","44.0","0.0" +"2026-06-01","meghana.k@samsung.com","37.0","40.0","2.0","38.0","0.0" +"2026-06-01","j.pielaszkie@samsung.com","36.0","31.0","2.0","26.0","0.0" +"2026-06-01","misha.jawa@samsung.com","36.0","24.0","3.0","20.0","0.0" +"2026-06-01","v.jangir@samsung.com","35.0","19.0","4.0","15.0","0.0" +"2026-06-01","s.nakka@samsung.com","35.0","60.0","4.0","56.0","0.0" +"2026-06-01","AI-Campaign-Manager-bedrock-role-e5m5hh","35.0","3.0","0.0","0.0","2.0" +"2026-06-01","sugir.tharaj@samsung.com","35.0","75.0","4.0","71.0","0.0" +"2026-06-01","bellkar.r@samsung.com","34.0","29.0","4.0","26.0","0.0" +"2026-06-01","sunit.m@samsung.com","34.0","34.0","3.0","30.0","0.0" +"2026-06-01","johnson.l@samsung.com","33.0","28.0","3.0","25.0","0.0" +"2026-06-01","aditya.r@partner.samsung.com","32.0","3.0","1.0","2.0","0.0" +"2026-06-01","k.strojek@samsung.com","32.0","19.0","1.0","15.0","0.0" +"2026-06-01","saransh.p@samsung.com","31.0","23.0","3.0","20.0","0.0" +"2026-06-01","m.koscielak@samsung.com","31.0","58.0","6.0","42.0","0.0" +"2026-06-01","d4.mishra@samsung.com","30.0","20.0","0.0","19.0","0.0" +"2026-06-01","shuyang.y@samsung.com","30.0","21.0","3.0","18.0","0.0" +"2026-06-01","princy.j@samsung.com","30.0","35.0","3.0","32.0","0.0" +"2026-06-01","kr1.ayush@samsung.com","30.0","9.0","0.0","0.0","0.0" +"2026-06-01","thakur.v@samsung.com","30.0","31.0","2.0","29.0","0.0" +"2026-06-01","gaurav.garg1@samsung.com","30.0","22.0","3.0","19.0","0.0" +"2026-06-01","hyunwoo.s@samsung.com","28.0","24.0","2.0","21.0","0.0" +"2026-06-01","k.fraczek@samsung.com","28.0","8.0","4.0","4.0","0.0" +"2026-06-01","m.barczyk@samsung.com","28.0","20.0","2.0","18.0","0.0" +"2026-06-01","m.siddiquie@samsung.com","27.0","26.0","2.0","24.0","0.0" +"2026-06-01","jayesh.asawa@samsung.com","27.0","6126.0","2.0","18.0","184.0" +"2026-06-01","sehwan.noh@samsung.com","27.0","38.0","0.0","37.0","0.0" +"2026-06-01","s.kolakowski@samsung.com","26.0","15.0","2.0","12.0","0.0" +"2026-06-01","anshu.makkar@samsung.com","25.0","29.0","0.0","28.0","0.0" +"2026-06-01","e.martis@samsung.com","25.0","15.0","2.0","12.0","0.0" +"2026-06-01","r.fenochio@samsung.com","23.0","15.0","2.0","13.0","0.0" +"2026-06-01","sourav.kshp@samsung.com","23.0","12.0","2.0","9.0","0.0" +"2026-06-01","b.smietanka@samsung.com","23.0","168.0","0.0","6.0","6.0" +"2026-06-01","weidong5.w@samsung.com","22.0","24.0","0.0","24.0","0.0" +"2026-06-01","v.saucedo@samsung.com","22.0","15.0","4.0","10.0","0.0" +"2026-06-01","s.chaitanya@samsung.com","22.0","12.0","2.0","10.0","0.0" +"2026-06-01","c1.cabiati@samsung.com","22.0","20.0","3.0","17.0","0.0" +"2026-06-01","hao2.gao@samsung.com","22.0","24.0","3.0","21.0","0.0" +"2026-06-01","jiwooks.kim@samsung.com","22.0","20.0","3.0","18.0","0.0" +"2026-06-01","inhyuk.jung@samsung.com","22.0","24.0","2.0","22.0","0.0" +"2026-06-01","a.tolambiya@samsung.com","21.0","17.0","1.0","15.0","0.0" +"2026-06-01","ramprasad.d@samsung.com","21.0","13.0","3.0","11.0","0.0" +"2026-06-01","y1.chen@partner.samsung.com","21.0","16.0","3.0","13.0","0.0" +"2026-06-01","huijun.wu@samsung.com","20.0","30.0","0.0","29.0","0.0" +"2026-06-01","hyungi.seong@samsung.com","20.0","19.0","1.0","18.0","0.0" +"2026-06-01","devesh.g@samsung.com","20.0","5.0","3.0","3.0","0.0" +"2026-06-01","vk.deshpande@samsung.com","20.0","9.0","2.0","7.0","0.0" +"2026-06-01","sapna.singh@samsung.com","20.0","33.0","2.0","31.0","0.0" +"2026-06-01","n.viswas@samsung.com","19.0","4.0","0.0","0.0","0.0" +"2026-06-01","patrick.ting@samsung.com","19.0","19.0","1.0","18.0","0.0" +"2026-06-01","c.kret@samsung.com","19.0","9.0","2.0","7.0","0.0" +"2026-06-01","zehua.z@samsung.com","19.0","12.0","0.0","10.0","0.0" +"2026-06-01","jasmeen.p@samsung.com","19.0","7.0","1.0","5.0","0.0" +"2026-06-01","t.zarod@samsung.com","19.0","9.0","2.0","7.0","0.0" +"2026-06-01","t1.park@samsung.com","19.0","5.0","2.0","3.0","0.0" +"2026-06-01","n.ransohoff@samsung.com","19.0","11.0","3.0","7.0","0.0" +"2026-06-01","sumeet.a@samsung.com","19.0","22.0","2.0","20.0","0.0" +"2026-06-01","a.gromova@samsung.com","19.0","28.0","3.0","24.0","0.0" +"2026-06-01","aravind.s5@samsung.com","19.0","34.0","2.0","32.0","0.0" +"2026-06-01","t.abhinav@samsung.com","19.0","15.0","1.0","14.0","0.0" +"2026-06-01","chirag.chauhan@samsung.com","18.0","25.0","2.0","23.0","0.0" +"2026-06-01","vinay.gali@samsung.com","18.0","27.0","2.0","25.0","0.0" +"2026-06-01","sarat.c@partner.samsung.com","18.0","16.0","3.0","13.0","0.0" +"2026-06-01","j.morrill@samsung.com","18.0","14.0","1.0","13.0","0.0" +"2026-06-01","a.yildirim2@samsung.com","17.0","21.0","1.0","20.0","0.0" +"2026-06-01","r4.gupta@samsung.com","17.0","28.0","2.0","26.0","0.0" +"2026-06-01","b.gujjar@samsung.com","17.0","26.0","2.0","24.0","0.0" +"2026-06-01","t.teter@partner.samsung.com","17.0","13.0","1.0","12.0","0.0" +"2026-06-01","harikrish.p@samsung.com","17.0","13.0","1.0","12.0","0.0" +"2026-06-01","y.li10@samsung.com","16.0","16.0","2.0","14.0","0.0" +"2026-06-01","sangeeta.1@samsung.com","16.0","11.0","1.0","8.0","0.0" +"2026-06-01","lei1.liu@samsung.com","16.0","11.0","1.0","9.0","0.0" +"2026-06-01","rakshith.r@samsung.com","16.0","19.0","0.0","19.0","0.0" +"2026-06-01","suresh.mgi@samsung.com","15.0","24.0","2.0","22.0","0.0" +"2026-06-01","d.hu10@samsung.com","15.0","29.0","1.0","28.0","0.0" +"2026-06-01","g.prasal@partner.samsung.com","15.0","13.0","1.0","13.0","0.0" +"2026-06-01","kookheon.kim@samsung.com","15.0","24.0","2.0","21.0","0.0" +"2026-06-01","l.roderick@samsung.com","15.0","12.0","1.0","11.0","0.0" +"2026-06-01","girija.m1@samsung.com","15.0","24.0","2.0","22.0","0.0" +"2026-06-01","changmin.bae@samsung.com","15.0","12.0","2.0","10.0","0.0" +"2026-06-01","khyeok.lee@samsung.com","15.0","16.0","1.0","15.0","0.0" +"2026-06-01","u.saboji@samsung.com","15.0","6.0","2.0","4.0","0.0" +"2026-06-01","r.jiang@samsung.com","14.0","11.0","2.0","8.0","0.0" +"2026-06-01","k.beatty@samsung.com","14.0","6.0","3.0","3.0","0.0" +"2026-06-01","david.gaffin@samsung.com","14.0","11.0","3.0","8.0","0.0" +"2026-06-01","saejun.cha@samsung.com","14.0","10.0","2.0","7.0","0.0" +"2026-06-01","khandekar.s@samsung.com","14.0","7.0","2.0","4.0","0.0" +"2026-06-01","s.sirigiredd@samsung.com","14.0","15.0","1.0","14.0","0.0" +"2026-06-01","hs1214.lee@samsung.com","14.0","11.0","2.0","9.0","0.0" +"2026-06-01","h.evanoff@partner.samsung.com","14.0","16.0","2.0","14.0","0.0" +"2026-06-01","sam.morris@samsung.com","14.0","22.0","2.0","20.0","0.0" +"2026-06-01","jui.shah@samsung.com","14.0","16.0","1.0","15.0","0.0" +"2026-06-01","k.kokiec@samsung.com","13.0","7.0","0.0","6.0","0.0" +"2026-06-01","j3.martin@samsung.com","13.0","22.0","1.0","21.0","0.0" +"2026-06-01","h.sonehara@samsung.com","13.0","7.0","1.0","6.0","0.0" +"2026-06-01","m.roslak@samsung.com","13.0","13.0","1.0","12.0","0.0" +"2026-06-01","santhosh.s2@samsung.com","13.0","14.0","1.0","13.0","0.0" +"2026-06-01","erhu.he@samsung.com","13.0","11.0","1.0","10.0","0.0" +"2026-06-01","v3.chen@samsung.com","13.0","6.0","2.0","4.0","0.0" +"2026-06-01","a.dhanens@samsung.com","13.0","13.0","2.0","11.0","0.0" +"2026-06-01","harsh.c@samsung.com","13.0","7.0","1.0","6.0","0.0" +"2026-06-01","j.gorski2@samsung.com","13.0","22.0","2.0","21.0","0.0" +"2026-06-01","shabeer.p@samsung.com","13.0","6.0","1.0","4.0","0.0" +"2026-06-01","tarun.sanjay@samsung.com","12.0","8.0","1.0","7.0","0.0" +"2026-06-01","swetha.gb@samsung.com","12.0","4.0","1.0","3.0","0.0" +"2026-06-01","john.iburg@samsung.com","12.0","9.0","1.0","7.0","0.0" +"2026-06-01","m.kaliszewsk@partner.samsung.com","12.0","28.0","1.0","27.0","0.0" +"2026-06-01","k.bugala@samsung.com","12.0","6.0","1.0","4.0","0.0" +"2026-06-01","m.khomenkov@samsung.com","12.0","12.0","1.0","12.0","0.0" +"2026-06-01","cem.yilmaz@samsung.com","11.0","7.0","2.0","5.0","0.0" +"2026-06-01","d.gorbenko@samsung.com","11.0","13.0","1.0","13.0","0.0" +"2026-06-01","zh.kim@samsung.com","11.0","10.0","0.0","9.0","0.0" +"2026-06-01","l.steigerwal@samsung.com","11.0","38.0","1.0","11.0","0.0" +"2026-06-01","r.sikdar@samsung.com","11.0","8.0","1.0","7.0","0.0" +"2026-06-01","y3.jeong@samsung.com","11.0","11.0","0.0","11.0","0.0" +"2026-06-01","k.seitllari@samsung.com","11.0","5.0","2.0","3.0","0.0" +"2026-06-01","taehodev.lee@samsung.com","11.0","4.0","0.0","0.0","0.0" +"2026-06-01","r.stefanowic@samsung.com","11.0","20.0","1.0","18.0","0.0" +"2026-06-01","s.kolusu@samsung.com","11.0","8.0","1.0","7.0","0.0" +"2026-06-01","hosung.you@samsung.com","11.0","17.0","1.0","16.0","0.0" +"2026-06-01","rajnish.k7@samsung.com","11.0","9.0","1.0","6.0","0.0" +"2026-06-01","j.vishal@samsung.com","11.0","21.0","1.0","20.0","0.0" +"2026-06-01","kushaldeep.s@samsung.com","11.0","14.0","2.0","13.0","0.0" +"2026-06-01","sumit.m2@samsung.com","10.0","7.0","1.0","6.0","0.0" +"2026-06-01","thomas.h@samsung.com","10.0","15.0","1.0","13.0","0.0" +"2026-06-01","m.boriya@samsung.com","10.0","8.0","1.0","7.0","0.0" +"2026-06-01","abhishek.km3@samsung.com","10.0","8.0","1.0","7.0","0.0" +"2026-06-01","ke.jin1@samsung.com","10.0","15.0","1.0","13.0","0.0" +"2026-06-01","bo1.sun@samsung.com","10.0","3.0","2.0","2.0","0.0" +"2026-06-01","a.goldsby@partner.samsung.com","10.0","18.0","2.0","15.0","0.0" +"2026-06-01","arul.a1@partner.samsung.com","10.0","7.0","2.0","6.0","0.0" +"2026-06-01","m.brenner@samsung.com","10.0","7.0","1.0","6.0","0.0" +"2026-06-01","r.babinski@samsung.com","10.0","16.0","1.0","15.0","0.0" +"2026-06-01","anurag.c@samsung.com","10.0","9.0","1.0","9.0","0.0" +"2026-06-01","pagalavan.s1@samsung.com","9.0","5.0","1.0","4.0","0.0" +"2026-06-01","seth.schortz@samsung.com","9.0","4.0","2.0","3.0","0.0" +"2026-06-01","elena.piech@samsung.com","9.0","4.0","2.0","3.0","0.0" +"2026-06-01","jh0306.lim@samsung.com","9.0","3.0","1.0","2.0","0.0" +"2026-06-01","gg.rojas@samsung.com","9.0","7.0","1.0","6.0","0.0" +"2026-06-01","p.stefanski@samsung.com","9.0","8.0","1.0","7.0","0.0" +"2026-06-01","jia.xue@samsung.com","9.0","9.0","0.0","8.0","0.0" +"2026-06-01","n.bertha@samsung.com","9.0","4.0","2.0","2.0","0.0" +"2026-06-01","i.barrios1@samsung.com","9.0","9.0","1.0","8.0","0.0" +"2026-06-01","k.madejek@partner.samsung.com","9.0","7.0","1.0","6.0","0.0" +"2026-06-01","zane.z@samsung.com","9.0","10.0","1.0","10.0","0.0" +"2026-06-01","sundar.siv@samsung.com","8.0","11.0","0.0","10.0","0.0" +"2026-06-01","t.sanyal@samsung.com","8.0","12.0","1.0","12.0","0.0" +"2026-06-01","c.lewczyk@samsung.com","8.0","2.0","1.0","1.0","0.0" +"2026-06-01","m.rachuba@samsung.com","8.0","3.0","1.0","2.0","0.0" +"2026-06-01","a.movsesyan@samsung.com","8.0","4.0","1.0","3.0","0.0" +"2026-06-01","dasari.l@samsung.com","8.0","5.0","1.0","5.0","0.0" +"2026-06-01","z.stolarski@samsung.com","8.0","19.0","0.0","19.0","0.0" +"2026-06-01","a.pentela@samsung.com","8.0","11.0","1.0","10.0","0.0" +"2026-06-01","c.resnick@samsung.com","8.0","11.0","1.0","10.0","0.0" +"2026-06-01","satya.n@samsung.com","8.0","2.0","0.0","1.0","0.0" +"2026-06-01","udayan.c@samsung.com","8.0","10.0","1.0","9.0","0.0" +"2026-06-01","c4.song@samsung.com","8.0","6.0","1.0","5.0","0.0" +"2026-06-01","konni.bunya@partner.samsung.com","8.0","13.0","0.0","13.0","0.0" +"2026-06-01","f.tarlan@samsung.com","7.0","5.0","1.0","5.0","0.0" +"2026-06-01","m.wasniewski@partner.samsung.com","7.0","8.0","1.0","7.0","0.0" +"2026-06-01","varun.rawat@samsung.com","7.0","7.0","1.0","5.0","0.0" +"2026-06-01","irvin.low@samsung.com","7.0","6.0","0.0","5.0","0.0" +"2026-06-01","n.srinidhi@samsung.com","7.0","11.0","1.0","10.0","0.0" +"2026-06-01","a.chaudhary@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-01","anshika.s@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-01","rishabh.m@samsung.com","7.0","6.0","1.0","5.0","0.0" +"2026-06-01","j.bermudo@samsung.com","7.0","5.0","0.0","4.0","0.0" +"2026-06-01","sudarshan.p@samsung.com","7.0","5.0","1.0","4.0","0.0" +"2026-06-01","k.gangadhara@partner.samsung.com","7.0","3.0","1.0","2.0","0.0" +"2026-06-01","vincent.k@samsung.com","7.0","10.0","1.0","9.0","0.0" +"2026-06-01","george1.kim@samsung.com","7.0","1.0","1.0","0.0","0.0" +"2026-06-01","a.efremchev@samsung.com","7.0","7.0","0.0","6.0","0.0" +"2026-06-01","g.becker@samsung.com","7.0","6.0","0.0","6.0","0.0" +"2026-06-01","dongd1.xu@samsung.com","7.0","4.0","1.0","3.0","0.0" +"2026-06-01","a.sekowski@samsung.com","6.0","8.0","1.0","7.0","0.0" +"2026-06-01","a.gorthi@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-01","g.dagley@partner.samsung.com","6.0","5.0","1.0","4.0","0.0" +"2026-06-01","edwardj.kim@partner.samsung.com","6.0","1.0","1.0","0.0","0.0" +"2026-06-01","bohdan.v@samsung.com","6.0","5.0","0.0","4.0","0.0" +"2026-06-01","vivek.das@samsung.com","6.0","7.0","1.0","6.0","0.0" +"2026-06-01","kiran.pk@partner.samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-01","d.kargapolov@samsung.com","6.0","6.0","1.0","5.0","0.0" +"2026-06-01","pavel.p@partner.samsung.com","6.0","6.0","1.0","5.0","0.0" +"2026-06-01","g.murad@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-01","jongyoun.han@samsung.com","6.0","5.0","1.0","4.0","0.0" +"2026-06-01","srinath.s1@samsung.com","6.0","5.0","0.0","5.0","0.0" +"2026-06-01","gs.suvarapu@samsung.com","6.0","4.0","0.0","3.0","0.0" +"2026-06-01","pashupati.r1@samsung.com","6.0","4.0","0.0","4.0","0.0" +"2026-06-01","m.adnan25@samsung.com","6.0","4.0","1.0","3.0","0.0" +"2026-06-01","a.daveiga@samsung.com","6.0","4.0","1.0","2.0","0.0" +"2026-06-01","p3.lee@samsung.com","6.0","4.0","0.0","3.0","0.0" +"2026-06-01","csup.hwang@samsung.com","6.0","6.0","0.0","5.0","0.0" +"2026-06-01","seohyoung.ha@samsung.com","5.0","2.0","1.0","1.0","0.0" +"2026-06-01","joseph.rice@samsung.com","5.0","5.0","1.0","4.0","0.0" +"2026-06-01","c.joshi@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-01","y2.yan@samsung.com","5.0","7.0","0.0","6.0","0.0" +"2026-06-01","akash.b2@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-01","s.chiou@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-01","m.kandikonda@samsung.com","5.0","2.0","1.0","2.0","0.0" +"2026-06-01","narendra.p@samsung.com","5.0","5.0","0.0","5.0","0.0" +"2026-06-01","samriddh.g@samsung.com","5.0","10.0","0.0","9.0","0.0" +"2026-06-01","navneeth.n@partner.samsung.com","5.0","8.0","1.0","7.0","0.0" +"2026-06-01","d.barouski@samsung.com","5.0","9.0","1.0","9.0","0.0" +"2026-06-01","j.ejsmund@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-01","w1.huang@samsung.com","5.0","3.0","0.0","2.0","0.0" +"2026-06-01","a.richhariya@samsung.com","5.0","8.0","1.0","7.0","0.0" +"2026-06-01","debra2.es@samsung.com","5.0","4.0","1.0","3.0","0.0" +"2026-06-01","kusha.pande@samsung.com","5.0","4.0","0.0","3.0","0.0" +"2026-06-01","a.orlov@samsung.com","5.0","10.0","0.0","10.0","0.0" +"2026-06-01","shreedhar.c@samsung.com","5.0","3.0","0.0","3.0","0.0" +"2026-06-01","sreejit.r@samsung.com","5.0","4.0","1.0","4.0","0.0" +"2026-06-01","aman.c@samsung.com","4.0","2.0","1.0","1.0","0.0" +"2026-06-01","subrat.kp@samsung.com","4.0","4.0","1.0","4.0","0.0" +"2026-06-01","anthonyj.5@samsung.com","4.0","6.0","1.0","6.0","0.0" +"2026-06-01","ashwin.v@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-01","mohd.khan@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-01","yt.chien@partner.samsung.com","4.0","3.0","1.0","3.0","0.0" +"2026-06-01","sasitharan.r@samsung.com","4.0","9.0","0.0","8.0","0.0" +"2026-06-01","a.choudhury@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-01","cmutha.ks@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-01","manojit.roy@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-01","sachin.r@samsung.com","4.0","3.0","0.0","2.0","0.0" +"2026-06-01","a.conner@samsung.com","4.0","5.0","1.0","5.0","0.0" +"2026-06-01","eunice4.kim@samsung.com","4.0","1.0","1.0","1.0","0.0" +"2026-06-01","a.shakya@samsung.com","4.0","2.0","0.0","1.0","0.0" +"2026-06-01","b.brodecki@partner.samsung.com","4.0","5.0","0.0","4.0","0.0" +"2026-06-01","srujan.s@partner.samsung.com","4.0","3.0","1.0","3.0","0.0" +"2026-06-01","agam.jain@samsung.com","4.0","2.0","0.0","2.0","0.0" +"2026-06-01","somonnoy.c@samsung.com","4.0","6.0","0.0","5.0","0.0" +"2026-06-01","h.marcellini@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-01","h.szustak@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-01","david.golan@samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-01","ashish.tr@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","l.spahn@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","github-adgear-pacingd-go-pr-review","3.0","2.0","0.0","1.0","0.0" +"2026-06-01","s.bodhe@partner.samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","bernard.feng@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","abhijit.sh5@samsung.com","3.0","3.0","0.0","2.0","0.0" +"2026-06-01","n.bangalore@samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-01","kishore.r@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","meet.gogri@samsung.com","3.0","5.0","0.0","4.0","0.0" +"2026-06-01","r.ghewari2@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-01","sameer.dash@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","riddhi.p@partner.samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-01","m.wojtas@partner.samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","l.xiong@samsung.com","3.0","4.0","0.0","4.0","0.0" +"2026-06-01","arpit.nigam@samsung.com","3.0","4.0","1.0","3.0","0.0" +"2026-06-01","n2.mehta@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-01","yeonju.ann@samsung.com","3.0","3.0","0.0","3.0","0.0" +"2026-06-01","e1.weber@samsung.com","3.0","2.0","1.0","2.0","0.0" +"2026-06-01","mohd.aslam@samsung.com","3.0","1.0","0.0","1.0","0.0" +"2026-06-01","nitish.yadav@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-01","hr.shinde@samsung.com","3.0","4.0","1.0","3.0","0.0" +"2026-06-01","emily.chen@samsung.com","3.0","5.0","0.0","5.0","0.0" +"2026-06-01","linda.chu@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-01","akshat.8797@samsung.com","3.0","1.0","0.0","0.0","0.0" +"2026-06-01","s.kangal@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-01","m.wiacek2@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-01","adarsh.v@samsung.com","3.0","6.0","0.0","6.0","0.0" +"2026-06-01","m.abhinav@samsung.com","3.0","2.0","0.0","1.0","0.0" +"2026-06-01","himanshu.t@samsung.com","3.0","1.0","1.0","1.0","0.0" +"2026-06-01","k.pampati@partner.samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-01","d.toolan@samsung.com","3.0","2.0","0.0","2.0","0.0" +"2026-06-01","shelley.wu@samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-01","s.murugesan@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","i.meldere@samsung.com","2.0","4.0","0.0","4.0","0.0" +"2026-06-01","j.ouye@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","kim.daeun@partner.samsung.com","2.0","1.0","0.0","0.0","0.0" +"2026-06-01","mr.dareddy@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","s2.lucas@partner.samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-01","g.nagori@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","praveen.kmr@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","i.andriychuk@samsung.com","2.0","3.0","0.0","2.0","0.0" +"2026-06-01","s.deepthi@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-01","rahul.jain1@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","akhil.k@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","k.urbanski@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","vignesh.b@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","g.deochakke@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","george.f@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","sharmin.c@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","beth.padera@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","prashant.sg@samsung.com","2.0","3.0","0.0","3.0","0.0" +"2026-06-01","m.lepicki@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","pooja.sharma@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","nikhil.c2@samsung.com","2.0","2.0","0.0","1.0","0.0" +"2026-06-01","s.danchenko@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","nishchay.k@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","vibhav.3@samsung.com","2.0","2.0","0.0","2.0","0.0" +"2026-06-01","m.wolny@partner.samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","sundar.b@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","pi_audience_agentic_ai","2.0","1.0","0.0","0.0","0.0" +"2026-06-01","xiaoting.li@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","joyston.f@samsung.com","2.0","1.0","0.0","1.0","0.0" +"2026-06-01","r2.jain@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","rakesh.kr2@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","m.nowacki3@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","vidhan.vijay@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","r.kalafate@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","m.ferreira@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.szyszka@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","o3.martinez@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","d2.tran@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","a12.li@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","umesh.km@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","s.zehnder@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","ajay.guna@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","l.torgerson@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","ramisetty.k@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","anuj.bhatt@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","j.foltyn@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","kk.anil@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-01","deepesh.d@samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-01","g1.raju@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","r.nadimpalli@partner.samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","kr.saurabh09@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","gyeong.song@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","arun.a@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","m.pavlovski@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","rfp-automation-bedrock-role-j0bj2i","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","chung.jade@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","jl.wilkinson@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","r.latortue@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","daniel.ortiz@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","gongli.duan@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","claude-code-gha-role","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","m.witkowski@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","kim.sj@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","mohan.lal@samsung.com","1.0","2.0","0.0","1.0","0.0" +"2026-06-01","cost-agent-dev-bedrock-role-bs48gl","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","ravali.b@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","m.chomiak@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","xiao.yang1@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.parimi@partner.samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.krasnikau@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","t2.lee@samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","l.bienkowski@partner.samsung.com","1.0","1.0","0.0","0.0","0.0" +"2026-06-01","a.ambalavana@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","mengya.wei@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","dhruv.gaur@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","ritwik.c@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","arun.km@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.gunukula@partner.samsung.com","1.0","2.0","0.0","2.0","0.0" +"2026-06-01","varun.gupta1@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.andersson@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","r.vishnumurt@samsung.com","1.0","0.0","0.0","0.0","0.0" +"2026-06-01","sudipta.p@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","at.lakshmi@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","l.rawson@samsung.com","1.0","1.0","0.0","1.0","0.0" +"2026-06-01","jared.katz@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","apurvaa.vs@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","g.callahan@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","k.kulasinski@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","somnath.g@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","p1.singh@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","niyanta.z@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","v.narayanan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","r2.deshpande@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","tushar.bg@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","c.jakrachai@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","aditya.verma@samsung.com","0.0","1.0","0.0","0.0","0.0" +"2026-06-01","yating.w@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","khanna.amit@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","k.srodon@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","t.glorio@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","k.rafanelli@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","m.lamba@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","upadhyaya.r@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","jiachuan.h@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","vish.jeet@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","sujan.challa@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","r.parol@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","daniel.cox@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","semantic_bot","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","s.dynak@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","bhardwaj.a@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-01","p.kwietniews@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","k.duch@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","AmazonBedrockExecutionRoleForKnowledgeBase_ozwr5","0.0","5.0","0.0","0.0","0.0" +"2026-06-01","b.anehosur@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","pi-creative-scoring-bedrock-role-4msq5g","0.0","441.0","0.0","0.0","15.0" +"2026-06-01","s.katarkandi@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.manikandan@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","e.thomas@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.arche@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","sinha.shalu@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","j4.scanlon@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","woongji.kim@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","monika.sroha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","engineering_ssp_dev","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","himanshu.b@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","jc4.sun@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","vikas.magar@samsung.com","0.0","1.0","0.0","1.0","0.0" +"2026-06-01","yahan.zhang@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","i.stojkovic@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","srikanth.h@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","a3.paliwal@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","zeyang.ye@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","p.furtak@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.setia@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","himesha.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","antik.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","christiana.a@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","sy13.kim@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","blackhole-use1-sandbox","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","s.megha@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","p.marciniak2@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","s.aleynikov@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","w.lewandowsk@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","aashish.g@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","l.stucky@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","muthu.v1@partner.samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","m.lewandows5@samsung.com","0.0","0.0","0.0","0.0","0.0" +"2026-06-01","a.sharma2@samsung.com","0.0","0.0","0.0","0.0","0.0" diff --git a/crates/ai/src/agent/action_result/convert.rs b/crates/ai/src/agent/action_result/convert.rs index ec89cdb5..e37eb4a5 100644 --- a/crates/ai/src/agent/action_result/convert.rs +++ b/crates/ai/src/agent/action_result/convert.rs @@ -63,9 +63,24 @@ impl TryFrom for api::request::input::tool_call_resu }, ), ), - RequestCommandOutputResult::CancelledBeforeExecution => { - Err(ConvertToAPITypeError::Ignore) - } + RequestCommandOutputResult::CancelledBeforeExecution => Ok( + api::request::input::tool_call_result::Result::RunShellCommand( + #[allow(deprecated)] + api::RunShellCommandResult { + command: "cancelled".to_string(), + output: Default::default(), + exit_code: Default::default(), + result: Some(api::run_shell_command_result::Result::CommandFinished( + api::ShellCommandFinished { + command_id: String::new(), + output: "User cancelled the command before it executed." + .to_string(), + exit_code: 130, + }, + )), + }, + ), + ), RequestCommandOutputResult::Denylisted { command } => { #[allow(deprecated)] @@ -125,8 +140,21 @@ impl TryFrom }, ), ), - WriteToLongRunningShellCommandResult::Cancelled => - Err(ConvertToAPITypeError::Ignore), + WriteToLongRunningShellCommandResult::Cancelled => { + Ok( + api::request::input::tool_call_result::Result::WriteToLongRunningShellCommand( + api::WriteToLongRunningShellCommandResult { + result: Some(api::write_to_long_running_shell_command_result::Result::CommandFinished( + api::ShellCommandFinished { + command_id: String::new(), + output: "User cancelled the long-running shell command.".to_string(), + exit_code: 130, + } + )) + }, + ), + ) + } WriteToLongRunningShellCommandResult::Error(ShellCommandError::BlockNotFound) => { Ok(api::request::input::tool_call_result::Result::WriteToLongRunningShellCommand( api::WriteToLongRunningShellCommandResult { @@ -169,7 +197,15 @@ impl TryFrom for api::request::input::tool_call_result::Result )), }), ), - ReadFilesResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + ReadFilesResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::ReadFiles(api::ReadFilesResult { + result: Some(api::read_files_result::Result::Error( + api::read_files_result::Error { + message: "User cancelled the file read.".to_string(), + }, + )), + }), + ), } } } @@ -206,7 +242,17 @@ impl TryFrom for api::request::input::tool_call_result::Re }, ), ), - UploadArtifactResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + UploadArtifactResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::UploadFileArtifact( + api::UploadFileArtifactResult { + result: Some(api::upload_file_artifact_result::Result::Error( + api::upload_file_artifact_result::Error { + message: "User cancelled the upload.".to_string(), + }, + )), + }, + ), + ), } } } @@ -239,7 +285,17 @@ impl TryFrom for api::request::input::tool_call_result::Re }, ), ), - SearchCodebaseResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + SearchCodebaseResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::SearchCodebase( + api::SearchCodebaseResult { + result: Some(api::search_codebase_result::Result::Error( + api::search_codebase_result::Error { + message: "User cancelled the codebase search.".to_string(), + }, + )), + }, + ), + ), } } } @@ -285,7 +341,17 @@ impl TryFrom for api::request::input::tool_call_result:: }, ), ), - RequestFileEditsResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + RequestFileEditsResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::ApplyFileDiffs( + api::ApplyFileDiffsResult { + result: Some(api::apply_file_diffs_result::Result::Error( + api::apply_file_diffs_result::Error { + message: "User cancelled the file edits.".to_string(), + }, + )), + }, + ), + ), } } } @@ -362,7 +428,13 @@ impl TryFrom for api::request::input::tool_call_result::Result { })), }, )), - GrepResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + GrepResult::Cancelled => Ok(api::request::input::tool_call_result::Result::Grep( + api::GrepResult { + result: Some(api::grep_result::Result::Error(api::grep_result::Error { + message: "User cancelled the grep.".to_string(), + })), + }, + )), } } } @@ -386,7 +458,15 @@ impl TryFrom for api::request::input::tool_call_result::Result { )), }), ), - FileGlobResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + FileGlobResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::FileGlob(api::FileGlobResult { + result: Some(api::file_glob_result::Result::Error( + api::file_glob_result::Error { + message: "User cancelled the file glob.".to_string(), + }, + )), + }), + ), } } } @@ -416,7 +496,15 @@ impl TryFrom for api::request::input::tool_call_result::Result )), }), ), - FileGlobV2Result::Cancelled => Err(ConvertToAPITypeError::Ignore), + FileGlobV2Result::Cancelled => Ok( + api::request::input::tool_call_result::Result::FileGlobV2(api::FileGlobV2Result { + result: Some(api::file_glob_v2_result::Result::Error( + api::file_glob_v2_result::Error { + message: "User cancelled the file glob.".to_string(), + }, + )), + }), + ), } } } @@ -468,7 +556,17 @@ impl TryFrom for api::request::input::tool_call_result::R }, ), ), - ReadMCPResourceResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + ReadMCPResourceResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::ReadMcpResource( + api::ReadMcpResourceResult { + result: Some(api::read_mcp_resource_result::Result::Error( + api::read_mcp_resource_result::Error { + message: "User cancelled the MCP resource read.".to_string(), + }, + )), + }, + ), + ), } } } @@ -494,7 +592,17 @@ impl TryFrom for api::request::input::tool_call_result::Resul }, )) } - CallMCPToolResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + CallMCPToolResult::Cancelled => { + Ok(api::request::input::tool_call_result::Result::CallMcpTool( + api::CallMcpToolResult { + result: Some(api::call_mcp_tool_result::Result::Error( + api::call_mcp_tool_result::Error { + message: "User cancelled the MCP tool call.".to_string(), + }, + )), + }, + )) + } } } } @@ -530,7 +638,15 @@ impl TryFrom for api::request::input::tool_call_result::Result )), }), ), - ReadSkillResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + ReadSkillResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::ReadSkill(api::ReadSkillResult { + result: Some(api::read_skill_result::Result::Error( + api::read_skill_result::Error { + message: "User cancelled reading the skill.".to_string(), + }, + )), + }), + ), } } } @@ -564,7 +680,17 @@ impl TryFrom for api::request::input::tool_call_result::Res }, ), ), - ReadDocumentsResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + ReadDocumentsResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::ReadDocuments( + api::ReadDocumentsResult { + result: Some(api::read_documents_result::Result::Error( + api::read_documents_result::Error { + message: "User cancelled reading the documents.".to_string(), + }, + )), + }, + ), + ), } } } @@ -600,7 +726,17 @@ impl TryFrom for api::request::input::tool_call_result::Res }, ), ), - EditDocumentsResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + EditDocumentsResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::EditDocuments( + api::EditDocumentsResult { + result: Some(api::edit_documents_result::Result::Error( + api::edit_documents_result::Error { + message: "User cancelled editing the documents.".to_string(), + }, + )), + }, + ), + ), } } } @@ -636,7 +772,17 @@ impl TryFrom for api::request::input::tool_call_result::R }, ), ), - CreateDocumentsResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + CreateDocumentsResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::CreateDocuments( + api::CreateDocumentsResult { + result: Some(api::create_documents_result::Result::Error( + api::create_documents_result::Error { + message: "User cancelled creating the documents.".to_string(), + }, + )), + }, + ), + ), } } } @@ -760,7 +906,17 @@ impl TryFrom }, ), ), - TransferShellCommandControlToUserResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + TransferShellCommandControlToUserResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::TransferShellCommandControlToUser( + api::TransferShellCommandControlToUserResult { + result: Some(api::transfer_shell_command_control_to_user_result::Result::Error( + api::ShellCommandError { + r#type: Some(api::shell_command_error::Type::CommandNotFound(())), + }, + )), + }, + ), + ), TransferShellCommandControlToUserResult::Error(ShellCommandError::BlockNotFound) => { Ok(api::request::input::tool_call_result::Result::TransferShellCommandControlToUser( api::TransferShellCommandControlToUserResult { @@ -1027,7 +1183,17 @@ impl TryFrom for api::request::input::tool_call_result::Resul }, )) } - UseComputerResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + UseComputerResult::Cancelled => { + Ok(api::request::input::tool_call_result::Result::UseComputer( + api::UseComputerResult { + result: Some(api::use_computer_result::Result::Error( + api::use_computer_result::Error { + message: "User cancelled the computer use.".to_string(), + }, + )), + }, + )) + } } } } @@ -1121,7 +1287,17 @@ impl TryFrom for api::request::input::tool_call_result: }, ), ), - FetchConversationResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + FetchConversationResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::FetchConversation( + api::FetchConversationResult { + result: Some(api::fetch_conversation_result::Result::Error( + api::fetch_conversation_result::Error { + message: "User cancelled fetching the conversation.".to_string(), + }, + )), + }, + ), + ), } } } @@ -1312,7 +1488,18 @@ impl TryFrom for api::request::input::tool_call_resu }, ), ), - InsertReviewCommentsResult::Cancelled => Err(ConvertToAPITypeError::Ignore), + InsertReviewCommentsResult::Cancelled => Ok( + api::request::input::tool_call_result::Result::InsertReviewComments( + api::InsertReviewCommentsResult { + repo_path: String::new(), + result: Some(api::insert_review_comments_result::Result::Error( + api::insert_review_comments_result::Error { + message: "User cancelled inserting review comments.".to_string(), + }, + )), + }, + ), + ), } } } diff --git a/crates/ai/src/agent/action_result/mod.rs b/crates/ai/src/agent/action_result/mod.rs index 38b98855..f684dd5d 100644 --- a/crates/ai/src/agent/action_result/mod.rs +++ b/crates/ai/src/agent/action_result/mod.rs @@ -206,6 +206,16 @@ impl RequestCommandOutputResult { Self::CancelledBeforeExecution | Self::LongRunningCommandSnapshot { .. } => false, } } + + /// Returns a stable identity string for loop detection (command input only, no output). + pub fn loop_description(&self) -> String { + match self { + Self::Completed { command, .. } + | Self::LongRunningCommandSnapshot { command, .. } + | Self::Denylisted { command } => command.clone(), + Self::CancelledBeforeExecution => "cancelled".to_string(), + } + } } impl Display for RequestCommandOutputResult { @@ -823,6 +833,88 @@ impl AIAgentActionResultType { } } + /// Stable identity for loop detection: tool type + input, no variable output. + pub fn loop_description(&self) -> String { + match self { + Self::RequestCommandOutput(r) => { + format!("run_shell_command: {}", r.loop_description()) + } + Self::ReadFiles(ReadFilesResult::Success { files }) => { + format!( + "read_files: [{}]", + files + .iter() + .map(|f| f.file_name.as_str()) + .collect::>() + .join(", ") + ) + } + Self::ReadFiles(ReadFilesResult::Error(error)) => { + format!("read_files: error={error}") + } + Self::ReadFiles(ReadFilesResult::Cancelled) => "read_files: cancelled".to_string(), + Self::RequestFileEdits(RequestFileEditsResult::Success { + updated_files, + deleted_files, + .. + }) => { + let mut names: Vec<&str> = updated_files + .iter() + .map(|f| f.file_context.file_name.as_str()) + .collect(); + names.extend(deleted_files.iter().map(|s| s.as_str())); + format!("apply_file_diffs: [{}]", names.join(", ")) + } + Self::RequestFileEdits(RequestFileEditsResult::DiffApplicationFailed { error }) => { + format!("apply_file_diffs: failed={error}") + } + Self::RequestFileEdits(RequestFileEditsResult::Cancelled) => { + "apply_file_diffs: cancelled".to_string() + } + Self::Grep(GrepResult::Success { matched_files }) => { + format!( + "grep: [{}]", + matched_files + .iter() + .map(|f| f.file_path.as_str()) + .collect::>() + .join(", ") + ) + } + Self::Grep(GrepResult::Error(error)) => format!("grep: error={error}"), + Self::Grep(GrepResult::Cancelled) => "grep: cancelled".to_string(), + Self::FileGlobV2(FileGlobV2Result::Success { matched_files, .. }) => { + format!( + "file_glob: [{}]", + matched_files + .iter() + .map(|f| f.file_path.as_str()) + .collect::>() + .join(", ") + ) + } + Self::FileGlobV2(FileGlobV2Result::Error(error)) => format!("file_glob: error={error}"), + Self::FileGlobV2(FileGlobV2Result::Cancelled) => "file_glob: cancelled".to_string(), + Self::SearchCodebase(SearchCodebaseResult::Success { files }) => { + format!( + "search_codebase: [{}]", + files + .iter() + .map(|f| f.file_name.as_str()) + .collect::>() + .join(", ") + ) + } + Self::SearchCodebase(SearchCodebaseResult::Failed { reason, message }) => { + format!("search_codebase: failed={reason:?} {message}") + } + Self::SearchCodebase(SearchCodebaseResult::Cancelled) => { + "search_codebase: cancelled".to_string() + } + other => format!("{other}"), + } + } + pub fn is_cancelled(&self) -> bool { match self { Self::RequestCommandOutput(RequestCommandOutputResult::CancelledBeforeExecution) => { diff --git a/crates/ai/src/project_context/model.rs b/crates/ai/src/project_context/model.rs index eec47c6a..7a26dff2 100644 --- a/crates/ai/src/project_context/model.rs +++ b/crates/ai/src/project_context/model.rs @@ -98,8 +98,7 @@ impl ProjectRules { } } else { for project_rule in rule.all_rules() { - available_rule_paths - .push(project_rule.path.to_string_lossy().to_string()); + available_rule_paths.push(project_rule.path.to_string_lossy().to_string()); } } } diff --git a/crates/galaxy_core/src/telemetry.rs b/crates/galaxy_core/src/telemetry.rs index 2270a84e..e033904d 100644 --- a/crates/galaxy_core/src/telemetry.rs +++ b/crates/galaxy_core/src/telemetry.rs @@ -100,7 +100,9 @@ macro_rules! send_telemetry_from_app_ctx { pub enum EnablementState { Always, Flag(FeatureFlag), - ChannelSpecific { channels: Vec }, + ChannelSpecific { + channels: Vec, + }, } impl EnablementState { diff --git a/crates/galaxy_core/src/ui/theme/color.rs b/crates/galaxy_core/src/ui/theme/color.rs index d1d0c39d..5a80f136 100644 --- a/crates/galaxy_core/src/ui/theme/color.rs +++ b/crates/galaxy_core/src/ui/theme/color.rs @@ -8,7 +8,7 @@ use self::internal_colors::{ neutral_4, }; -use super::{AnsiColor, AnsiColorIdentifier, Fill, TerminalColors, GalaxyTheme}; +use super::{AnsiColor, AnsiColorIdentifier, Fill, GalaxyTheme, TerminalColors}; use crate::ui::color::{ blend::Blend, diff --git a/crates/galaxyui_core/src/accessibility.rs b/crates/galaxyui_core/src/accessibility.rs index ad913eaf..5052e80d 100644 --- a/crates/galaxyui_core/src/accessibility.rs +++ b/crates/galaxyui_core/src/accessibility.rs @@ -257,7 +257,10 @@ pub enum ActionAccessibilityContent { impl ActionAccessibilityContent { pub fn from_debug() -> Self { Self::CustomFn(|action| { - AccessibilityContent::new_without_help(format!("{action:?}."), GalaxyA11yRole::UserAction) + AccessibilityContent::new_without_help( + format!("{action:?}."), + GalaxyA11yRole::UserAction, + ) }) } } diff --git a/crates/galaxyui_core/src/elements/new_scrollable/dual_axis_config.rs b/crates/galaxyui_core/src/elements/new_scrollable/dual_axis_config.rs index 182e505c..00c71706 100644 --- a/crates/galaxyui_core/src/elements/new_scrollable/dual_axis_config.rs +++ b/crates/galaxyui_core/src/elements/new_scrollable/dual_axis_config.rs @@ -14,12 +14,12 @@ use crate::{ SizeConstraint, }; +#[cfg(debug_assertions)] +use super::ScrollableAxis; use super::{ util::{scroll_clipped_scrollable_handle_with_delta, scroll_delta_for_axis}, NewScrollableElement, SingleAxisConfig, }; -#[cfg(debug_assertions)] -use super::ScrollableAxis; use crate::elements::ScrollTarget; diff --git a/crates/galaxyui_core/src/elements/new_scrollable/single_axis_config.rs b/crates/galaxyui_core/src/elements/new_scrollable/single_axis_config.rs index e38838db..f2ad3a7e 100644 --- a/crates/galaxyui_core/src/elements/new_scrollable/single_axis_config.rs +++ b/crates/galaxyui_core/src/elements/new_scrollable/single_axis_config.rs @@ -11,12 +11,12 @@ use crate::{ SizeConstraint, }; +#[cfg(debug_assertions)] +use super::ScrollableAxis; use super::{ util::{scroll_clipped_scrollable_handle_with_delta, scroll_delta_for_axis}, NewScrollableElement, }; -#[cfg(debug_assertions)] -use super::ScrollableAxis; use crate::elements::{ScrollTarget, ScrollToPositionMode}; diff --git a/crates/input_classifier/src/heuristic_classifier/mod.rs b/crates/input_classifier/src/heuristic_classifier/mod.rs index e90e6ca0..a6260a0d 100644 --- a/crates/input_classifier/src/heuristic_classifier/mod.rs +++ b/crates/input_classifier/src/heuristic_classifier/mod.rs @@ -13,15 +13,8 @@ use crate::{ }, }; -/// Minimum number of tokens users' input should have before kicking off input detection -/// to switch from AI input to command input. -/// This could be tuned. -const MINIMUM_COMMAND_DETECTION_TOKEN_LENGTH: u8 = 2; - -/// Minimum number of tokens users' input should have before kicking off input detection -/// to switch from command input to AI input. -/// This could be tuned. -const MINIMUM_NATURAL_LANGUAGE_DETECTION_TOKEN_LENGTH: u8 = 2; +/// Minimum number of characters the input buffer must have before classification runs. +const MINIMUM_CLASSIFICATION_CHAR_LENGTH: usize = 3; /// The percentage of input tokens that can be recognized as a natural language word before /// we consider the input as natural language. This could be tuned. @@ -43,20 +36,32 @@ impl InputClassifier for HeuristicClassifier { let word_tokens = parse_query_into_tokens(input.buffer_text.as_str()); let total_word_token_count = word_tokens.len(); + log::info!( + "[input-classifier] detect_input_type called: buffer={:?}, word_tokens={}, current={:?}", + &input.buffer_text, + total_word_token_count, + context.current_input_type + ); + if total_word_token_count == 1 && is_one_off_natural_language_word_or_prefix(&word_tokens[0].to_lowercase()) { + log::info!("[input-classifier] → one-off NL word, returning AI"); return InputType::AI; } if is_likely_shell_command(&input, total_word_token_count).await { + log::info!("[input-classifier] → is_likely_shell_command=true, returning Shell"); return InputType::Shell; } - self.classify_input(input, context) + let result = self.classify_input(input, context) .await .map(|result| result.to_input_type()) - .unwrap_or(context.current_input_type) + .unwrap_or(context.current_input_type); + + log::info!("[input-classifier] → classify_input returned {:?}", result); + result } async fn classify_input( @@ -96,18 +101,12 @@ impl InputClassifier for HeuristicClassifier { async fn natural_language_detection_heuristic( input: ParsedTokensSnapshot, word_tokens: Vec, - current_input_type: InputType, + _current_input_type: InputType, include_last_token: bool, ) -> ClassificationResult { - let word_tokens_count = word_tokens.len(); + let _word_tokens_count = word_tokens.len(); - let min_token_length = if matches!(current_input_type, InputType::AI) { - MINIMUM_COMMAND_DETECTION_TOKEN_LENGTH - } else { - MINIMUM_NATURAL_LANGUAGE_DETECTION_TOKEN_LENGTH - }; - - if min_token_length > word_tokens_count as u8 { + if input.buffer_text.len() < MINIMUM_CLASSIFICATION_CHAR_LENGTH { return ClassificationResult::pure_shell(); } diff --git a/crates/input_classifier/src/util.rs b/crates/input_classifier/src/util.rs index 8ee6604d..3a443078 100644 --- a/crates/input_classifier/src/util.rs +++ b/crates/input_classifier/src/util.rs @@ -58,20 +58,16 @@ pub fn is_prefix_of_natural_language_word(input: &str) -> bool { pub async fn is_likely_shell_command( input: &ParsedTokensSnapshot, - word_tokens_count: usize, + _word_tokens_count: usize, ) -> bool { const YIELD_BATCH_SIZE: usize = 5; let mut likely_command_token_count = 0; let total_token_count = input.parsed_tokens.len(); - let mut is_first_token_command = false; for (idx, token) in input.parsed_tokens.iter().enumerate() { - // Periodically, yield to the executor so this task can be aborted if - // requested. if idx % YIELD_BATCH_SIZE == 0 { futures_lite::future::yield_now().await; } - // Early return if we encounter a one-off command / keyword at the beginning of the line. if token.token_index == 0 && ONE_OFF_SHELL_COMMAND_KEYWORDS.contains(&token.token.as_str()) { return true; @@ -82,10 +78,6 @@ pub async fn is_likely_shell_command( { likely_command_token_count += 1; } - - if token.token_index == 0 { - is_first_token_command = token.token_description.is_some(); - } } // When token count is lower than 2, we should make sure all tokens @@ -98,16 +90,17 @@ pub async fn is_likely_shell_command( DETECT_AS_COMMAND_THRESHOLD }; - // Classify as shell if: - // 1) We hit significant threshold of likely shell command tokens. - // 2) When there are fewer than 3 tokens, the first token is a valid top-level command. - if likely_command_token_count >= (total_token_count as f32 * command_threshold) as usize - || (word_tokens_count < 3 && is_first_token_command) - { - return true; - } - - false + let threshold_count = (total_token_count as f32 * command_threshold) as usize; + let is_shell = likely_command_token_count >= threshold_count; + log::info!( + "[input-classifier] is_likely_shell_command: tokens={}, cmd_tokens={}, threshold={:.2} (need {}), result={}", + total_token_count, + likely_command_token_count, + command_threshold, + threshold_count, + is_shell + ); + is_shell } /// Returns true if the first token is a command that is installed on the system. diff --git a/crates/local_inference/Cargo.toml b/crates/local_inference/Cargo.toml new file mode 100644 index 00000000..efea625e --- /dev/null +++ b/crates/local_inference/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "local_inference" +authors.workspace = true +edition = "2024" +publish.workspace = true +license.workspace = true + +[features] +default = ["cpu"] +cpu = [] +metal = ["candle-core/metal", "candle-nn/metal", "candle-transformers/metal"] +cuda = ["candle-core/cuda", "candle-nn/cuda", "candle-transformers/cuda"] + +[dependencies] +anyhow.workspace = true +async-trait.workspace = true +candle-core = "0.9.2" +candle-nn = "0.9.2" +candle-transformers = "0.9.2" +directories.workspace = true +hf-hub = { version = "0.4", features = ["tokio"] } +log.workspace = true +serde.workspace = true +serde_json.workspace = true +tokenizers = "0.21.4" +tokio = { workspace = true, features = ["fs", "sync", "rt", "macros", "rt-multi-thread"] } + +[dev-dependencies] +env_logger = "0.10" diff --git a/crates/local_inference/examples/classify.rs b/crates/local_inference/examples/classify.rs new file mode 100644 index 00000000..b00f35ee --- /dev/null +++ b/crates/local_inference/examples/classify.rs @@ -0,0 +1,66 @@ +use local_inference::{ + Device, InferenceEngine, InferenceTask, InputClassificationInput, InputClassificationTask, + TabNamingInput, TabNamingTask, +}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::init(); + + println!("Initializing inference engine (this may download the model on first run)..."); + let engine = InferenceEngine::new(Device::best_available()).await?; + println!("Engine ready!\n"); + + // --- Input Classification --- + let test_inputs = vec![ + "ls -la", + "git status", + "what files are in this directory?", + "explain this error to me", + "docker compose up -d", + "how do I fix this segfault?", + "cd /tmp && rm -rf build/", + "refactor the auth module to use JWT", + ]; + + println!("=== Input Classification ===\n"); + for input in &test_inputs { + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: input.to_string(), + recent_commands: vec!["git log".into(), "npm test".into()], + is_follow_up: false, + }, + ) + .await?; + + println!( + " {:50} -> {:?} (confidence: {:.2})", + format!("\"{}\"", input), + result.category, + result.confidence + ); + } + + // --- Tab Naming --- + println!("\n=== Tab Naming ===\n"); + let tab_name = TabNamingTask + .run( + &engine, + TabNamingInput { + recent_commands: vec![ + "git checkout feature/auth".into(), + "cargo test".into(), + "vim src/auth/mod.rs".into(), + ], + working_directory: "/home/user/projects/myapp".into(), + }, + ) + .await?; + + println!(" Suggested tab name: \"{tab_name}\""); + + Ok(()) +} diff --git a/crates/local_inference/src/engine/generation.rs b/crates/local_inference/src/engine/generation.rs new file mode 100644 index 00000000..25a72f9e --- /dev/null +++ b/crates/local_inference/src/engine/generation.rs @@ -0,0 +1,115 @@ +use anyhow::Result; +use candle_core::Tensor; + +#[derive(Clone)] +pub struct GenerationConfig { + pub max_tokens: usize, + pub temperature: f64, + pub top_p: f64, +} + +impl Default for GenerationConfig { + fn default() -> Self { + Self { + max_tokens: 32, + temperature: 0.7, + top_p: 0.9, + } + } +} + +impl GenerationConfig { + pub fn deterministic() -> Self { + Self { + max_tokens: 32, + temperature: 0.0, + top_p: 1.0, + } + } + + pub fn creative() -> Self { + Self { + max_tokens: 64, + temperature: 0.9, + top_p: 0.95, + } + } +} + +pub fn sample(logits: &Tensor, config: &GenerationConfig) -> Result { + if config.temperature == 0.0 { + return greedy(logits); + } + + let logits = logits.to_dtype(candle_core::DType::F32)?; + let logits_vec = logits.to_vec1::()?; + + // Apply temperature + let scaled: Vec = logits_vec + .iter() + .map(|&x| (x as f64) / config.temperature) + .collect(); + + // Softmax + let max_val = scaled.iter().cloned().fold(f64::NEG_INFINITY, f64::max); + let exp: Vec = scaled.iter().map(|&x| (x - max_val).exp()).collect(); + let sum: f64 = exp.iter().sum(); + let probs: Vec = exp.iter().map(|&x| x / sum).collect(); + + // Top-p (nucleus) sampling + let mut indexed_probs: Vec<(usize, f64)> = probs.iter().copied().enumerate().collect(); + indexed_probs.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + + let mut cumulative = 0.0; + let mut candidates = Vec::new(); + for (idx, prob) in &indexed_probs { + cumulative += prob; + candidates.push((*idx, *prob)); + if cumulative >= config.top_p { + break; + } + } + + // Renormalize candidates + let candidate_sum: f64 = candidates.iter().map(|(_, p)| p).sum(); + let threshold = simple_rng() * candidate_sum; + + let mut acc = 0.0; + for (idx, prob) in &candidates { + acc += prob; + if acc >= threshold { + return Ok(*idx as u32); + } + } + + // Fallback to top candidate + Ok(candidates[0].0 as u32) +} + +fn greedy(logits: &Tensor) -> Result { + let logits = logits.to_dtype(candle_core::DType::F32)?; + let logits_vec = logits.to_vec1::()?; + let max_idx = logits_vec + .iter() + .enumerate() + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) + .map(|(idx, _)| idx) + .unwrap_or(0); + Ok(max_idx as u32) +} + +fn simple_rng() -> f64 { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + use std::time::SystemTime; + + let mut hasher = DefaultHasher::new(); + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_nanos() + .hash(&mut hasher); + std::thread::current().id().hash(&mut hasher); + let hash = hasher.finish(); + (hash as f64) / (u64::MAX as f64) +} diff --git a/crates/local_inference/src/engine/mod.rs b/crates/local_inference/src/engine/mod.rs new file mode 100644 index 00000000..6cc887ba --- /dev/null +++ b/crates/local_inference/src/engine/mod.rs @@ -0,0 +1,309 @@ +mod generation; +mod model_loader; + +use std::path::PathBuf; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +use anyhow::{Context as _, Result}; +use candle_core::{DType, Tensor}; +use candle_transformers::models::llama::{Cache, Config, Llama, LlamaConfig}; +use tokenizers::Tokenizer; +use tokio::sync::Mutex; + +pub use generation::GenerationConfig; + +/// A token that can be used to cancel an in-progress generation. +/// Clone it and pass to `generate_cancellable`, then call `cancel()` to +/// interrupt the generation loop between tokens. +#[derive(Clone)] +pub struct CancellationToken { + cancelled: Arc, +} + +impl CancellationToken { + pub fn new() -> Self { + Self { + cancelled: Arc::new(AtomicBool::new(false)), + } + } + + pub fn cancel(&self) { + self.cancelled.store(true, Ordering::Relaxed); + } + + pub fn is_cancelled(&self) -> bool { + self.cancelled.load(Ordering::Relaxed) + } +} + +const HF_REPO: &str = "HuggingFaceTB/SmolLM2-135M-Instruct"; +const MODEL_FILENAME: &str = "model.safetensors"; +const TOKENIZER_FILENAME: &str = "tokenizer.json"; +const CONFIG_FILENAME: &str = "config.json"; + +#[derive(Debug, Clone, Copy)] +pub enum Device { + Cpu, + #[cfg(feature = "metal")] + Metal, + #[cfg(feature = "cuda")] + Cuda(usize), +} + +impl Device { + pub fn best_available() -> Self { + #[cfg(feature = "metal")] + { + return Device::Metal; + } + #[cfg(feature = "cuda")] + { + return Device::Cuda(0); + } + #[cfg(not(any(feature = "metal", feature = "cuda")))] + { + Device::Cpu + } + } + + fn to_candle_device(&self) -> Result { + match self { + Device::Cpu => Ok(candle_core::Device::Cpu), + #[cfg(feature = "metal")] + Device::Metal => Ok(candle_core::Device::new_metal(0)?), + #[cfg(feature = "cuda")] + Device::Cuda(ordinal) => Ok(candle_core::Device::new_cuda(*ordinal)?), + } + } +} + +pub struct InferenceEngine { + model: Arc>, + cache: Arc>, + config: Config, + tokenizer: Tokenizer, + device: candle_core::Device, +} + +impl InferenceEngine { + pub async fn new(device: Device) -> Result { + let candle_device = device.to_candle_device()?; + let model_dir = model_loader::ensure_model_available().await?; + + let config_path = model_dir.join(CONFIG_FILENAME); + let config_str = tokio::fs::read_to_string(&config_path) + .await + .with_context(|| format!("reading config from {}", config_path.display()))?; + let llama_config: LlamaConfig = + serde_json::from_str(&config_str).context("parsing model config")?; + let config = llama_config.into_config(false); + + let model_path = model_dir.join(MODEL_FILENAME); + let vb = unsafe { + candle_nn::VarBuilder::from_mmaped_safetensors( + &[model_path], + DType::F32, + &candle_device, + )? + }; + + let model = Llama::load(vb, &config).context("loading SmolLM2 model")?; + let cache = Cache::new(true, DType::F32, &config, &candle_device)?; + + let tokenizer_path = model_dir.join(TOKENIZER_FILENAME); + let tokenizer_bytes = tokio::fs::read(&tokenizer_path) + .await + .with_context(|| format!("reading tokenizer from {}", tokenizer_path.display()))?; + let tokenizer = + Tokenizer::from_bytes(&tokenizer_bytes).map_err(|e| anyhow::anyhow!("{e}"))?; + + log::info!( + "Local inference engine initialized (device: {:?}, model: {})", + device, + HF_REPO + ); + + Ok(Self { + model: Arc::new(Mutex::new(model)), + cache: Arc::new(Mutex::new(cache)), + config, + tokenizer, + device: candle_device, + }) + } + + pub fn tokenizer(&self) -> &Tokenizer { + &self.tokenizer + } + + pub fn device(&self) -> &candle_core::Device { + &self.device + } + + pub async fn generate_cancellable( + &self, + prompt: &str, + config: &GenerationConfig, + cancel: &CancellationToken, + ) -> Result { + let encoding = self + .tokenizer + .encode(prompt, true) + .map_err(|e| anyhow::anyhow!("{e}"))?; + + let input_ids = encoding.get_ids().to_vec(); + let mut tokens = input_ids.clone(); + + let eos_token_id = self + .tokenizer + .token_to_id("") + .or_else(|| self.tokenizer.token_to_id("<|endoftext|>")) + .or_else(|| self.tokenizer.token_to_id("<|im_end|>")); + + let model = self.model.lock().await; + let mut cache = self.cache.lock().await; + + if cancel.is_cancelled() { + anyhow::bail!("cancelled before generation started"); + } + + // Reset cache for new generation + *cache = Cache::new(true, DType::F32, &self.config, &self.device)?; + + // Prefill: process all input tokens at once + let input_tensor = Tensor::new(input_ids.as_slice(), &self.device)?.unsqueeze(0)?; + let logits = model.forward(&input_tensor, 0, &mut cache)?; + + if cancel.is_cancelled() { + anyhow::bail!("cancelled during prefill"); + } + + // Sample first generated token from the last logits position + let first_logits = logits.squeeze(0)?; + let next_token = generation::sample(&first_logits, config)?; + + if let Some(eos) = eos_token_id { + if next_token == eos { + let generated_tokens = &tokens[input_ids.len()..]; + let output = self + .tokenizer + .decode(generated_tokens, true) + .map_err(|e| anyhow::anyhow!("{e}"))?; + return Ok(output.trim().to_string()); + } + } + tokens.push(next_token); + + // Decode: generate one token at a time, checking cancellation between tokens + for _i in 1..config.max_tokens { + if cancel.is_cancelled() { + anyhow::bail!("cancelled during generation"); + } + + let pos = tokens.len() - 1; + let next_input = Tensor::new(&[*tokens.last().unwrap()], &self.device)?.unsqueeze(0)?; + let logits = model.forward(&next_input, pos, &mut cache)?; + + let next_logits = logits.squeeze(0)?; + let next_token = generation::sample(&next_logits, config)?; + + if let Some(eos) = eos_token_id { + if next_token == eos { + break; + } + } + + tokens.push(next_token); + } + + let generated_tokens = &tokens[input_ids.len()..]; + let output = self + .tokenizer + .decode(generated_tokens, true) + .map_err(|e| anyhow::anyhow!("{e}"))?; + + Ok(output.trim().to_string()) + } + + pub async fn generate(&self, prompt: &str, config: &GenerationConfig) -> Result { + let encoding = self + .tokenizer + .encode(prompt, true) + .map_err(|e| anyhow::anyhow!("{e}"))?; + + let input_ids = encoding.get_ids().to_vec(); + let mut tokens = input_ids.clone(); + + let eos_token_id = self + .tokenizer + .token_to_id("") + .or_else(|| self.tokenizer.token_to_id("<|endoftext|>")) + .or_else(|| self.tokenizer.token_to_id("<|im_end|>")); + + let model = self.model.lock().await; + let mut cache = self.cache.lock().await; + + // Reset cache for new generation + *cache = Cache::new(true, DType::F32, &self.config, &self.device)?; + + // Prefill: process all input tokens at once + let input_tensor = Tensor::new(input_ids.as_slice(), &self.device)?.unsqueeze(0)?; + let logits = model.forward(&input_tensor, 0, &mut cache)?; + + // Sample first generated token from the last logits position + let first_logits = logits.squeeze(0)?; + let next_token = generation::sample(&first_logits, config)?; + + if let Some(eos) = eos_token_id { + if next_token == eos { + let generated_tokens = &tokens[input_ids.len()..]; + let output = self + .tokenizer + .decode(generated_tokens, true) + .map_err(|e| anyhow::anyhow!("{e}"))?; + return Ok(output.trim().to_string()); + } + } + tokens.push(next_token); + + // Decode: generate one token at a time + for _i in 1..config.max_tokens { + let pos = tokens.len() - 1; + let next_input = Tensor::new(&[*tokens.last().unwrap()], &self.device)?.unsqueeze(0)?; + let logits = model.forward(&next_input, pos, &mut cache)?; + + let next_logits = logits.squeeze(0)?; + let next_token = generation::sample(&next_logits, config)?; + + if let Some(eos) = eos_token_id { + if next_token == eos { + break; + } + } + + tokens.push(next_token); + } + + let generated_tokens = &tokens[input_ids.len()..]; + let output = self + .tokenizer + .decode(generated_tokens, true) + .map_err(|e| anyhow::anyhow!("{e}"))?; + + Ok(output.trim().to_string()) + } + + pub fn model_dir() -> PathBuf { + model_loader::model_cache_dir() + } + + pub async fn is_model_downloaded() -> bool { + let dir = Self::model_dir(); + tokio::fs::metadata(dir.join(MODEL_FILENAME)).await.is_ok() + && tokio::fs::metadata(dir.join(TOKENIZER_FILENAME)) + .await + .is_ok() + && tokio::fs::metadata(dir.join(CONFIG_FILENAME)).await.is_ok() + } +} diff --git a/crates/local_inference/src/engine/model_loader.rs b/crates/local_inference/src/engine/model_loader.rs new file mode 100644 index 00000000..c70a045a --- /dev/null +++ b/crates/local_inference/src/engine/model_loader.rs @@ -0,0 +1,74 @@ +use std::path::PathBuf; + +use anyhow::{Context, Result}; + +use super::{CONFIG_FILENAME, HF_REPO, MODEL_FILENAME, TOKENIZER_FILENAME}; + +pub fn model_cache_dir() -> PathBuf { + directories::ProjectDirs::from("", "", "galaxy") + .map(|dirs| dirs.cache_dir().join("models").join("smollm2-135m")) + .unwrap_or_else(|| PathBuf::from(".galaxy/models/smollm2-135m")) +} + +pub async fn ensure_model_available() -> Result { + let cache_dir = model_cache_dir(); + + let model_path = cache_dir.join(MODEL_FILENAME); + let tokenizer_path = cache_dir.join(TOKENIZER_FILENAME); + let config_path = cache_dir.join(CONFIG_FILENAME); + + if model_path.exists() && tokenizer_path.exists() && config_path.exists() { + log::debug!("Model already cached at {}", cache_dir.display()); + return Ok(cache_dir); + } + + log::info!("Downloading SmolLM2-135M model from HuggingFace..."); + tokio::fs::create_dir_all(&cache_dir) + .await + .with_context(|| format!("creating cache dir {}", cache_dir.display()))?; + + let api = hf_hub::api::tokio::Api::new().context("initializing HuggingFace API")?; + let repo = api.model(HF_REPO.to_string()); + + let downloaded_model = repo + .get(MODEL_FILENAME) + .await + .context("downloading model weights")?; + let downloaded_tokenizer = repo + .get(TOKENIZER_FILENAME) + .await + .context("downloading tokenizer")?; + let downloaded_config = repo + .get(CONFIG_FILENAME) + .await + .context("downloading config")?; + + // hf-hub caches files itself, but we symlink/copy to our canonical location + // for predictable access + link_or_copy(&downloaded_model, &model_path).await?; + link_or_copy(&downloaded_tokenizer, &tokenizer_path).await?; + link_or_copy(&downloaded_config, &config_path).await?; + + log::info!("Model downloaded and cached at {}", cache_dir.display()); + Ok(cache_dir) +} + +async fn link_or_copy(src: &std::path::Path, dst: &std::path::Path) -> Result<()> { + if dst.exists() { + return Ok(()); + } + + // Try symlink first (saves disk space) + #[cfg(unix)] + { + if tokio::fs::symlink(src, dst).await.is_ok() { + return Ok(()); + } + } + + // Fall back to copy + tokio::fs::copy(src, dst) + .await + .with_context(|| format!("copying {} to {}", src.display(), dst.display()))?; + Ok(()) +} diff --git a/crates/local_inference/src/lib.rs b/crates/local_inference/src/lib.rs new file mode 100644 index 00000000..eea60405 --- /dev/null +++ b/crates/local_inference/src/lib.rs @@ -0,0 +1,22 @@ +pub mod engine; +pub mod tasks; + +use async_trait::async_trait; + +pub use engine::{CancellationToken, Device, GenerationConfig, InferenceEngine}; +pub use tasks::{ + InputCategory, InputClassificationInput, InputClassificationResult, InputClassificationTask, + PromptSuggestionInput, PromptSuggestionTask, TabNamingInput, TabNamingTask, +}; + +#[async_trait] +pub trait InferenceTask: Send + Sync { + type Input: Send; + type Output: Send; + + async fn run( + &self, + engine: &InferenceEngine, + input: Self::Input, + ) -> anyhow::Result; +} diff --git a/crates/local_inference/src/tasks/input_classification.rs b/crates/local_inference/src/tasks/input_classification.rs new file mode 100644 index 00000000..518d069c --- /dev/null +++ b/crates/local_inference/src/tasks/input_classification.rs @@ -0,0 +1,118 @@ +use async_trait::async_trait; + +use crate::InferenceTask; +use crate::engine::{CancellationToken, GenerationConfig, InferenceEngine}; + +pub struct InputClassificationTask; + +#[derive(Clone)] +pub struct InputClassificationInput { + pub user_input: String, + pub recent_commands: Vec, + pub is_follow_up: bool, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum InputCategory { + Shell, + AgentPrompt, +} + +pub struct InputClassificationResult { + pub category: InputCategory, + pub confidence: f32, +} + +impl InputClassificationTask { + fn build_prompt(input: &InputClassificationInput) -> String { + let history = if input.recent_commands.is_empty() { + String::from("(none)") + } else { + input + .recent_commands + .iter() + .take(3) + .map(|c| format!("- {c}")) + .collect::>() + .join("\n") + }; + + let follow_up_hint = if input.is_follow_up { + " The user just received an AI response, so this may be a follow-up." + } else { + "" + }; + + format!( + "<|im_start|>system\n\ + You classify terminal input. Respond with ONLY one word: \"shell\" or \"agent\".\n\ + \"shell\" = a CLI command the user wants to execute.\n\ + \"agent\" = a natural language prompt for an AI assistant.{follow_up_hint}\ + <|im_end|>\n\ + <|im_start|>user\n\ + Recent commands:\n{history}\n\ + Classify this input: \"{}\"\ + <|im_end|>\n\ + <|im_start|>assistant\n", + input.user_input + ) + } + + fn parse_output(output: &str) -> InputClassificationResult { + let output_lower = output.trim().to_lowercase(); + + let category = if output_lower.contains("shell") || output_lower.contains("command") { + InputCategory::Shell + } else { + InputCategory::AgentPrompt + }; + + let confidence = if output_lower == "shell" || output_lower == "agent" { + 0.95 + } else { + 0.7 + }; + + InputClassificationResult { + category, + confidence, + } + } + + pub async fn run_cancellable( + &self, + engine: &InferenceEngine, + input: InputClassificationInput, + cancel: &CancellationToken, + ) -> anyhow::Result { + let prompt = Self::build_prompt(&input); + let config = GenerationConfig { + max_tokens: 4, + temperature: 0.0, + top_p: 1.0, + }; + let output = engine.generate_cancellable(&prompt, &config, cancel).await?; + Ok(Self::parse_output(&output)) + } +} + +#[async_trait] +impl InferenceTask for InputClassificationTask { + type Input = InputClassificationInput; + type Output = InputClassificationResult; + + async fn run( + &self, + engine: &InferenceEngine, + input: Self::Input, + ) -> anyhow::Result { + let prompt = Self::build_prompt(&input); + let config = GenerationConfig { + max_tokens: 4, + temperature: 0.0, + top_p: 1.0, + }; + let output = engine.generate(&prompt, &config).await?; + Ok(Self::parse_output(&output)) + } +} diff --git a/crates/local_inference/src/tasks/mod.rs b/crates/local_inference/src/tasks/mod.rs new file mode 100644 index 00000000..92af9a17 --- /dev/null +++ b/crates/local_inference/src/tasks/mod.rs @@ -0,0 +1,9 @@ +mod input_classification; +mod prompt_suggestion; +mod tab_naming; + +pub use input_classification::{ + InputCategory, InputClassificationInput, InputClassificationResult, InputClassificationTask, +}; +pub use prompt_suggestion::{PromptSuggestionInput, PromptSuggestionTask}; +pub use tab_naming::{TabNamingInput, TabNamingTask}; diff --git a/crates/local_inference/src/tasks/prompt_suggestion.rs b/crates/local_inference/src/tasks/prompt_suggestion.rs new file mode 100644 index 00000000..7d577114 --- /dev/null +++ b/crates/local_inference/src/tasks/prompt_suggestion.rs @@ -0,0 +1,64 @@ +use async_trait::async_trait; + +use crate::InferenceTask; +use crate::engine::{GenerationConfig, InferenceEngine}; + +pub struct PromptSuggestionTask; + +pub struct PromptSuggestionInput { + pub recent_commands: Vec, + pub current_input: String, + pub working_directory: String, +} + +#[async_trait] +impl InferenceTask for PromptSuggestionTask { + type Input = PromptSuggestionInput; + type Output = Vec; + + async fn run( + &self, + engine: &InferenceEngine, + input: Self::Input, + ) -> anyhow::Result> { + let history = input + .recent_commands + .iter() + .take(5) + .map(|c| format!("- {c}")) + .collect::>() + .join("\n"); + + let prompt = format!( + "<|im_start|>system\n\ + You suggest terminal commands. Give exactly 3 suggestions, one per line. \ + No numbering, no explanation, just the commands.\ + <|im_end|>\n\ + <|im_start|>user\n\ + Directory: {}\n\ + Recent commands:\n{}\n\ + Current partial input: \"{}\"\n\ + Suggest 3 likely next commands:\ + <|im_end|>\n\ + <|im_start|>assistant\n", + input.working_directory, history, input.current_input + ); + + let config = GenerationConfig { + max_tokens: 64, + temperature: 0.6, + top_p: 0.9, + }; + + let output = engine.generate(&prompt, &config).await?; + + let suggestions: Vec = output + .lines() + .map(|l| l.trim().to_string()) + .filter(|l| !l.is_empty()) + .take(3) + .collect(); + + Ok(suggestions) + } +} diff --git a/crates/local_inference/src/tasks/tab_naming.rs b/crates/local_inference/src/tasks/tab_naming.rs new file mode 100644 index 00000000..acf96c1f --- /dev/null +++ b/crates/local_inference/src/tasks/tab_naming.rs @@ -0,0 +1,60 @@ +use async_trait::async_trait; + +use crate::InferenceTask; +use crate::engine::{GenerationConfig, InferenceEngine}; + +pub struct TabNamingTask; + +pub struct TabNamingInput { + pub recent_commands: Vec, + pub working_directory: String, +} + +#[async_trait] +impl InferenceTask for TabNamingTask { + type Input = TabNamingInput; + type Output = String; + + async fn run(&self, engine: &InferenceEngine, input: Self::Input) -> anyhow::Result { + let commands_str = input + .recent_commands + .iter() + .take(5) + .map(|c| format!("- {c}")) + .collect::>() + .join("\n"); + + let prompt = format!( + "<|im_start|>system\n\ + You name terminal tabs. Respond with ONLY a short name (2-4 words max). No explanation.\ + <|im_end|>\n\ + <|im_start|>user\n\ + Directory: {}\n\ + Recent commands:\n{}\n\ + What should this tab be named?\ + <|im_end|>\n\ + <|im_start|>assistant\n", + input.working_directory, commands_str + ); + + let config = GenerationConfig { + max_tokens: 12, + temperature: 0.3, + top_p: 0.9, + }; + + let output = engine.generate(&prompt, &config).await?; + + // Clean up: take only the first line, strip quotes + let name = output + .lines() + .next() + .unwrap_or(&output) + .trim() + .trim_matches('"') + .trim_matches('\'') + .to_string(); + + Ok(name) + } +} diff --git a/crates/local_inference/tests/e2e_scenarios.rs b/crates/local_inference/tests/e2e_scenarios.rs new file mode 100644 index 00000000..7317cd75 --- /dev/null +++ b/crates/local_inference/tests/e2e_scenarios.rs @@ -0,0 +1,335 @@ +//! End-to-end scenario tests that validate realistic usage patterns for +//! the local inference engine across all task types (classification, +//! prompt suggestion, tab naming). + +use local_inference::{ + Device, InferenceEngine, InferenceTask, InputCategory, InputClassificationInput, + InputClassificationTask, PromptSuggestionInput, PromptSuggestionTask, TabNamingInput, + TabNamingTask, +}; +use std::time::Instant; + +async fn get_engine() -> InferenceEngine { + InferenceEngine::new(Device::Cpu) + .await + .expect("Failed to initialize engine — is the model downloaded?") +} + +// --- Full workflow scenarios --- + +/// Simulates a user session where they type various inputs and the classifier +/// routes them correctly between shell and agent modes. +#[tokio::test] +async fn scenario_user_session_mode_switching() { + let engine = get_engine().await; + + struct TestCase { + input: &'static str, + recent: Vec<&'static str>, + follow_up: bool, + expected: InputCategory, + } + + let cases = vec![ + TestCase { + input: "cd ~/projects/myapp", + recent: vec!["ls", "pwd"], + follow_up: false, + expected: InputCategory::Shell, + }, + TestCase { + input: "npm start", + recent: vec!["cd ~/projects/myapp", "ls"], + follow_up: false, + expected: InputCategory::Shell, + }, + TestCase { + input: "why is my server crashing on startup?", + recent: vec!["npm start", "cd ~/projects/myapp"], + follow_up: false, + expected: InputCategory::AgentPrompt, + }, + TestCase { + input: "can you also check the environment variables?", + recent: vec!["npm start"], + follow_up: true, + expected: InputCategory::AgentPrompt, + }, + TestCase { + input: "export NODE_ENV=production", + recent: vec!["npm start"], + follow_up: false, + expected: InputCategory::Shell, + }, + ]; + + println!("\n--- User Session Mode Switching Scenario ---"); + for case in &cases { + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: case.input.to_string(), + recent_commands: case.recent.iter().map(|s| s.to_string()).collect(), + is_follow_up: case.follow_up, + }, + ) + .await + .expect("classification failed"); + + println!( + " {:?} -> {:?} (expected {:?}, confidence {:.2})", + case.input, result.category, case.expected, result.confidence + ); + assert_eq!( + result.category, case.expected, + "Misclassified {:?}", + case.input + ); + } +} + +/// Simulates prompt suggestions being generated after various shell commands. +#[tokio::test] +async fn scenario_prompt_suggestions_after_commands() { + let engine = get_engine().await; + + println!("\n--- Prompt Suggestions After Commands Scenario ---"); + + // After a failed build + let suggestions = PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec![ + "cargo build".into(), + "cargo test -- --nocapture".into(), + "cargo build".into(), // repeated = likely still failing + ], + current_input: String::new(), + working_directory: "/home/user/projects/rust-app".into(), + }, + ) + .await + .expect("suggestion failed"); + + println!(" After repeated builds: {suggestions:?}"); + assert!(!suggestions.is_empty()); + + // After git workflow + let suggestions = PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec![ + "git add .".into(), + "git commit -m 'wip'".into(), + "git push".into(), + ], + current_input: String::new(), + working_directory: "/home/user/projects/feature-branch".into(), + }, + ) + .await + .expect("suggestion failed"); + + println!(" After git workflow: {suggestions:?}"); + assert!(!suggestions.is_empty()); + + // With partial input + let suggestions = PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec!["docker ps".into(), "docker logs app".into()], + current_input: "docker".to_string(), + working_directory: "/home/user/deployments".into(), + }, + ) + .await + .expect("suggestion failed"); + + println!(" With 'docker' partial input: {suggestions:?}"); + assert!(!suggestions.is_empty()); +} + +/// Simulates tab naming for different development contexts. +#[tokio::test] +async fn scenario_tab_naming_development_contexts() { + let engine = get_engine().await; + + println!("\n--- Tab Naming for Development Contexts ---"); + + struct TabCase { + label: &'static str, + commands: Vec<&'static str>, + cwd: &'static str, + } + + let cases = vec![ + TabCase { + label: "Git operations", + commands: vec!["git log --oneline", "git branch -a", "git fetch origin"], + cwd: "/home/user/projects/galaxy", + }, + TabCase { + label: "Docker deployment", + commands: vec!["docker compose up -d", "docker ps", "docker logs web"], + cwd: "/home/user/services/api", + }, + TabCase { + label: "Python data science", + commands: vec!["jupyter notebook", "pip install pandas", "python analysis.py"], + cwd: "/home/user/research/data-pipeline", + }, + TabCase { + label: "Rust development", + commands: vec!["cargo build", "cargo test", "cargo clippy"], + cwd: "/home/user/projects/my-crate", + }, + ]; + + for case in &cases { + let name = TabNamingTask + .run( + &engine, + TabNamingInput { + recent_commands: case.commands.iter().map(|s| s.to_string()).collect(), + working_directory: case.cwd.to_string(), + }, + ) + .await + .expect("tab naming failed"); + + println!(" {}: \"{}\"", case.label, name); + assert!(!name.is_empty(), "tab name should not be empty"); + assert!( + name.split_whitespace().count() <= 6, + "tab name too long for '{}': \"{}\"", + case.label, + name + ); + } +} + +/// Tests performance: all tasks should complete quickly enough for interactive use. +#[tokio::test] +async fn scenario_performance_under_load() { + let engine = get_engine().await; + + println!("\n--- Performance Under Load ---"); + + // Classification should be fast (< 500ms on CPU) + let start = Instant::now(); + for _ in 0..5 { + InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: "git push origin main".to_string(), + recent_commands: vec!["git add .".into(), "git commit -m 'test'".into()], + is_follow_up: false, + }, + ) + .await + .expect("classification failed"); + } + let classification_time = start.elapsed(); + println!( + " 5 classifications: {:?} (avg {:?})", + classification_time, + classification_time / 5 + ); + + // Tab naming + let start = Instant::now(); + TabNamingTask + .run( + &engine, + TabNamingInput { + recent_commands: vec!["make build".into(), "make test".into()], + working_directory: "/home/user/project".into(), + }, + ) + .await + .expect("tab naming failed"); + let tab_time = start.elapsed(); + println!(" 1 tab naming: {:?}", tab_time); + + // Prompt suggestion + let start = Instant::now(); + PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec!["ls".into(), "cd src".into()], + current_input: String::new(), + working_directory: "/home/user".into(), + }, + ) + .await + .expect("prompt suggestion failed"); + let suggest_time = start.elapsed(); + println!(" 1 prompt suggestion: {:?}", suggest_time); + + // Classification should be under 2s per call on CPU (generous limit for CI) + assert!( + classification_time / 5 < std::time::Duration::from_secs(2), + "classification too slow: {:?} per call", + classification_time / 5 + ); +} + +/// Tests that the engine can be reused across many calls without degradation. +#[tokio::test] +async fn scenario_engine_stability_across_calls() { + let engine = get_engine().await; + + println!("\n--- Engine Stability Across Calls ---"); + + // Mix different task types in sequence + for i in 0..3 { + // Classification + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: format!("test command {i}"), + recent_commands: vec![], + is_follow_up: false, + }, + ) + .await + .expect("classification should not degrade"); + assert!(result.confidence > 0.0); + + // Tab naming + let name = TabNamingTask + .run( + &engine, + TabNamingInput { + recent_commands: vec![format!("cmd {i}")], + working_directory: format!("/tmp/test-{i}"), + }, + ) + .await + .expect("tab naming should not degrade"); + assert!(!name.is_empty()); + + // Prompt suggestion + let suggestions = PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec![format!("action {i}")], + current_input: String::new(), + working_directory: "/tmp".into(), + }, + ) + .await + .expect("prompt suggestion should not degrade"); + assert!(!suggestions.is_empty()); + + println!(" Round {}: all tasks passed", i + 1); + } +} diff --git a/crates/local_inference/tests/integration.rs b/crates/local_inference/tests/integration.rs new file mode 100644 index 00000000..b31957c3 --- /dev/null +++ b/crates/local_inference/tests/integration.rs @@ -0,0 +1,406 @@ +use local_inference::{ + Device, GenerationConfig, InferenceEngine, InferenceTask, InputCategory, + InputClassificationInput, InputClassificationTask, PromptSuggestionInput, PromptSuggestionTask, + TabNamingInput, TabNamingTask, +}; + +async fn get_engine() -> InferenceEngine { + InferenceEngine::new(Device::Cpu) + .await + .expect("Failed to initialize engine — is the model downloaded?") +} + +#[tokio::test] +async fn test_engine_initializes() { + let engine = get_engine().await; + assert!(engine.tokenizer().get_vocab_size(true) > 0); +} + +#[tokio::test] +async fn test_basic_generation() { + let engine = get_engine().await; + let config = GenerationConfig::deterministic(); + let output = engine + .generate("The capital of France is", &config) + .await + .expect("generation failed"); + assert!(!output.is_empty(), "generated output should not be empty"); + println!("Generated: {output}"); +} + +#[tokio::test] +async fn test_classify_shell_command() { + let engine = get_engine().await; + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: "ls -la /tmp".to_string(), + recent_commands: vec!["cd /tmp".into(), "mkdir test".into()], + is_follow_up: false, + }, + ) + .await + .expect("classification failed"); + + println!( + "\"ls -la /tmp\" -> {:?} (confidence: {:.2})", + result.category, result.confidence + ); + assert_eq!(result.category, InputCategory::Shell); +} + +#[tokio::test] +async fn test_classify_agent_prompt() { + let engine = get_engine().await; + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: "explain how async works in rust".to_string(), + recent_commands: vec!["cargo build".into()], + is_follow_up: false, + }, + ) + .await + .expect("classification failed"); + + println!( + "\"explain how async works in rust\" -> {:?} (confidence: {:.2})", + result.category, result.confidence + ); + assert_eq!(result.category, InputCategory::AgentPrompt); +} + +#[tokio::test] +async fn test_classify_ambiguous_input() { + let engine = get_engine().await; + + // "docker" alone could be either — we just want to make sure it doesn't panic + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: "docker".to_string(), + recent_commands: vec![], + is_follow_up: false, + }, + ) + .await + .expect("classification failed"); + + println!( + "\"docker\" -> {:?} (confidence: {:.2})", + result.category, result.confidence + ); + // Either classification is fine, just assert it doesn't crash + assert!(result.confidence > 0.0); +} + +#[tokio::test] +async fn test_tab_naming() { + let engine = get_engine().await; + let name = TabNamingTask + .run( + &engine, + TabNamingInput { + recent_commands: vec![ + "git log --oneline".into(), + "git diff".into(), + "git add .".into(), + ], + working_directory: "/home/user/projects/galaxy".into(), + }, + ) + .await + .expect("tab naming failed"); + + println!("Tab name: \"{name}\""); + assert!(!name.is_empty()); + // Tab names should be short + assert!( + name.split_whitespace().count() <= 6, + "tab name too long: \"{name}\"" + ); +} + +#[tokio::test] +async fn test_prompt_suggestions() { + let engine = get_engine().await; + let suggestions = PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec!["cargo build".into(), "cargo test".into()], + current_input: "cargo".to_string(), + working_directory: "/home/user/projects/galaxy".into(), + }, + ) + .await + .expect("prompt suggestion failed"); + + println!("Suggestions: {suggestions:?}"); + assert!( + !suggestions.is_empty(), + "should have at least one suggestion" + ); +} + +#[tokio::test] +async fn test_generation_respects_max_tokens() { + let engine = get_engine().await; + let config = GenerationConfig { + max_tokens: 5, + temperature: 0.0, + top_p: 1.0, + }; + + let output = engine + .generate("Once upon a time", &config) + .await + .expect("generation failed"); + + let token_count = engine + .tokenizer() + .encode(output.as_str(), false) + .map(|e| e.get_ids().len()) + .unwrap_or(0); + + println!("Generated ({token_count} tokens): \"{output}\""); + // Should be roughly around max_tokens (could be less if EOS hit) + assert!(token_count <= 6, "generated too many tokens: {token_count}"); +} + +// --- Edge case tests --- + +#[tokio::test] +async fn test_classify_empty_input() { + let engine = get_engine().await; + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: String::new(), + recent_commands: vec![], + is_follow_up: false, + }, + ) + .await + .expect("classification should not panic on empty input"); + + println!( + "empty input -> {:?} (confidence: {:.2})", + result.category, result.confidence + ); + assert!(result.confidence > 0.0); +} + +#[tokio::test] +async fn test_classify_very_long_input() { + let engine = get_engine().await; + let long_input = "a".repeat(500); + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: long_input, + recent_commands: vec!["ls".into()], + is_follow_up: false, + }, + ) + .await + .expect("classification should handle long input"); + + println!( + "long input -> {:?} (confidence: {:.2})", + result.category, result.confidence + ); + assert!(result.confidence > 0.0); +} + +#[tokio::test] +async fn test_classify_special_characters() { + let engine = get_engine().await; + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: "find . -name '*.rs' | xargs grep -l 'TODO'".to_string(), + recent_commands: vec!["grep -rn test .".into()], + is_follow_up: false, + }, + ) + .await + .expect("classification should handle special characters"); + + println!( + "pipe command -> {:?} (confidence: {:.2})", + result.category, result.confidence + ); + assert_eq!(result.category, InputCategory::Shell); +} + +#[tokio::test] +async fn test_classify_follow_up_context() { + let engine = get_engine().await; + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: "can you also add error handling?".to_string(), + recent_commands: vec!["cargo build".into()], + is_follow_up: true, + }, + ) + .await + .expect("classification should handle follow-up"); + + println!( + "follow-up -> {:?} (confidence: {:.2})", + result.category, result.confidence + ); + assert_eq!(result.category, InputCategory::AgentPrompt); +} + +#[tokio::test] +async fn test_classify_multiple_sequential_calls() { + let engine = get_engine().await; + + let inputs = vec![ + ("git status", InputCategory::Shell), + ("what does this error mean?", InputCategory::AgentPrompt), + ("npm install express", InputCategory::Shell), + ("refactor this to use async/await", InputCategory::AgentPrompt), + ]; + + for (input, expected_category) in inputs { + let result = InputClassificationTask + .run( + &engine, + InputClassificationInput { + user_input: input.to_string(), + recent_commands: vec!["ls".into()], + is_follow_up: false, + }, + ) + .await + .expect("classification failed"); + + println!("\"{input}\" -> {:?} (confidence: {:.2})", result.category, result.confidence); + assert_eq!( + result.category, expected_category, + "expected {expected_category:?} for \"{input}\", got {:?}", + result.category + ); + } +} + +#[tokio::test] +async fn test_tab_naming_empty_commands() { + let engine = get_engine().await; + let name = TabNamingTask + .run( + &engine, + TabNamingInput { + recent_commands: vec![], + working_directory: "/home/user".into(), + }, + ) + .await + .expect("tab naming should handle empty commands"); + + println!("Tab name (no commands): \"{name}\""); + assert!(!name.is_empty()); +} + +#[tokio::test] +async fn test_tab_naming_deep_directory() { + let engine = get_engine().await; + let name = TabNamingTask + .run( + &engine, + TabNamingInput { + recent_commands: vec!["python train.py".into(), "tensorboard --logdir=runs".into()], + working_directory: "/home/user/projects/ml-research/experiments/transformer-v2" + .into(), + }, + ) + .await + .expect("tab naming failed"); + + println!("Tab name (deep dir): \"{name}\""); + assert!(!name.is_empty()); + assert!( + name.split_whitespace().count() <= 6, + "tab name too long: \"{name}\"" + ); +} + +#[tokio::test] +async fn test_prompt_suggestions_empty_input() { + let engine = get_engine().await; + let suggestions = PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec!["git status".into(), "git add .".into()], + current_input: String::new(), + working_directory: "/home/user/project".into(), + }, + ) + .await + .expect("prompt suggestion should handle empty input"); + + println!("Suggestions (empty input): {suggestions:?}"); + assert!( + !suggestions.is_empty(), + "should suggest something even with empty input" + ); +} + +#[tokio::test] +async fn test_prompt_suggestions_no_history() { + let engine = get_engine().await; + let suggestions = PromptSuggestionTask + .run( + &engine, + PromptSuggestionInput { + recent_commands: vec![], + current_input: "docker".to_string(), + working_directory: "/tmp".into(), + }, + ) + .await + .expect("prompt suggestion should handle no history"); + + println!("Suggestions (no history): {suggestions:?}"); + assert!( + !suggestions.is_empty(), + "should suggest something even without history" + ); +} + +#[tokio::test] +async fn test_deterministic_classification() { + let engine = get_engine().await; + let input = InputClassificationInput { + user_input: "ls -la".to_string(), + recent_commands: vec!["cd /tmp".into()], + is_follow_up: false, + }; + + let result1 = InputClassificationTask + .run(&engine, input.clone()) + .await + .expect("first classification failed"); + let result2 = InputClassificationTask + .run(&engine, input) + .await + .expect("second classification failed"); + + // With temperature 0.0, results should be deterministic + assert_eq!( + result1.category, result2.category, + "deterministic classification should yield consistent results" + ); +} diff --git a/crates/lsp/src/config.rs b/crates/lsp/src/config.rs index 7988d2be..79de5a22 100644 --- a/crates/lsp/src/config.rs +++ b/crates/lsp/src/config.rs @@ -6,11 +6,10 @@ use anyhow::Result; #[cfg(not(target_arch = "wasm32"))] use command::r#async::Command; use lsp_types::{ - ClientCapabilities, ClientInfo, CodeActionClientCapabilities, - CompletionClientCapabilities, CompletionItemCapability, - CompletionItemCapabilityResolveSupport, DidChangeWatchedFilesClientCapabilities, - GotoCapability, HoverClientCapabilities, InitializeParams, MarkupKind, - PublishDiagnosticsClientCapabilities, RenameClientCapabilities, + ClientCapabilities, ClientInfo, CodeActionClientCapabilities, CompletionClientCapabilities, + CompletionItemCapability, CompletionItemCapabilityResolveSupport, + DidChangeWatchedFilesClientCapabilities, GotoCapability, HoverClientCapabilities, + InitializeParams, MarkupKind, PublishDiagnosticsClientCapabilities, RenameClientCapabilities, SignatureHelpClientCapabilities, TextDocumentClientCapabilities, TextDocumentSyncClientCapabilities, Uri, WindowClientCapabilities, WorkDoneProgressParams, WorkspaceClientCapabilities, WorkspaceFolder, @@ -341,10 +340,7 @@ fn default_client_capabilities() -> ClientCapabilities { dynamic_registration: Some(false), completion_item: Some(CompletionItemCapability { snippet_support: Some(true), - documentation_format: Some(vec![ - MarkupKind::Markdown, - MarkupKind::PlainText, - ]), + documentation_format: Some(vec![MarkupKind::Markdown, MarkupKind::PlainText]), resolve_support: Some(CompletionItemCapabilityResolveSupport { properties: vec![ "documentation".into(), diff --git a/crates/lsp/src/model.rs b/crates/lsp/src/model.rs index 4d7d90f3..b53ffc1b 100644 --- a/crates/lsp/src/model.rs +++ b/crates/lsp/src/model.rs @@ -749,14 +749,20 @@ impl LspServerModel { CompletionTrigger::TriggerCharacter(ch) => { (CompletionTriggerKind::TRIGGER_CHARACTER, Some(ch)) } - CompletionTrigger::Incomplete => { - (CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS, None) - } + CompletionTrigger::Incomplete => ( + CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS, + None, + ), }; Ok(async move { service .text_document() - .completion(&path, position.into_lsp(), Some(trigger_kind), trigger_character) + .completion( + &path, + position.into_lsp(), + Some(trigger_kind), + trigger_character, + ) .await }) } diff --git a/crates/lsp/src/service.rs b/crates/lsp/src/service.rs index 243fda43..aec6e136 100644 --- a/crates/lsp/src/service.rs +++ b/crates/lsp/src/service.rs @@ -780,10 +780,7 @@ impl<'a> TextDocumentService<'a> { Ok(result?.map(Into::into)) } - pub async fn completion_resolve( - &self, - item: CompletionItem, - ) -> anyhow::Result { + pub async fn completion_resolve(&self, item: CompletionItem) -> anyhow::Result { let result = self .service .send_request::(item) @@ -931,10 +928,7 @@ impl<'a> TextDocumentService<'a> { work_done_progress_params: Default::default(), }; - let result = self - .service - .send_request::(params) - .await; + let result = self.service.send_request::(params).await; if let Err(e) = &result { self.service.log_to_server_log( @@ -975,16 +969,18 @@ fn workspace_edit_to_file_edits( let path = lsp_uri_to_path(&edit.text_document.uri)?; result.push(FileEdits { path, - edits: edit.edits.into_iter().map(|e| match e { - lsp_types::OneOf::Left(text_edit) => text_edit.into(), - lsp_types::OneOf::Right(annotated) => { - lsp_types::TextEdit { + edits: edit + .edits + .into_iter() + .map(|e| match e { + lsp_types::OneOf::Left(text_edit) => text_edit.into(), + lsp_types::OneOf::Right(annotated) => lsp_types::TextEdit { range: annotated.text_edit.range, new_text: annotated.text_edit.new_text, } - .into() - } - }).collect(), + .into(), + }) + .collect(), }); } } diff --git a/crates/onboarding/examples/callout.rs b/crates/onboarding/examples/callout.rs index fd0ae8c6..a006cbc8 100644 --- a/crates/onboarding/examples/callout.rs +++ b/crates/onboarding/examples/callout.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, Result}; use galaxy_core::ui::appearance::Appearance; -use galaxy_core::ui::theme::{AnsiColor, AnsiColors, Details, Fill, TerminalColors, GalaxyTheme}; +use galaxy_core::ui::theme::{AnsiColor, AnsiColors, Details, Fill, GalaxyTheme, TerminalColors}; use galaxyui::color::ColorU; use galaxyui::elements::{Rect, Stack}; use galaxyui::fonts::{Cache, FamilyId, Weight}; diff --git a/crates/onboarding/examples/callout_flow.rs b/crates/onboarding/examples/callout_flow.rs index 096d4d27..2284db18 100644 --- a/crates/onboarding/examples/callout_flow.rs +++ b/crates/onboarding/examples/callout_flow.rs @@ -1,6 +1,6 @@ use anyhow::Result; use galaxy_core::ui::appearance::Appearance; -use galaxy_core::ui::theme::{AnsiColor, AnsiColors, Details, Fill, TerminalColors, GalaxyTheme}; +use galaxy_core::ui::theme::{AnsiColor, AnsiColors, Details, Fill, GalaxyTheme, TerminalColors}; use galaxyui::fonts::{Cache, FamilyId, Weight}; use galaxyui::platform; use galaxyui::prelude::CrossAxisAlignment; diff --git a/crates/persistence/src/model.rs b/crates/persistence/src/model.rs index 885a9cfa..fbc4cac3 100644 --- a/crates/persistence/src/model.rs +++ b/crates/persistence/src/model.rs @@ -1041,6 +1041,12 @@ pub struct AgentConversationData { /// delivery without re-delivering already-processed events. #[serde(default, skip_serializing_if = "Option::is_none")] pub last_event_sequence: Option, + /// Progressive summary of older conversation messages. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub progressive_summary: Option, + /// Number of messages that were summarized into progressive_summary. + #[serde(default)] + pub messages_summarized_up_to: usize, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -1358,6 +1364,8 @@ mod tests { run_id: None, autoexecute_override: None, last_event_sequence: Some(42), + progressive_summary: None, + messages_summarized_up_to: 0, }; let json = serde_json::to_string(&data).expect("serialize"); let roundtripped: AgentConversationData = serde_json::from_str(&json).expect("deserialize"); @@ -1388,6 +1396,8 @@ mod tests { run_id: None, autoexecute_override: None, last_event_sequence: None, + progressive_summary: None, + messages_summarized_up_to: 0, }; let json = serde_json::to_string(&data).expect("serialize"); assert!(