diff --git a/Cargo.lock b/Cargo.lock index a7de0d5..be96ebf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,8 @@ dependencies = [ "tokio-util", "tower-cookies", "tower-http 0.5.2", + "tracing", + "tracing-subscriber", "uuid", ] @@ -128,9 +130,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" @@ -140,9 +142,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arc-swap" -version = "1.9.2" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" dependencies = [ "rustversion", ] @@ -161,9 +163,9 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.8" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" [[package]] name = "async-stream" @@ -554,9 +556,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.66" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", @@ -589,9 +591,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -1422,9 +1424,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 5.3.0", "wasip2", + "wasm-bindgen", ] [[package]] @@ -1434,11 +1438,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasm-bindgen", ] [[package]] @@ -1602,9 +1604,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ "typenum", ] @@ -1953,9 +1955,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ "cfg-if", "futures-util", @@ -1994,9 +1996,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -2246,12 +2248,14 @@ dependencies = [ [[package]] name = "omniference" -version = "0.2.1" +version = "0.3.0" +source = "git+https://github.com/Henrik-3/omniference.git?rev=fd69edd438430d68f9b81b5570033b12d33a4fd2#fd69edd438430d68f9b81b5570033b12d33a4fd2" dependencies = [ "anyhow", "async-stream", "async-trait", "axum 0.7.9", + "base64", "bytes", "dotenvy", "futures", @@ -2454,7 +2458,7 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand 0.10.2", + "rand 0.10.1", "sha2 0.11.0", "stringprep", ] @@ -2611,15 +2615,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.16" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" dependencies = [ "bytes", - "getrandom 0.4.3", + "getrandom 0.3.4", "lru-slab", - "rand 0.10.2", - "rand_pcg", + "rand 0.9.4", "ring", "rustc-hash", "rustls", @@ -2633,16 +2636,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.15" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2679,15 +2682,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] [[package]] name = "rand" -version = "0.10.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", "getrandom 0.4.3", @@ -2704,6 +2717,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -2715,18 +2738,18 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] [[package]] -name = "rand_pcg" -version = "0.10.2" +name = "rand_core" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" -dependencies = [ - "rand_core 0.10.1", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_xoshiro" @@ -2965,9 +2988,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.3" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustix" @@ -3022,9 +3045,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -3461,7 +3484,7 @@ dependencies = [ "log", "md-5", "memchr", - "rand 0.10.2", + "rand 0.10.1", "rust_decimal", "serde", "serde_json", @@ -3688,9 +3711,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", "num-conv", @@ -3708,9 +3731,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", @@ -4205,9 +4228,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.4" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -4285,9 +4308,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -4299,9 +4322,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ "js-sys", "wasm-bindgen", @@ -4309,9 +4332,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4319,9 +4342,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", @@ -4332,9 +4355,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] @@ -4718,9 +4741,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -5219,3 +5242,7 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "omniference" +version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 21166cd..e25fc3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ reqwest = { version = "0.12", default-features = false, features = [ "multipart", ] } tower-http = { version = "0.5", features = ["cors"] } -omniference = "0.2" +omniference = { git = "https://github.com/Henrik-3/omniference.git", rev = "fd69edd438430d68f9b81b5570033b12d33a4fd2" } aes-gcm = "0.10" rand = "0.8" rust_decimal = { version = "1.39.0", features = ["db-postgres", "serde"] } @@ -45,6 +45,8 @@ futures-util = "0.3.31" async-stream = "0.3.6" tokio-util = "0.7.17" extism = "1" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } sha2 = "0.10" base64 = "0.22" thiserror = "2" diff --git a/frontend/components/AppSidebar.vue b/frontend/components/AppSidebar.vue index 984d1bb..dc3c342 100644 --- a/frontend/components/AppSidebar.vue +++ b/frontend/components/AppSidebar.vue @@ -113,6 +113,7 @@ const store = useMainStore(); const chatStore = useChatStore(); const budgetStore = useBudgetStore(); const router = useRouter(); +const route = useRoute(); const showWorkspaceManager = ref(false); @@ -122,6 +123,7 @@ const userInitials = computed(() => { }); function goToSettings() { + store.settingsReturnPath = route.path.startsWith('/chats/') ? route.fullPath : '/'; router.push('/settings'); } diff --git a/frontend/components/chat/MessageItem.vue b/frontend/components/chat/MessageItem.vue index 69f87bf..40f29da 100644 --- a/frontend/components/chat/MessageItem.vue +++ b/frontend/components/chat/MessageItem.vue @@ -11,7 +11,7 @@
@@ -21,7 +21,7 @@
-
+
@@ -31,7 +31,53 @@
-
+
+ +
+ +
-
+
@@ -224,8 +270,8 @@ const attachedImages = computed(() => { return []; } return props.message.content_parts - .filter((part: any) => part.type === 'image' && part.image_id) - .map((part: any) => ({ + .filter(part => part.type === 'image') + .map(part => ({ image_id: part.image_id, url: `/api/v1/images/${part.image_id}`, })); @@ -281,6 +327,55 @@ const renderedContent = computed(() => { return renderComplete(props.message.content); }); +const hasInterleavedParts = computed(() => { + if (isUser.value) return false; + const parts = props.message.content_parts; + return Array.isArray(parts) && parts.some(p => p.type === 'tool_call'); +}); + +const partHtmlCache = new Map(); + +const orderedParts = computed(() => { + const parts = props.message.content_parts; + if (!Array.isArray(parts)) return []; + return parts.filter(part => part.type !== 'tool_result').map((part, index) => { + if (part.type === 'tool_call') { + const tool = props.message.tool_calls?.find(tc => tc.tool_call_id === part.id); + return {type: 'tool_call' as const, tool}; + } + if (part.type === 'image') { + return { + type: 'image' as const, + imageId: part.image_id, + url: `/api/v1/images/${encodeURIComponent(part.image_id)}`, + }; + } + if (part.type === 'reasoning') { + const text = part.text; + const cached = partHtmlCache.get(index); + const html = cached?.text === text && cached.streaming === isStreaming.value ? cached.html : isStreaming.value ? renderStreaming(text) : renderComplete(text); + partHtmlCache.set(index, {text, streaming: isStreaming.value, html}); + return {type: 'reasoning' as const, html}; + } + const text = part.text; + const cached = partHtmlCache.get(index); + const html = cached?.text === text && cached.streaming === isStreaming.value ? cached.html : isStreaming.value ? renderStreaming(text) : renderComplete(text); + partHtmlCache.set(index, {text, streaming: isStreaming.value, html}); + return {type: 'text' as const, html}; + }); +}); + +const reasoningToggles = ref>({}); + +function isReasoningOpen(idx: number): boolean { + if (idx in reasoningToggles.value) return reasoningToggles.value[idx]; + return isStreaming.value && idx === orderedParts.value.length - 1; +} + +function toggleReasoning(idx: number) { + reasoningToggles.value[idx] = !isReasoningOpen(idx); +} + const renderedReasoning = computed(() => { if (!props.message.reasoning_content) return ''; diff --git a/frontend/components/chat/ToolExecutionDisplay.vue b/frontend/components/chat/ToolExecutionDisplay.vue index 1d407f8..f777255 100644 --- a/frontend/components/chat/ToolExecutionDisplay.vue +++ b/frontend/components/chat/ToolExecutionDisplay.vue @@ -131,6 +131,9 @@ function isValidImageProtocol(url: string): boolean { const imageUrl = computed(() => { if (!props.output) return null; const out = typeof props.output === 'string' ? tryParseJson(props.output) : props.output; + if (out?.image_id && typeof out.image_id === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(out.image_id)) { + return `/api/v1/images/${encodeURIComponent(out.image_id)}`; + } if (out?.image_url && isValidImageProtocol(out.image_url)) return out.image_url; if (out?.image_reference && isValidImageProtocol(out.image_reference)) return out.image_reference; if (out?.url && isImageUrl(out.url)) return out.url; diff --git a/frontend/components/settings/DefaultModelPicker.vue b/frontend/components/settings/DefaultModelPicker.vue index 3f1f5c7..a157cf9 100644 --- a/frontend/components/settings/DefaultModelPicker.vue +++ b/frontend/components/settings/DefaultModelPicker.vue @@ -3,7 +3,7 @@