llms-py 3.0.0b6__py3-none-any.whl → 3.0.0b8__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.
Files changed (181) hide show
  1. llms/__pycache__/main.cpython-314.pyc +0 -0
  2. llms/{ui/modules/analytics.mjs → extensions/analytics/ui/index.mjs} +55 -164
  3. llms/extensions/app/__init__.py +519 -0
  4. llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
  5. llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
  6. llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
  7. llms/extensions/app/db.py +641 -0
  8. llms/extensions/app/db_manager.py +195 -0
  9. llms/extensions/app/requests.json +9073 -0
  10. llms/extensions/app/threads.json +15290 -0
  11. llms/{ui/modules/threads → extensions/app/ui}/Recents.mjs +82 -55
  12. llms/{ui/modules/threads → extensions/app/ui}/index.mjs +83 -20
  13. llms/extensions/app/ui/threadStore.mjs +407 -0
  14. llms/extensions/core_tools/__init__.py +598 -0
  15. llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
  16. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
  17. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
  18. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
  19. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
  20. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
  21. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
  22. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
  23. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
  24. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
  25. llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
  26. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  27. llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +344 -0
  28. llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +9884 -0
  29. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
  30. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
  31. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
  32. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
  33. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
  34. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
  35. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
  36. llms/extensions/core_tools/ui/index.mjs +650 -0
  37. llms/extensions/gallery/__init__.py +61 -0
  38. llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
  39. llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
  40. llms/extensions/gallery/db.py +298 -0
  41. llms/extensions/gallery/ui/index.mjs +481 -0
  42. llms/extensions/katex/__init__.py +6 -0
  43. llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
  44. llms/extensions/katex/ui/README.md +125 -0
  45. llms/extensions/katex/ui/contrib/auto-render.js +338 -0
  46. llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
  47. llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
  48. llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
  49. llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
  50. llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
  51. llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
  52. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
  53. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
  54. llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
  55. llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
  56. llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
  57. llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
  58. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
  59. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
  60. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  61. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  62. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  63. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  64. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  65. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  66. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  67. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  68. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  69. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  70. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  71. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  72. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  73. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  74. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  75. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  76. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  77. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  78. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  79. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  80. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  81. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  82. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  83. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  84. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  85. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  86. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  87. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  88. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  89. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  90. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  91. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  92. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  93. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  94. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  116. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  117. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  118. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  119. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  120. llms/extensions/katex/ui/index.mjs +92 -0
  121. llms/extensions/katex/ui/katex-swap.css +1230 -0
  122. llms/extensions/katex/ui/katex-swap.min.css +1 -0
  123. llms/extensions/katex/ui/katex.css +1230 -0
  124. llms/extensions/katex/ui/katex.js +19080 -0
  125. llms/extensions/katex/ui/katex.min.css +1 -0
  126. llms/extensions/katex/ui/katex.min.js +1 -0
  127. llms/extensions/katex/ui/katex.min.mjs +1 -0
  128. llms/extensions/katex/ui/katex.mjs +18547 -0
  129. llms/extensions/providers/__init__.py +18 -0
  130. llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  131. llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  132. llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  133. llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
  134. llms/{providers → extensions/providers}/__pycache__/nvidia.cpython-314.pyc +0 -0
  135. llms/{providers → extensions/providers}/__pycache__/openai.cpython-314.pyc +0 -0
  136. llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  137. llms/{providers → extensions/providers}/anthropic.py +45 -5
  138. llms/{providers → extensions/providers}/chutes.py +21 -18
  139. llms/{providers → extensions/providers}/google.py +99 -27
  140. llms/{providers → extensions/providers}/nvidia.py +6 -8
  141. llms/{providers → extensions/providers}/openai.py +3 -6
  142. llms/{providers → extensions/providers}/openrouter.py +12 -10
  143. llms/extensions/system_prompts/__init__.py +45 -0
  144. llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
  145. llms/extensions/system_prompts/ui/index.mjs +285 -0
  146. llms/extensions/system_prompts/ui/prompts.json +1067 -0
  147. llms/extensions/tools/__init__.py +5 -0
  148. llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  149. llms/{ui/modules/tools.mjs → extensions/tools/ui/index.mjs} +12 -10
  150. llms/index.html +26 -38
  151. llms/llms.json +20 -1
  152. llms/main.py +845 -245
  153. llms/providers-extra.json +0 -32
  154. llms/ui/App.mjs +18 -20
  155. llms/ui/ai.mjs +38 -15
  156. llms/ui/app.css +1440 -59
  157. llms/ui/ctx.mjs +154 -18
  158. llms/ui/index.mjs +17 -14
  159. llms/ui/lib/vue.min.mjs +10 -9
  160. llms/ui/lib/vue.mjs +1796 -1635
  161. llms/ui/markdown.mjs +4 -2
  162. llms/ui/modules/chat/ChatBody.mjs +101 -334
  163. llms/ui/modules/chat/HomeTools.mjs +12 -0
  164. llms/ui/modules/chat/SettingsDialog.mjs +1 -1
  165. llms/ui/modules/chat/index.mjs +351 -314
  166. llms/ui/modules/layout.mjs +2 -26
  167. llms/ui/modules/model-selector.mjs +3 -3
  168. llms/ui/tailwind.input.css +35 -1
  169. llms/ui/utils.mjs +33 -3
  170. {llms_py-3.0.0b6.dist-info → llms_py-3.0.0b8.dist-info}/METADATA +1 -1
  171. llms_py-3.0.0b8.dist-info/RECORD +198 -0
  172. llms/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  173. llms/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  174. llms/providers/__pycache__/google.cpython-314.pyc +0 -0
  175. llms/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  176. llms/ui/modules/threads/threadStore.mjs +0 -586
  177. llms_py-3.0.0b6.dist-info/RECORD +0 -66
  178. {llms_py-3.0.0b6.dist-info → llms_py-3.0.0b8.dist-info}/WHEEL +0 -0
  179. {llms_py-3.0.0b6.dist-info → llms_py-3.0.0b8.dist-info}/entry_points.txt +0 -0
  180. {llms_py-3.0.0b6.dist-info → llms_py-3.0.0b8.dist-info}/licenses/LICENSE +0 -0
  181. {llms_py-3.0.0b6.dist-info → llms_py-3.0.0b8.dist-info}/top_level.txt +0 -0
