llms-py 2.0.34__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- llms/__init__.py +3 -1
- llms/__pycache__/__init__.cpython-312.pyc +0 -0
- llms/__pycache__/__init__.cpython-313.pyc +0 -0
- llms/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/__pycache__/__main__.cpython-312.pyc +0 -0
- llms/__pycache__/__main__.cpython-314.pyc +0 -0
- llms/__pycache__/llms.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-313.pyc +0 -0
- llms/__pycache__/main.cpython-314.pyc +0 -0
- llms/__pycache__/plugins.cpython-314.pyc +0 -0
- llms/{ui/Analytics.mjs → extensions/analytics/ui/index.mjs} +154 -238
- llms/extensions/app/README.md +20 -0
- llms/extensions/app/__init__.py +530 -0
- llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
- llms/extensions/app/db.py +644 -0
- llms/extensions/app/db_manager.py +195 -0
- llms/extensions/app/requests.json +9073 -0
- llms/extensions/app/threads.json +15290 -0
- llms/{ui → extensions/app/ui}/Recents.mjs +91 -65
- llms/{ui/Sidebar.mjs → extensions/app/ui/index.mjs} +124 -58
- llms/extensions/app/ui/threadStore.mjs +411 -0
- llms/extensions/core_tools/CALCULATOR.md +32 -0
- llms/extensions/core_tools/__init__.py +598 -0
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
- llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +344 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +9884 -0
- llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
- llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
- llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
- llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
- llms/extensions/core_tools/ui/index.mjs +650 -0
- llms/extensions/gallery/README.md +61 -0
- llms/extensions/gallery/__init__.py +61 -0
- llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/gallery/db.py +298 -0
- llms/extensions/gallery/ui/index.mjs +482 -0
- llms/extensions/katex/README.md +39 -0
- llms/extensions/katex/__init__.py +6 -0
- llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/katex/ui/README.md +125 -0
- llms/extensions/katex/ui/contrib/auto-render.js +338 -0
- llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
- llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
- llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
- llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
- llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
- llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
- llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
- llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- llms/extensions/katex/ui/index.mjs +92 -0
- llms/extensions/katex/ui/katex-swap.css +1230 -0
- llms/extensions/katex/ui/katex-swap.min.css +1 -0
- llms/extensions/katex/ui/katex.css +1230 -0
- llms/extensions/katex/ui/katex.js +19080 -0
- llms/extensions/katex/ui/katex.min.css +1 -0
- llms/extensions/katex/ui/katex.min.js +1 -0
- llms/extensions/katex/ui/katex.min.mjs +1 -0
- llms/extensions/katex/ui/katex.mjs +18547 -0
- llms/extensions/providers/__init__.py +18 -0
- llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
- llms/extensions/providers/anthropic.py +229 -0
- llms/extensions/providers/chutes.py +155 -0
- llms/extensions/providers/google.py +378 -0
- llms/extensions/providers/nvidia.py +105 -0
- llms/extensions/providers/openai.py +156 -0
- llms/extensions/providers/openrouter.py +72 -0
- llms/extensions/system_prompts/README.md +22 -0
- llms/extensions/system_prompts/__init__.py +45 -0
- llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/system_prompts/ui/index.mjs +280 -0
- llms/extensions/system_prompts/ui/prompts.json +1067 -0
- llms/extensions/tools/__init__.py +5 -0
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/ui/index.mjs +204 -0
- llms/index.html +35 -77
- llms/llms.json +357 -1186
- llms/main.py +2847 -999
- llms/providers-extra.json +356 -0
- llms/providers.json +1 -0
- llms/ui/App.mjs +151 -60
- llms/ui/ai.mjs +132 -60
- llms/ui/app.css +2173 -161
- llms/ui/ctx.mjs +365 -0
- llms/ui/index.mjs +129 -0
- llms/ui/lib/charts.mjs +9 -13
- llms/ui/lib/servicestack-vue.mjs +3 -3
- llms/ui/lib/vue.min.mjs +10 -9
- llms/ui/lib/vue.mjs +1796 -1635
- llms/ui/markdown.mjs +18 -7
- llms/ui/modules/chat/ChatBody.mjs +691 -0
- llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +9 -9
- llms/ui/modules/chat/index.mjs +828 -0
- llms/ui/modules/layout.mjs +243 -0
- llms/ui/modules/model-selector.mjs +851 -0
- llms/ui/tailwind.input.css +496 -80
- llms/ui/utils.mjs +161 -93
- {llms_py-2.0.34.dist-info → llms_py-3.0.0.dist-info}/METADATA +1 -1
- llms_py-3.0.0.dist-info/RECORD +202 -0
- llms/ui/Avatar.mjs +0 -85
- llms/ui/Brand.mjs +0 -52
- llms/ui/ChatPrompt.mjs +0 -590
- llms/ui/Main.mjs +0 -823
- llms/ui/ModelSelector.mjs +0 -78
- llms/ui/OAuthSignIn.mjs +0 -92
- llms/ui/ProviderIcon.mjs +0 -30
- llms/ui/ProviderStatus.mjs +0 -105
- llms/ui/SignIn.mjs +0 -64
- llms/ui/SystemPromptEditor.mjs +0 -31
- llms/ui/SystemPromptSelector.mjs +0 -56
- llms/ui/Welcome.mjs +0 -8
- llms/ui/threadStore.mjs +0 -563
- llms/ui.json +0 -1069
- llms_py-2.0.34.dist-info/RECORD +0 -48
- {llms_py-2.0.34.dist-info → llms_py-3.0.0.dist-info}/WHEEL +0 -0
- {llms_py-2.0.34.dist-info → llms_py-3.0.0.dist-info}/entry_points.txt +0 -0
- {llms_py-2.0.34.dist-info → llms_py-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {llms_py-2.0.34.dist-info → llms_py-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { ref, onMounted, watch, inject } from 'vue'
|
|
2
2
|
import { useRouter, useRoute } from 'vue-router'
|
|
3
|
-
import { useThreadStore } from './threadStore.mjs'
|
|
4
|
-
import { renderMarkdown } from './markdown.mjs'
|
|
5
3
|
|
|
6
4
|
const RecentResults = {
|
|
7
|
-
template
|
|
5
|
+
template: `
|
|
8
6
|
<div class="flex-1 overflow-y-auto" @scroll="onScroll">
|
|
9
7
|
<div class="mx-auto max-w-6xl px-4 py-4">
|
|
10
|
-
<div class="text-sm text-gray-600 dark:text-gray-400 mb-3"
|
|
11
|
-
<span v-if="q">{{
|
|
12
|
-
<span v-else>
|
|
8
|
+
<div class="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
|
9
|
+
<span v-if="q">{{ total }} result{{ total===1?'':'s' }}</span>
|
|
10
|
+
<span v-else>All conversations</span>
|
|
13
11
|
</div>
|
|
14
12
|
|
|
15
|
-
<div v-if="!threads.length" class="text-gray-500 dark:text-gray-400">No conversations
|
|
13
|
+
<div v-if="!loading && threads.length === 0" class="text-gray-500 dark:text-gray-400">No conversations found.</div>
|
|
16
14
|
|
|
17
15
|
<table class="w-full">
|
|
18
16
|
<tbody>
|
|
19
|
-
<tr v-for="t in
|
|
20
|
-
<td class="py-3 px-1 border-b border-gray-200 dark:border-gray-700 max-w-
|
|
17
|
+
<tr v-for="t in threads" :key="t.id" class="hover:bg-gray-50 dark:hover:bg-gray-800">
|
|
18
|
+
<td class="py-3 px-1 border-b border-gray-200 dark:border-gray-700 max-w-2xl">
|
|
21
19
|
<button type="button" @click="open(t.id)" class="w-full text-left">
|
|
22
20
|
<div class="flex items-start justify-between gap-3">
|
|
23
21
|
<div class="min-w-0 flex-1">
|
|
@@ -33,12 +31,13 @@ const RecentResults = {
|
|
|
33
31
|
<div class="text-right whitespace-nowrap">
|
|
34
32
|
<div class="text-xs text-gray-500 dark:text-gray-400">{{ formatDate(t.updatedAt || t.createdAt) }}</div>
|
|
35
33
|
<div class="text-[11px] text-gray-500/80 dark:text-gray-400/80">{{ (t.messages?.length || 0) }} messages</div>
|
|
36
|
-
<div v-if="t.model" class="text-[11px] text-blue-600 dark:text-blue-400">{{ t.model }}</div>
|
|
34
|
+
<div v-if="t.model" class="text-[11px] text-blue-600 dark:text-blue-400 max-w-[140px] truncate" :title="t.model">{{ t.model }}</div>
|
|
37
35
|
</div>
|
|
38
36
|
</td>
|
|
39
37
|
</tr>
|
|
40
38
|
</tbody>
|
|
41
39
|
</table>
|
|
40
|
+
<div v-if="loading" class="py-4 text-center text-gray-500 dark:text-gray-400">Loading...</div>
|
|
42
41
|
</div>
|
|
43
42
|
</div>
|
|
44
43
|
`,
|
|
@@ -46,100 +45,126 @@ const RecentResults = {
|
|
|
46
45
|
q: String
|
|
47
46
|
},
|
|
48
47
|
setup(props) {
|
|
49
|
-
const
|
|
48
|
+
const ctx = inject('ctx')
|
|
49
|
+
const ai = ctx.ai
|
|
50
50
|
const router = useRouter()
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
|
|
52
|
+
const threads = ref([])
|
|
53
|
+
const loading = ref(false)
|
|
54
|
+
const noMore = ref(false)
|
|
55
|
+
const total = ref(0)
|
|
56
|
+
let skip = 0
|
|
57
|
+
const take = 25
|
|
58
|
+
|
|
59
|
+
// Simple debounce function
|
|
60
|
+
function debounce(fn, delay) {
|
|
61
|
+
let timeoutID = null
|
|
62
|
+
return function () {
|
|
63
|
+
clearTimeout(timeoutID)
|
|
64
|
+
timeoutID = setTimeout(() => fn.apply(this, arguments), delay)
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
console.log('end', Date.now() - start)
|
|
68
|
-
})
|
|
66
|
+
}
|
|
69
67
|
|
|
70
68
|
const normalized = (s) => (s || '').toString().toLowerCase()
|
|
71
|
-
|
|
72
69
|
const replaceChars = new Set('<>`*|#'.split(''))
|
|
73
70
|
const clean = s => [...s].map(c => replaceChars.has(c) ? ' ' : c).join('')
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
const loadMore = async (reset = false) => {
|
|
73
|
+
if (reset) {
|
|
74
|
+
skip = 0
|
|
75
|
+
threads.value = []
|
|
76
|
+
noMore.value = false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (loading.value || noMore.value) return
|
|
80
|
+
|
|
81
|
+
loading.value = true
|
|
82
|
+
try {
|
|
83
|
+
const query = {
|
|
84
|
+
take,
|
|
85
|
+
skip,
|
|
86
|
+
...(props.q ? { q: props.q } : {})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const results = await ctx.threads.query(query)
|
|
90
|
+
|
|
91
|
+
if (results.length < take) {
|
|
92
|
+
noMore.value = true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (reset) {
|
|
96
|
+
threads.value = results
|
|
97
|
+
} else {
|
|
98
|
+
threads.value.push(...results)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
skip += results.length
|
|
102
|
+
|
|
103
|
+
total.value = threads.value.length
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.error("Failed to load threads", e)
|
|
106
|
+
} finally {
|
|
107
|
+
loading.value = false
|
|
108
|
+
}
|
|
89
109
|
}
|
|
90
110
|
|
|
111
|
+
const update = debounce(() => loadMore(true), 250)
|
|
112
|
+
|
|
113
|
+
onMounted(() => {
|
|
114
|
+
loadMore(true)
|
|
115
|
+
})
|
|
116
|
+
|
|
91
117
|
const onScroll = (e) => {
|
|
92
118
|
const el = e.target
|
|
93
|
-
if (el.scrollTop + el.clientHeight >= el.scrollHeight -
|
|
94
|
-
|
|
95
|
-
visibleCount.value = Math.min(visibleCount.value + defaultVisibleCount, filtered.value.length)
|
|
96
|
-
updateVisible()
|
|
97
|
-
}
|
|
119
|
+
if (el.scrollTop + el.clientHeight >= el.scrollHeight - 50) { // 50px threshold
|
|
120
|
+
loadMore()
|
|
98
121
|
}
|
|
99
122
|
}
|
|
100
123
|
|
|
101
124
|
watch(() => props.q, () => {
|
|
102
|
-
visibleCount.value = defaultVisibleCount
|
|
103
125
|
update()
|
|
104
126
|
})
|
|
105
127
|
|
|
106
128
|
const snippet = (t) => {
|
|
107
129
|
const highlight = (s) => clean(s).replace(new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'), `<mark>$1</mark>`)
|
|
108
130
|
const query = normalized(props.q)
|
|
109
|
-
if (!query) return (t.messages && t.messages.length) ? highlight(t.messages[t.messages.length-1].content) : ''
|
|
131
|
+
if (!query) return (t.messages && t.messages.length) ? highlight(t.messages[t.messages.length - 1].content) : ''
|
|
132
|
+
|
|
133
|
+
// Check title
|
|
110
134
|
if (normalized(t.title).includes(query)) return highlight(t.title)
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
|
|
136
|
+
// Check messages
|
|
137
|
+
if (Array.isArray(t.messages)) {
|
|
138
|
+
for (const m of t.messages) {
|
|
113
139
|
const c = normalized(m?.content)
|
|
114
|
-
if (c.includes(query)){
|
|
140
|
+
if (c.includes(query)) {
|
|
115
141
|
// return small excerpt around first match
|
|
116
142
|
const idx = c.indexOf(query)
|
|
117
143
|
const orig = (m?.content || '')
|
|
118
144
|
const start = Math.max(0, idx - 40)
|
|
119
145
|
const end = Math.min(orig.length, idx + query.length + 60)
|
|
120
|
-
const prefix = start>0 ? '…' : ''
|
|
121
|
-
const suffix = end<orig.length ? '…' : ''
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
return highlight(snippet)
|
|
146
|
+
const prefix = start > 0 ? '…' : ''
|
|
147
|
+
const suffix = end < orig.length ? '…' : ''
|
|
148
|
+
const snippetText = prefix + orig.slice(start, end) + suffix
|
|
149
|
+
return highlight(snippetText)
|
|
125
150
|
}
|
|
126
151
|
}
|
|
127
152
|
}
|
|
128
|
-
|
|
153
|
+
|
|
154
|
+
// Fallback to last message if no specific match found (e.g. matched on hidden metadata or partial?)
|
|
155
|
+
return (t.messages && t.messages.length) ? highlight(t.messages[t.messages.length - 1].content) : ''
|
|
129
156
|
}
|
|
130
157
|
|
|
131
158
|
const open = (id) => router.push(`${ai.base}/c/${id}`)
|
|
132
159
|
const formatDate = (iso) => new Date(iso).toLocaleString()
|
|
133
160
|
|
|
134
161
|
return {
|
|
135
|
-
config,
|
|
136
162
|
threads,
|
|
137
|
-
|
|
138
|
-
|
|
163
|
+
loading,
|
|
164
|
+
total,
|
|
139
165
|
snippet,
|
|
140
166
|
open,
|
|
141
167
|
formatDate,
|
|
142
|
-
renderMarkdown,
|
|
143
168
|
onScroll,
|
|
144
169
|
}
|
|
145
170
|
}
|
|
@@ -154,9 +179,10 @@ export default {
|
|
|
154
179
|
<!-- Header -->
|
|
155
180
|
<div class="border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-4 py-3 min-h-16">
|
|
156
181
|
<div class="max-w-6xl mx-auto flex items-center justify-between gap-3">
|
|
157
|
-
<
|
|
158
|
-
<div class="flex-1 flex items-center gap-2">
|
|
182
|
+
<label for="search-history" class="cursor-pointer text-lg font-semibold text-gray-900 dark:text-gray-100">Search History</label>
|
|
183
|
+
<div class="flex-1 flex items-center gap-2 max-w-sm">
|
|
159
184
|
<input
|
|
185
|
+
id="search-history"
|
|
160
186
|
v-model="q"
|
|
161
187
|
type="search"
|
|
162
188
|
placeholder="Search titles and messages..."
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { onMounted, inject } from 'vue'
|
|
2
2
|
import { useRouter } from 'vue-router'
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { statsTitle, formatCost } from './utils.mjs'
|
|
3
|
+
import { appendQueryString } from '@servicestack/client'
|
|
4
|
+
import ThreadStore from './threadStore.mjs'
|
|
5
|
+
import Recents from './Recents.mjs'
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
let ext
|
|
9
8
|
|
|
10
9
|
// Thread Item Component
|
|
11
10
|
const ThreadItem = {
|
|
@@ -21,10 +20,10 @@ const ThreadItem = {
|
|
|
21
20
|
{{ thread.title }}
|
|
22
21
|
</div>
|
|
23
22
|
<div class="text-xs text-gray-500 dark:text-gray-400 truncate">
|
|
24
|
-
<span>{{
|
|
25
|
-
<span v-if="thread.stats?.inputTokens" :title="statsTitle(thread.stats)">
|
|
26
|
-
• {{ humanifyNumber(thread.stats.inputTokens + thread.stats.outputTokens) }} toks
|
|
27
|
-
{{ thread.stats.cost ? ' ' +
|
|
23
|
+
<span>{{ $fmt.relativeTime(thread.updatedAt) }} • {{ thread.messages.length }} msgs</span>
|
|
24
|
+
<span v-if="thread.stats?.inputTokens" :title="$fmt.statsTitle(thread.stats)">
|
|
25
|
+
• {{ $fmt.humanifyNumber(thread.stats.inputTokens + thread.stats.outputTokens) }} toks
|
|
26
|
+
{{ thread.stats.cost ? ' ' + $fmt.cost(thread.stats.cost) : '' }}
|
|
28
27
|
</span>
|
|
29
28
|
</div>
|
|
30
29
|
<div v-if="thread.model" class="text-xs text-blue-600 dark:text-blue-400 truncate">
|
|
@@ -60,32 +59,12 @@ const ThreadItem = {
|
|
|
60
59
|
emits: ['select', 'delete'],
|
|
61
60
|
|
|
62
61
|
setup() {
|
|
63
|
-
const formatRelativeTime = (timestamp) => {
|
|
64
|
-
const now = new Date()
|
|
65
|
-
const date = new Date(timestamp)
|
|
66
|
-
const diffInSeconds = Math.floor((now - date) / 1000)
|
|
67
|
-
|
|
68
|
-
if (diffInSeconds < 60) return 'Just now'
|
|
69
|
-
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`
|
|
70
|
-
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`
|
|
71
|
-
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`
|
|
72
|
-
|
|
73
|
-
return date.toLocaleDateString()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
62
|
return {
|
|
77
|
-
formatRelativeTime,
|
|
78
|
-
humanifyNumber,
|
|
79
|
-
statsTitle,
|
|
80
|
-
formatCost,
|
|
81
63
|
}
|
|
82
64
|
}
|
|
83
65
|
}
|
|
84
66
|
|
|
85
67
|
const GroupedThreads = {
|
|
86
|
-
components: {
|
|
87
|
-
ThreadItem,
|
|
88
|
-
},
|
|
89
68
|
template: `
|
|
90
69
|
<!-- Today -->
|
|
91
70
|
<div v-if="groupedThreads.today.length > 0" class="mb-4">
|
|
@@ -94,7 +73,20 @@ const GroupedThreads = {
|
|
|
94
73
|
v-for="thread in groupedThreads.today"
|
|
95
74
|
:key="thread.id"
|
|
96
75
|
:thread="thread"
|
|
97
|
-
:is-active="currentThread?.id
|
|
76
|
+
:is-active="currentThread?.id == thread.id"
|
|
77
|
+
@select="$emit('select', $event)"
|
|
78
|
+
@delete="$emit('delete', $event)"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<!-- Yesterday -->
|
|
83
|
+
<div v-if="groupedThreads.yesterday.length > 0" class="mb-4">
|
|
84
|
+
<h3 class="px-4 py-2 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider select-none">Yesterday</h3>
|
|
85
|
+
<ThreadItem
|
|
86
|
+
v-for="thread in groupedThreads.yesterday"
|
|
87
|
+
:key="thread.id"
|
|
88
|
+
:thread="thread"
|
|
89
|
+
:is-active="currentThread?.id == thread.id"
|
|
98
90
|
@select="$emit('select', $event)"
|
|
99
91
|
@delete="$emit('delete', $event)"
|
|
100
92
|
/>
|
|
@@ -107,7 +99,7 @@ const GroupedThreads = {
|
|
|
107
99
|
v-for="thread in groupedThreads.lastWeek"
|
|
108
100
|
:key="thread.id"
|
|
109
101
|
:thread="thread"
|
|
110
|
-
:is-active="currentThread?.id
|
|
102
|
+
:is-active="currentThread?.id == thread.id"
|
|
111
103
|
@select="$emit('select', $event)"
|
|
112
104
|
@delete="$emit('delete', $event)"
|
|
113
105
|
/>
|
|
@@ -120,7 +112,7 @@ const GroupedThreads = {
|
|
|
120
112
|
v-for="thread in groupedThreads.lastMonth"
|
|
121
113
|
:key="thread.id"
|
|
122
114
|
:thread="thread"
|
|
123
|
-
:is-active="currentThread?.id
|
|
115
|
+
:is-active="currentThread?.id == thread.id"
|
|
124
116
|
@select="$emit('select', $event)"
|
|
125
117
|
@delete="$emit('delete', $event)"
|
|
126
118
|
/>
|
|
@@ -133,7 +125,7 @@ const GroupedThreads = {
|
|
|
133
125
|
v-for="thread in monthThreads"
|
|
134
126
|
:key="thread.id"
|
|
135
127
|
:thread="thread"
|
|
136
|
-
:is-active="currentThread?.id
|
|
128
|
+
:is-active="currentThread?.id == thread.id"
|
|
137
129
|
@select="$emit('select', $event)"
|
|
138
130
|
@delete="$emit('delete', $event)"
|
|
139
131
|
/>
|
|
@@ -156,15 +148,10 @@ const GroupedThreads = {
|
|
|
156
148
|
emits: ['select', 'delete'],
|
|
157
149
|
}
|
|
158
150
|
|
|
159
|
-
const
|
|
160
|
-
components: {
|
|
161
|
-
Brand,
|
|
162
|
-
GroupedThreads,
|
|
163
|
-
ThreadItem,
|
|
164
|
-
},
|
|
151
|
+
const ThreadsSidebar = {
|
|
165
152
|
template: `
|
|
166
|
-
<div class="flex flex-col h-full
|
|
167
|
-
<Brand
|
|
153
|
+
<div class="flex flex-col h-full">
|
|
154
|
+
<Brand />
|
|
168
155
|
<!-- Thread List -->
|
|
169
156
|
<div class="flex-1 overflow-y-auto">
|
|
170
157
|
<div v-if="isLoading" class="p-4 text-center text-gray-500 dark:text-gray-400">
|
|
@@ -180,18 +167,28 @@ const Sidebar = {
|
|
|
180
167
|
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">Start a new chat to begin</p>
|
|
181
168
|
</div>
|
|
182
169
|
|
|
183
|
-
<div v-else class="py-2">
|
|
184
|
-
|
|
170
|
+
<div v-else class="relative py-2">
|
|
171
|
+
|
|
172
|
+
<div class="flex items-center space-x-2 absolute top-2 right-2">
|
|
173
|
+
<button type="button"
|
|
174
|
+
@click="createNewThread"
|
|
175
|
+
class="text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
|
|
176
|
+
title="New Chat"
|
|
177
|
+
>
|
|
178
|
+
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></g></svg>
|
|
179
|
+
</button>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<GroupedThreads :currentThread="currentThread" :groupedThreads="$threads.getGroupedThreads(50)"
|
|
185
183
|
@select="selectThread" @delete="deleteThread" />
|
|
186
184
|
</div>
|
|
187
185
|
</div>
|
|
188
186
|
</div>
|
|
189
187
|
`,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const ai =
|
|
188
|
+
setup(props) {
|
|
189
|
+
const ctx = inject('ctx')
|
|
190
|
+
const ai = ctx.ai
|
|
193
191
|
const router = useRouter()
|
|
194
|
-
const threadStore = useThreadStore()
|
|
195
192
|
const {
|
|
196
193
|
threads,
|
|
197
194
|
currentThread,
|
|
@@ -201,7 +198,7 @@ const Sidebar = {
|
|
|
201
198
|
createThread,
|
|
202
199
|
deleteThread: deleteThreadFromStore,
|
|
203
200
|
clearCurrentThread
|
|
204
|
-
} =
|
|
201
|
+
} = ctx.threads
|
|
205
202
|
|
|
206
203
|
onMounted(async () => {
|
|
207
204
|
await loadThreads()
|
|
@@ -209,7 +206,6 @@ const Sidebar = {
|
|
|
209
206
|
|
|
210
207
|
const selectThread = async (threadId) => {
|
|
211
208
|
router.push(`${ai.base}/c/${threadId}`)
|
|
212
|
-
emit('thread-selected')
|
|
213
209
|
}
|
|
214
210
|
|
|
215
211
|
const deleteThread = async (threadId) => {
|
|
@@ -223,24 +219,19 @@ const Sidebar = {
|
|
|
223
219
|
}
|
|
224
220
|
|
|
225
221
|
const createNewThread = async () => {
|
|
226
|
-
|
|
227
|
-
router.push(`${ai.base}/c/${newThread.id}`)
|
|
228
|
-
emit('thread-selected')
|
|
222
|
+
ctx.threads.startNewThread({ title: 'New Chat', model: ctx.chat.getSelectedModel(), redirect: true })
|
|
229
223
|
}
|
|
230
224
|
|
|
231
225
|
const goToInitialState = () => {
|
|
232
226
|
clearCurrentThread()
|
|
233
|
-
|
|
234
|
-
emit('thread-selected')
|
|
227
|
+
ctx.to(`/`)
|
|
235
228
|
}
|
|
236
229
|
|
|
237
230
|
const goToAnalytics = () => {
|
|
238
|
-
|
|
239
|
-
emit('thread-selected')
|
|
231
|
+
ctx.to(`/analytics`)
|
|
240
232
|
}
|
|
241
233
|
|
|
242
234
|
return {
|
|
243
|
-
threadStore,
|
|
244
235
|
threads,
|
|
245
236
|
currentThread,
|
|
246
237
|
isLoading,
|
|
@@ -254,4 +245,79 @@ const Sidebar = {
|
|
|
254
245
|
}
|
|
255
246
|
}
|
|
256
247
|
|
|
257
|
-
|
|
248
|
+
function useRequests(ext) {
|
|
249
|
+
async function query(query) {
|
|
250
|
+
return (await ext.getJson(appendQueryString(`/requests`, query))).response || []
|
|
251
|
+
}
|
|
252
|
+
async function deleteById(requestId) {
|
|
253
|
+
if (!requestId) {
|
|
254
|
+
throw new Error('Request ID is required')
|
|
255
|
+
}
|
|
256
|
+
return await ext.deleteJson(`/requests/${requestId}`)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function getThreadIds(query) {
|
|
260
|
+
return (await ext.getJson(appendQueryString(`/requests?fields=threadId¬_null=threadId&as=column&take=10000`, query))).response || []
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function getSummary() {
|
|
264
|
+
return (await ext.getJson(`/requests/summary`)).response
|
|
265
|
+
}
|
|
266
|
+
async function getDailySummary(day) {
|
|
267
|
+
return (await ext.getJson(`/requests/summary/${day}`)).response
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Get unique values for filter options
|
|
271
|
+
async function getFilterOptions() {
|
|
272
|
+
const results = await query({
|
|
273
|
+
select: 'distinct',
|
|
274
|
+
fields: 'model,provider',
|
|
275
|
+
not_null: 'model,provider',
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
if (results) {
|
|
279
|
+
const models = [...new Set(results.map(r => r.model).filter(m => m))].sort()
|
|
280
|
+
const providers = [...new Set(results.map(r => r.provider).filter(p => p))].sort()
|
|
281
|
+
console.log('getFilterOptions', models, providers)
|
|
282
|
+
return {
|
|
283
|
+
models,
|
|
284
|
+
providers
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
query,
|
|
291
|
+
deleteById,
|
|
292
|
+
getThreadIds,
|
|
293
|
+
getSummary,
|
|
294
|
+
getDailySummary,
|
|
295
|
+
getFilterOptions,
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export default {
|
|
300
|
+
order: -100,
|
|
301
|
+
|
|
302
|
+
install(ctx) {
|
|
303
|
+
ext = ctx.scope('app')
|
|
304
|
+
ctx.components({
|
|
305
|
+
ThreadsSidebar,
|
|
306
|
+
ThreadItem,
|
|
307
|
+
GroupedThreads,
|
|
308
|
+
Recents,
|
|
309
|
+
})
|
|
310
|
+
ctx.routes.push(...[
|
|
311
|
+
{ path: '/recents', component: Recents },
|
|
312
|
+
])
|
|
313
|
+
ThreadStore.install(ctx)
|
|
314
|
+
|
|
315
|
+
ctx.setGlobals({
|
|
316
|
+
requests: useRequests(ext)
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
ctx.setLayout({
|
|
320
|
+
left: 'ThreadsSidebar',
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
}
|