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
@@ -0,0 +1,407 @@
1
+ import { ref, computed } from 'vue'
2
+ import { appendQueryString, createErrorStatus } from '@servicestack/client'
3
+
4
+ /**
5
+ * Returns an ever-increasing unique integer id.
6
+ */
7
+ export const nextId = (() => {
8
+ let last = 0 // cache of the last id that was handed out
9
+ return () => {
10
+ const now = Date.now() // current millisecond timestamp
11
+ last = (now > last) ? now : last + 1
12
+ return last
13
+ }
14
+ })();
15
+
16
+
17
+ const threads = ref([])
18
+ const currentThread = ref(null)
19
+ const isLoading = ref(false)
20
+
21
+ let ctx = null
22
+ let ext = null
23
+
24
+ // Generate unique thread ID
25
+ function generateThreadId() {
26
+ return Date.now().toString()
27
+ }
28
+
29
+ function setError(error, msg = null) {
30
+ ctx?.setError(error, msg)
31
+ }
32
+
33
+ async function query(query) {
34
+ return (await ext.getJson(appendQueryString(`/threads`, query))).response || []
35
+ }
36
+
37
+ let watchThreadTimeout = ref(null)
38
+ async function watchThreadUpdates() {
39
+ const thread = currentThread.value
40
+ // console.debug('watchThreadUpdates', thread?.id, thread?.messages?.length, thread?.completedAt)
41
+ if (thread && thread?.messages?.length && !thread.completedAt) {
42
+ const api = await ext.getJson(appendQueryString(`/threads/${thread.id}/updates`, { after: thread.updatedAt }))
43
+ // console.log('watchThreadUpdates', api)
44
+ if (api.response) {
45
+ replaceThread(api.response)
46
+ return
47
+ } else {
48
+ setError(api.error, `watching thread ${thread.id}`)
49
+ }
50
+ }
51
+ stopWatchingThread()
52
+ }
53
+
54
+ function startWatchingThread() {
55
+ stopWatchingThread()
56
+ const thread = currentThread.value
57
+ if (thread && thread?.messages?.length && !thread.completedAt) {
58
+ watchThreadTimeout.value = setTimeout(watchThreadUpdates, 100)
59
+ }
60
+ }
61
+
62
+ function stopWatchingThread() {
63
+ console.debug('stopWatchingThread')
64
+ if (watchThreadTimeout.value) {
65
+ clearTimeout(watchThreadTimeout.value)
66
+ }
67
+ watchThreadTimeout.value = null
68
+ }
69
+
70
+ const isWatchingThread = computed(() => watchThreadTimeout.value != null)
71
+
72
+ async function cancelThread() {
73
+ console.log('cancelThread')
74
+ stopWatchingThread()
75
+ const thread = currentThread.value
76
+ if (!thread) return
77
+ const api = await ext.postJson(`/threads/${thread.id}/cancel`)
78
+ if (api.response) {
79
+ replaceThread(api.response)
80
+ } else {
81
+ setError(api.error, `Canceling thread ${thread.id}`)
82
+ }
83
+ }
84
+
85
+ // Create a new thread
86
+ async function createThread(args = {}) {
87
+ const thread = {
88
+ messages: [],
89
+ ...args
90
+ }
91
+ if (!thread.title) {
92
+ thread.title = 'New Chat'
93
+ }
94
+ if (thread.title.length > 200) {
95
+ thread.title = thread.title.slice(0, 200) + '...'
96
+ }
97
+
98
+ ctx.createThreadFilters.forEach(f => f(thread))
99
+
100
+ const api = await ext.postJson("/threads", thread)
101
+ if (api.response) {
102
+ threads.value.unshift(api.response)
103
+ return api.response
104
+ } else {
105
+ setError(api.error, `Creating thread ${thread.title}`)
106
+ }
107
+
108
+ return thread
109
+ }
110
+
111
+ function replaceThread(thread) {
112
+ if (!thread) {
113
+ console.error('replaceThread(null)')
114
+ return
115
+ }
116
+ const index = threads.value.findIndex(t => t.id === thread.id)
117
+ if (index !== -1) {
118
+ threads.value[index] = thread
119
+ }
120
+ if (currentThread.value?.id === thread.id) {
121
+ currentThread.value = thread
122
+ }
123
+ startWatchingThread()
124
+ return thread
125
+ }
126
+
127
+ // Update thread
128
+ async function updateThread(threadId, updates) {
129
+
130
+ if (!threadId)
131
+ throw new Error('threadId is required')
132
+
133
+ ctx.updateThreadFilters.forEach(f => f(updates))
134
+
135
+ const api = await ext.patchJson(`/threads/${threadId}`, updates)
136
+ if (api.response) {
137
+ return replaceThread(api.response)
138
+ } else {
139
+ setError(api.error, `Updating thread ${threadId}`)
140
+ }
141
+ }
142
+
143
+ async function deleteMessageFromThread(threadId, messageId) {
144
+ const thread = await getThread(threadId)
145
+ if (!thread) throw new Error('Thread not found')
146
+ const updatedMessages = thread.messages.filter(m => m.id !== messageId)
147
+ await updateThread(threadId, { messages: updatedMessages })
148
+ }
149
+
150
+ async function updateMessageInThread(threadId, messageId, updates) {
151
+ const thread = await getThread(threadId)
152
+ if (!thread) throw new Error('Thread not found')
153
+
154
+ const messageIndex = thread.messages.findIndex(m => m.id === messageId)
155
+ if (messageIndex === -1) throw new Error('Message not found')
156
+
157
+ const updatedMessages = [...thread.messages]
158
+ updatedMessages[messageIndex] = {
159
+ ...updatedMessages[messageIndex],
160
+ ...updates
161
+ }
162
+
163
+ await updateThread(threadId, { messages: updatedMessages })
164
+ }
165
+
166
+ async function redoMessageFromThread(threadId, timestamp) {
167
+ const thread = await getThread(threadId)
168
+ if (!thread) throw new Error('Thread not found')
169
+
170
+ // Find the index of the message to redo
171
+ const messageIndex = thread.messages.findIndex(m => m.timestamp === timestamp)
172
+ if (messageIndex === -1) {
173
+ setError(createErrorStatus(`Message not found for timestamp ${timestamp}`))
174
+ return
175
+ }
176
+
177
+ // setError({
178
+ // errorCode: 'TestError',
179
+ // message: `Error redoing message ${timestamp} in thread ${threadId}`,
180
+ // stackTrace: `Error in page.mjs
181
+ // at Line 1
182
+ // at Line 2
183
+ // at Line 3`,
184
+ // })
185
+ // return
186
+
187
+ // Keep only messages up to and including the target message
188
+ const updatedMessages = thread.messages.slice(0, messageIndex + 1)
189
+
190
+ // Update the thread with the new messages
191
+ const api = await queueChat(threadId, { messages: updatedMessages })
192
+ if (api.response) {
193
+ replaceThread(api.response)
194
+ } else {
195
+ setError(api.error, `Redoing message ${timestamp} in thread ${threadId}`)
196
+ }
197
+ }
198
+
199
+ async function loadThreads() {
200
+ isLoading.value = true
201
+
202
+ try {
203
+ const api = await ext.getJson('/threads?take=50')
204
+ threads.value = api.response || []
205
+ return threads.value
206
+ } finally {
207
+ isLoading.value = false
208
+ }
209
+ }
210
+
211
+ async function getThread(threadId) {
212
+ const cachedThread = threads.value.find(t => t.id == threadId)
213
+ if (cachedThread) return cachedThread
214
+ const api = await ext.getJson(`/threads?id=${threadId}`)
215
+ return api.response && api.response[0] || null
216
+ }
217
+
218
+ // Delete thread
219
+ async function deleteThread(threadId) {
220
+ await ext.delete(`/threads/${threadId}`)
221
+
222
+ threads.value = threads.value.filter(t => t.id !== threadId)
223
+
224
+ if (currentThread.value?.id === threadId) {
225
+ currentThread.value = null
226
+ }
227
+ }
228
+
229
+ // Set current thread
230
+ async function setCurrentThread(threadId) {
231
+ const thread = await getThread(threadId)
232
+ if (thread) {
233
+ currentThread.value = thread
234
+ startWatchingThread()
235
+ }
236
+ return thread
237
+ }
238
+
239
+ // Set current thread from router params (router-aware version)
240
+ async function setCurrentThreadFromRoute(threadId, router) {
241
+ if (!threadId) {
242
+ currentThread.value = null
243
+ return null
244
+ }
245
+
246
+ const thread = setCurrentThread(threadId)
247
+ if (thread) {
248
+ return thread
249
+ } else {
250
+ // Thread not found, redirect to home
251
+ if (router) {
252
+ router.push((globalThis.ai?.base || '') + '/')
253
+ }
254
+ currentThread.value = null
255
+ return null
256
+ }
257
+ }
258
+
259
+ // Clear current thread (go back to initial state)
260
+ function clearCurrentThread() {
261
+ currentThread.value = null
262
+ }
263
+
264
+ function getGroupedThreads(total) {
265
+ const now = new Date()
266
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
267
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
268
+ const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
269
+ const lastMonth = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
270
+
271
+ const groups = {
272
+ today: [],
273
+ yesterday: [],
274
+ lastWeek: [],
275
+ lastMonth: [],
276
+ older: {}
277
+ }
278
+
279
+ const takeThreads = threads.value.slice(0, total)
280
+
281
+ takeThreads.forEach(thread => {
282
+ const threadDate = new Date(thread.updatedAt)
283
+
284
+ if (threadDate >= today) {
285
+ groups.today.push(thread)
286
+ } else if (threadDate >= yesterday) {
287
+ groups.yesterday.push(thread)
288
+ } else if (threadDate >= lastWeek) {
289
+ groups.lastWeek.push(thread)
290
+ } else if (threadDate >= lastMonth) {
291
+ groups.lastMonth.push(thread)
292
+ } else {
293
+ const year = threadDate.getFullYear()
294
+ const month = threadDate.toLocaleString('default', { month: 'long' })
295
+ const key = `${month} ${year}`
296
+
297
+ if (!groups.older[key]) {
298
+ groups.older[key] = []
299
+ }
300
+ groups.older[key].push(thread)
301
+ }
302
+ })
303
+
304
+ return groups
305
+ }
306
+
307
+ // Group threads by time periods
308
+ const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
309
+
310
+ function getLatestCachedThread() {
311
+ return threads.value[0]
312
+ }
313
+
314
+ async function startNewThread({ title, model }) {
315
+ if (!model) {
316
+ console.error('No model selected')
317
+ return
318
+ }
319
+ if (!title) {
320
+ title = 'New Chat'
321
+ }
322
+ const latestThread = getLatestCachedThread()
323
+
324
+ console.log('startNewThread', title, model.name, ctx.router.currentRoute.value?.path, latestThread?.messages?.length)
325
+ ctx.setLayout({ left: 'ThreadsSidebar' })
326
+
327
+ if (latestThread && latestThread.title == title && !latestThread.messages?.length) {
328
+ if (ctx.router.currentRoute.value?.path != `/c/${latestThread.id}`) {
329
+ ctx.to(`/c/${latestThread.id}`)
330
+ }
331
+ return latestThread
332
+ }
333
+ const newThread = await createThread({
334
+ title,
335
+ model: model.name,
336
+ info: ctx.utils.toModelInfo(model),
337
+ })
338
+
339
+ console.log('newThread', newThread, model)
340
+ // Navigate to the new thread URL
341
+ ctx.to(`/c/${newThread.id}`)
342
+
343
+ // Get the thread to check for duplicates
344
+ let thread = await getThread(newThread.id)
345
+ console.log('thread', thread)
346
+ return thread
347
+ }
348
+
349
+ async function queueChat(threadId, body, options = {}) {
350
+ const api = await ctx.postJson(`/ext/app/threads/${threadId}/chat`, {
351
+ ...options,
352
+ body: typeof body == 'string'
353
+ ? body
354
+ : JSON.stringify(body),
355
+ })
356
+ return api
357
+ }
358
+
359
+ // Export the store
360
+ export function useThreadStore() {
361
+ return {
362
+ // State
363
+ threads,
364
+ currentThread,
365
+ isLoading,
366
+ groupedThreads,
367
+
368
+ // Actions
369
+ query,
370
+ createThread,
371
+ updateThread,
372
+ deleteMessageFromThread,
373
+ updateMessageInThread,
374
+ redoMessageFromThread,
375
+ loadThreads,
376
+ getThread,
377
+ deleteThread,
378
+ setCurrentThread,
379
+ setCurrentThreadFromRoute,
380
+ clearCurrentThread,
381
+ getGroupedThreads,
382
+ getLatestCachedThread,
383
+ generateThreadId,
384
+ startNewThread,
385
+ replaceThread,
386
+ queueChat,
387
+ isWatchingThread,
388
+ startWatchingThread,
389
+ stopWatchingThread,
390
+ cancelThread,
391
+ get watchingThread() {
392
+ return isWatchingThread.value
393
+ },
394
+ }
395
+ }
396
+
397
+ export default {
398
+ install(context) {
399
+ ctx = context
400
+ ext = ctx.scope('app')
401
+ ctx.setGlobals({ threads: useThreadStore() })
402
+ },
403
+
404
+ async load() {
405
+ await ctx.threads.loadThreads()
406
+ }
407
+ }