@@ -1,22 +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
3
 
5
4
  const RecentResults = {
6
5
  template: `
7
6
  <div class="flex-1 overflow-y-auto" @scroll="onScroll">
8
7
  <div class="mx-auto max-w-6xl px-4 py-4">
9
- <div class="text-sm text-gray-600 dark:text-gray-400 mb-3" v-if="threads.length">
10
- <span v-if="q">{{ filtered.length }} result{{ filtered.length===1?'':'s' }}</span>
11
- <span v-else>Searching {{ threads.length }} conversation{{ threads.length===1?'':'s' }}</span>
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>
12
11
  </div>
13
12
 
14
- <div v-if="!threads.length" class="text-gray-500 dark:text-gray-400">No conversations yet.</div>
13
+ <div v-if="!loading && threads.length === 0" class="text-gray-500 dark:text-gray-400">No conversations found.</div>
15
14
 
16
15
  <table class="w-full">
17
16
  <tbody>
18
- <tr v-for="t in displayed" :key="t.id" class="hover:bg-gray-50 dark:hover:bg-gray-800">
19
- <td class="py-3 px-1 border-b border-gray-200 dark:border-gray-700 max-w-3xl">
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">
20
19
  <button type="button" @click="open(t.id)" class="w-full text-left">
21
20
  <div class="flex items-start justify-between gap-3">
22
21
  <div class="min-w-0 flex-1">
@@ -32,12 +31,13 @@ const RecentResults = {
32
31
  <div class="text-right whitespace-nowrap">
33
32
  <div class="text-xs text-gray-500 dark:text-gray-400">{{ formatDate(t.updatedAt || t.createdAt) }}</div>
34
33
  <div class="text-[11px] text-gray-500/80 dark:text-gray-400/80">{{ (t.messages?.length || 0) }} messages</div>
35
- <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>
36
35
  </div>
37
36
  </td>
38
37
  </tr>
39
38
  </tbody>
40
39
  </table>
40
+ <div v-if="loading" class="py-4 text-center text-gray-500 dark:text-gray-400">Loading...</div>
41
41
  </div>
42
42
  </div>
43
43
  `,
@@ -47,59 +47,81 @@ const RecentResults = {
47
47
  setup(props) {
48
48
  const ctx = inject('ctx')
49
49
  const ai = ctx.ai
50
- const config = ctx.state.config
51
50
  const router = useRouter()
52
- const { threads, loadThreads } = useThreadStore()
53
- let defaultVisibleCount = 25
54
- const visibleCount = ref(defaultVisibleCount)
55
- const filtered = ref([])
56
- const displayed = ref([])
57
-
58
- const start = Date.now()
59
- console.log('start', start, threads.value.length)
60
-
61
- onMounted(async () => {
62
- visibleCount.value = defaultVisibleCount
63
- if (!threads.value.length) {
64
- await loadThreads()
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
- update()
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
- function update() {
76
- console.log('update', props.q)
77
- const query = normalized(props.q)
78
- filtered.value = !query
79
- ? threads.value
80
- : threads.value.filter(t => {
81
- const inTitle = normalized(t.title).includes(query)
82
- const inMsgs = Array.isArray(t.messages) && t.messages.some(m => normalized(m?.content).includes(query))
83
- return inTitle || inMsgs
84
- })
85
- updateVisible()
86
- }
87
- function updateVisible() {
88
- displayed.value = filtered.value.slice(0, Math.min(visibleCount.value, filtered.value.length))
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 - 24) {
94
- if (visibleCount.value < filtered.value.length) {
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
 
@@ -107,7 +129,11 @@ const RecentResults = {
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
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)
135
+
136
+ // Check messages
111
137
  if (Array.isArray(t.messages)) {
112
138
  for (const m of t.messages) {
113
139
  const c = normalized(m?.content)
@@ -119,23 +145,23 @@ const RecentResults = {
119
145
  const end = Math.min(orig.length, idx + query.length + 60)
120
146
  const prefix = start > 0 ? '…' : ''
121
147
  const suffix = end < orig.length ? '…' : ''
122
- const snippet = prefix + orig.slice(start, end) + suffix
123
- // return snippet
124
- return highlight(snippet)
148
+ const snippetText = prefix + orig.slice(start, end) + suffix
149
+ return highlight(snippetText)
125
150
  }
126
151
  }
127
152
  }
128
- return ''
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
- filtered,
138
- displayed,
163
+ loading,
164
+ total,
139
165
  snippet,
140
166
  open,
141
167
  formatDate,
@@ -153,9 +179,10 @@ export default {
153
179
  <!-- Header -->
154
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">
155
181
  <div class="max-w-6xl mx-auto flex items-center justify-between gap-3">
156
- <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Search Chats</h2>
157
- <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">
158
184
  <input
185
+ id="search-history"
159
186
  v-model="q"
160
187
  type="search"
161
188
  placeholder="Search titles and messages..."
@@ -1,8 +1,11 @@
1
1
  import { onMounted, inject } from 'vue'
2
2
  import { useRouter } from 'vue-router'
3
+ import { appendQueryString } from '@servicestack/client'
3
4
  import ThreadStore from './threadStore.mjs'
4
5
  import Recents from './Recents.mjs'
5
6
 
7
+ let ext
8
+
6
9
  // Thread Item Component
7
10
  const ThreadItem = {
8
11
  template: `
@@ -70,7 +73,20 @@ const GroupedThreads = {
70
73
  v-for="thread in groupedThreads.today"
71
74
  :key="thread.id"
72
75
  :thread="thread"
73
- :is-active="currentThread?.id === thread.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"
74
90
  @select="$emit('select', $event)"
75
91
  @delete="$emit('delete', $event)"
76
92
  />
@@ -83,7 +99,7 @@ const GroupedThreads = {
83
99
  v-for="thread in groupedThreads.lastWeek"
84
100
  :key="thread.id"
85
101
  :thread="thread"
86
- :is-active="currentThread?.id === thread.id"
102
+ :is-active="currentThread?.id == thread.id"
87
103
  @select="$emit('select', $event)"
88
104
  @delete="$emit('delete', $event)"
89
105
  />
@@ -96,7 +112,7 @@ const GroupedThreads = {
96
112
  v-for="thread in groupedThreads.lastMonth"
97
113
  :key="thread.id"
98
114
  :thread="thread"
99
- :is-active="currentThread?.id === thread.id"
115
+ :is-active="currentThread?.id == thread.id"
100
116
  @select="$emit('select', $event)"
101
117
  @delete="$emit('delete', $event)"
102
118
  />
@@ -109,7 +125,7 @@ const GroupedThreads = {
109
125
  v-for="thread in monthThreads"
110
126
  :key="thread.id"
111
127
  :thread="thread"
112
- :is-active="currentThread?.id === thread.id"
128
+ :is-active="currentThread?.id == thread.id"
113
129
  @select="$emit('select', $event)"
114
130
  @delete="$emit('delete', $event)"
115
131
  />
@@ -135,7 +151,7 @@ const GroupedThreads = {
135
151
  const ThreadsSidebar = {
136
152
  template: `
137
153
  <div class="flex flex-col h-full">
138
- <Brand @home="goToInitialState" @toggle-sidebar="$emit('toggle-sidebar')" />
154
+ <Brand />
139
155
  <!-- Thread List -->
140
156
  <div class="flex-1 overflow-y-auto">
141
157
  <div v-if="isLoading" class="p-4 text-center text-gray-500 dark:text-gray-400">
@@ -169,8 +185,7 @@ const ThreadsSidebar = {
169
185
  </div>
170
186
  </div>
171
187
  `,
172
- emits: ['thread-selected', 'toggle-sidebar'],
173
- setup(props, { emit }) {
188
+ setup(props) {
174
189
  const ctx = inject('ctx')
175
190
  const ai = ctx.ai
176
191
  const router = useRouter()
@@ -191,7 +206,6 @@ const ThreadsSidebar = {
191
206
 
192
207
  const selectThread = async (threadId) => {
193
208
  router.push(`${ai.base}/c/${threadId}`)
194
- emit('thread-selected')
195
209
  }
196
210
 
197
211
  const deleteThread = async (threadId) => {
@@ -205,20 +219,16 @@ const ThreadsSidebar = {
205
219
  }
206
220
 
207
221
  const createNewThread = async () => {
208
- const newThread = await createThread()
209
- router.push(`${ai.base}/c/${newThread.id}`)
210
- emit('thread-selected')
222
+ ctx.threads.startNewThread({ title: 'New Chat', model: ctx.chat.getSelectedModel() })
211
223
  }
212
224
 
213
225
  const goToInitialState = () => {
214
226
  clearCurrentThread()
215
- router.push(`${ai.base}/`)
216
- emit('thread-selected')
227
+ ctx.to(`/`)
217
228
  }
218
229
 
219
230
  const goToAnalytics = () => {
220
- router.push(`${ai.base}/analytics`)
221
- emit('thread-selected')
231
+ ctx.to(`/analytics`)
222
232
  }
223
233
 
224
234
  return {
@@ -235,8 +245,62 @@ const ThreadsSidebar = {
235
245
  }
236
246
  }
237
247
 
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&not_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
+
238
299
  export default {
300
+ order: -100,
301
+
239
302
  install(ctx) {
303
+ ext = ctx.scope('app')
240
304
  ctx.components({
241
305
  ThreadsSidebar,
242
306
  ThreadItem,
@@ -248,13 +312,12 @@ export default {
248
312
  ])
249
313
  ThreadStore.install(ctx)
250
314
 
315
+ ctx.setGlobals({
316
+ requests: useRequests(ext)
317
+ })
318
+
251
319
  ctx.setLayout({
252
320
  left: 'ThreadsSidebar',
253
321
  })
254
- },
255
-
256
- async load(ctx) {
257
- const { initDB } = ctx.threads
258
- await initDB()
259
322
  }
260
323
  }