llms-py 3.0.0b7__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 (157) hide show
  1. llms/__pycache__/main.cpython-314.pyc +0 -0
  2. llms/extensions/analytics/ui/index.mjs +51 -162
  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 +78 -9
  13. llms/extensions/app/ui/threadStore.mjs +407 -0
  14. llms/extensions/core_tools/__init__.py +272 -32
  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/__pycache__/db.cpython-314.pyc +0 -0
  38. llms/extensions/gallery/db.py +4 -4
  39. llms/extensions/gallery/ui/index.mjs +2 -1
  40. llms/extensions/katex/__init__.py +6 -0
  41. llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
  42. llms/extensions/katex/ui/README.md +125 -0
  43. llms/extensions/katex/ui/contrib/auto-render.js +338 -0
  44. llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
  45. llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
  46. llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
  47. llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
  48. llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
  49. llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
  50. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
  51. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
  52. llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
  53. llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
  54. llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
  55. llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
  56. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
  57. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
  58. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  59. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  60. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  61. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  62. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  63. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  64. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  65. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  66. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  67. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  68. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  69. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  70. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  71. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  72. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  73. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  74. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  75. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  76. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  77. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  78. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  79. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  80. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  81. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  82. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  83. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  84. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  85. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  86. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  87. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  88. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  89. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  90. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  91. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  92. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  93. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  94. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  116. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  117. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  118. llms/extensions/katex/ui/index.mjs +92 -0
  119. llms/extensions/katex/ui/katex-swap.css +1230 -0
  120. llms/extensions/katex/ui/katex-swap.min.css +1 -0
  121. llms/extensions/katex/ui/katex.css +1230 -0
  122. llms/extensions/katex/ui/katex.js +19080 -0
  123. llms/extensions/katex/ui/katex.min.css +1 -0
  124. llms/extensions/katex/ui/katex.min.js +1 -0
  125. llms/extensions/katex/ui/katex.min.mjs +1 -0
  126. llms/extensions/katex/ui/katex.mjs +18547 -0
  127. llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  128. llms/extensions/providers/anthropic.py +44 -1
  129. llms/extensions/system_prompts/ui/index.mjs +2 -1
  130. llms/extensions/tools/__init__.py +5 -0
  131. llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  132. llms/extensions/tools/ui/index.mjs +8 -8
  133. llms/index.html +26 -38
  134. llms/llms.json +4 -1
  135. llms/main.py +492 -103
  136. llms/ui/App.mjs +2 -3
  137. llms/ui/ai.mjs +29 -13
  138. llms/ui/app.css +250 -398
  139. llms/ui/ctx.mjs +84 -6
  140. llms/ui/index.mjs +4 -6
  141. llms/ui/lib/vue.min.mjs +10 -9
  142. llms/ui/lib/vue.mjs +1796 -1635
  143. llms/ui/markdown.mjs +4 -2
  144. llms/ui/modules/chat/ChatBody.mjs +90 -86
  145. llms/ui/modules/chat/HomeTools.mjs +0 -242
  146. llms/ui/modules/chat/index.mjs +103 -170
  147. llms/ui/modules/model-selector.mjs +2 -2
  148. llms/ui/tailwind.input.css +35 -1
  149. llms/ui/utils.mjs +12 -0
  150. {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/METADATA +1 -1
  151. llms_py-3.0.0b8.dist-info/RECORD +198 -0
  152. llms/ui/modules/threads/threadStore.mjs +0 -640
  153. llms_py-3.0.0b7.dist-info/RECORD +0 -80
  154. {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/WHEEL +0 -0
  155. {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/entry_points.txt +0 -0
  156. {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/licenses/LICENSE +0 -0
  157. {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/top_level.txt +0 -0
@@ -1,640 +0,0 @@
1
- import { ref, computed } from 'vue'
2
- import { openDB } from 'idb'
3
- import { nextId } from '../../utils.mjs'
4
-
5
- // Thread store for managing chat threads with IndexedDB
6
- const threads = ref([])
7
- const currentThread = ref(null)
8
- const isLoading = ref(false)
9
-
10
- let db = null
11
- let ctx = null
12
-
13
- // Initialize IndexedDB
14
- async function initDB() {
15
- if (db) return db
16
-
17
- db = await openDB('LlmsThreads', 3, {
18
- upgrade(db, _oldVersion, _newVersion, transaction) {
19
- if (!db.objectStoreNames.contains('threads')) {
20
- // Create threads store
21
- const threadStore = db.createObjectStore('threads', {
22
- keyPath: 'id',
23
- autoIncrement: false
24
- })
25
-
26
- // Create indexes for efficient querying
27
- threadStore.createIndex('createdAt', 'createdAt')
28
- threadStore.createIndex('updatedAt', 'updatedAt')
29
- threadStore.createIndex('title', 'title')
30
- }
31
- if (!db.objectStoreNames.contains('requests')) {
32
- // Create requests store
33
- const requestStore = db.createObjectStore('requests', {
34
- keyPath: 'id',
35
- autoIncrement: false
36
- })
37
- requestStore.createIndex('threadId', 'threadId')
38
- requestStore.createIndex('model', 'model')
39
- requestStore.createIndex('provider', 'provider')
40
- requestStore.createIndex('inputTokens', 'inputTokens')
41
- requestStore.createIndex('outputTokens', 'outputTokens')
42
- requestStore.createIndex('cost', 'cost')
43
- requestStore.createIndex('duration', 'duration')
44
- requestStore.createIndex('created', 'created')
45
- }
46
- }
47
- })
48
-
49
- return db
50
- }
51
-
52
- // Generate unique thread ID
53
- function generateThreadId() {
54
- return Date.now().toString()
55
- }
56
-
57
- async function logRequest(threadId, model, request, response) {
58
- await initDB()
59
- const metadata = response.metadata || {}
60
- const usage = response.usage || {}
61
- const [inputPrice, outputPrice] = metadata.pricing ? metadata.pricing.split('/') : [0, 0]
62
- const lastUserContent = request.messages?.slice().reverse().find(m => m.role === 'user')?.content
63
- const content = Array.isArray(lastUserContent)
64
- ? lastUserContent.filter(c => c?.text).map(c => c.text).join(' ')
65
- : lastUserContent || ''
66
- const title = content.slice(0, 100) + (content.length > 100 ? '...' : '')
67
- const inputTokens = usage?.prompt_tokens ?? 0
68
- const outputTokens = usage?.completion_tokens ?? 0
69
- const inputCachedTokens = usage?.prompt_token_details?.cached_tokens ?? 0
70
- const finishReason = response.choices[0]?.finish_reason || 'unknown'
71
-
72
- const subtractDays = (date, days) => {
73
- const result = new Date(date * 1000)
74
- result.setDate(result.getDate() - days)
75
- return parseInt(result.valueOf() / 1000)
76
- }
77
-
78
- const log = {
79
- id: nextId(),
80
- threadId: threadId,
81
- model: model.id,
82
- provider: model.provider,
83
- providerModel: response.model || model.provider_model,
84
- title,
85
- inputTokens,
86
- outputTokens,
87
- inputCachedTokens,
88
- totalTokens: usage.total_tokens ?? (inputTokens + outputTokens),
89
- inputPrice,
90
- outputPrice,
91
- cost: (parseFloat(inputPrice) * inputTokens / 1_000_000) + (parseFloat(outputPrice) * outputTokens / 1_000_000),
92
- duration: parseInt(metadata.duration) || 0,
93
- created: response.created ?? Math.floor(Date.now() / 1000),
94
- finishReason,
95
- providerRef: response.provider,
96
- ref: response.id || undefined,
97
- usage: usage,
98
- }
99
- console.debug('logRequest', log)
100
- const tx = db.transaction(['requests'], 'readwrite')
101
- await tx.objectStore('requests').add(log)
102
- await tx.complete
103
- return log
104
- }
105
-
106
- // Create a new thread
107
- async function createThread(args = {}) {
108
- await initDB()
109
-
110
- const thread = {
111
- id: generateThreadId(),
112
- messages: [],
113
- createdAt: new Date().toISOString(),
114
- updatedAt: new Date().toISOString(),
115
- ...args
116
- }
117
- if (!thread.title) {
118
- thread.title = 'New Chat'
119
- }
120
- if (thread.title.length > 200) {
121
- thread.title = thread.title.slice(0, 200) + '...'
122
- }
123
-
124
- ctx.createThreadFilters.forEach(f => f(thread))
125
-
126
- try {
127
- const tx = db.transaction(['threads'], 'readwrite')
128
- await tx.objectStore('threads').add(thread)
129
- await tx.complete
130
-
131
- threads.value.unshift(thread)
132
- // Note: currentThread will be set by router navigation
133
-
134
- } catch (e) {
135
- console.error('Error creating thread', e, thread)
136
- }
137
-
138
- return thread
139
- }
140
-
141
- // Update thread
142
- async function updateThread(threadId, updates) {
143
- await initDB()
144
-
145
- const tx = db.transaction(['threads'], 'readwrite')
146
- const store = tx.objectStore('threads')
147
-
148
- const thread = await store.get(threadId)
149
- if (!thread) throw new Error('Thread not found')
150
-
151
- const updatedThread = {
152
- ...thread,
153
- ...updates,
154
- updatedAt: new Date().toISOString()
155
- }
156
-
157
- ctx.updateThreadFilters.forEach(f => f(updatedThread))
158
-
159
- await store.put(updatedThread)
160
- await tx.complete
161
-
162
- // Update in memory
163
- const index = threads.value.findIndex(t => t.id === threadId)
164
- if (index !== -1) {
165
- threads.value[index] = updatedThread
166
- }
167
-
168
- if (currentThread.value?.id === threadId) {
169
- currentThread.value = updatedThread
170
- }
171
-
172
- return updatedThread
173
- }
174
-
175
- async function calculateThreadStats(threadId) {
176
- await initDB()
177
-
178
- const tx = db.transaction(['requests'], 'readonly')
179
- const store = tx.objectStore('requests')
180
- const index = store.index('threadId')
181
-
182
- const requests = await index.getAll(threadId)
183
-
184
- let inputTokens = 0
185
- let outputTokens = 0
186
- let cost = 0.0
187
- let duration = 0
188
-
189
- requests.forEach(req => {
190
- inputTokens += req.inputTokens || 0
191
- outputTokens += req.outputTokens || 0
192
- cost += req.cost || 0.0
193
- duration += req.duration || 0
194
- })
195
-
196
- return {
197
- inputTokens,
198
- outputTokens,
199
- cost,
200
- duration,
201
- requests: requests.length
202
- }
203
- }
204
-
205
- // Add message to thread
206
- async function addMessageToThread(threadId, message, usage) {
207
- const thread = await getThread(threadId)
208
- if (!thread) throw new Error('Thread not found')
209
-
210
- const newMessage = {
211
- id: nextId(),
212
- timestamp: new Date().toISOString(),
213
- ...message
214
- }
215
-
216
- // Add input and output token usage to previous 'input' message
217
- if (usage?.prompt_tokens != null) {
218
- const lastMessage = thread.messages[thread.messages.length - 1]
219
- if (lastMessage && lastMessage.role === 'user') {
220
- lastMessage.usage = {
221
- tokens: parseInt(usage.prompt_tokens),
222
- price: usage.input || '0',
223
- }
224
- }
225
- }
226
- if (usage?.completion_tokens != null) {
227
- newMessage.usage = {
228
- tokens: parseInt(usage.completion_tokens),
229
- price: usage.output || '0',
230
- duration: usage.duration || undefined,
231
- }
232
- }
233
-
234
- const updatedMessages = [...thread.messages, newMessage]
235
-
236
- // Auto-generate title from first user message if still "New Chat"
237
- let title = thread.title
238
- if (title === 'New Chat' && message.role === 'user' && updatedMessages.length <= 2) {
239
- let contentText = message.content
240
- if (Array.isArray(contentText)) {
241
- contentText = contentText.filter(c => c.type === 'text').map(c => c.text).join(' ')
242
- }
243
- title = contentText.slice(0, 200) + (contentText.length > 200 ? '...' : '')
244
- }
245
-
246
- const stats = await calculateThreadStats(threadId)
247
-
248
- await updateThread(threadId, {
249
- messages: updatedMessages,
250
- title: title,
251
- stats,
252
- })
253
-
254
- return newMessage
255
- }
256
-
257
- async function deleteMessageFromThread(threadId, messageId) {
258
- const thread = await getThread(threadId)
259
- if (!thread) throw new Error('Thread not found')
260
- const updatedMessages = thread.messages.filter(m => m.id !== messageId)
261
- await updateThread(threadId, { messages: updatedMessages })
262
- }
263
-
264
- async function updateMessageInThread(threadId, messageId, updates) {
265
- const thread = await getThread(threadId)
266
- if (!thread) throw new Error('Thread not found')
267
-
268
- const messageIndex = thread.messages.findIndex(m => m.id === messageId)
269
- if (messageIndex === -1) throw new Error('Message not found')
270
-
271
- const updatedMessages = [...thread.messages]
272
- updatedMessages[messageIndex] = {
273
- ...updatedMessages[messageIndex],
274
- ...updates
275
- }
276
-
277
- await updateThread(threadId, { messages: updatedMessages })
278
- }
279
-
280
- async function redoMessageFromThread(threadId, messageId) {
281
- const thread = await getThread(threadId)
282
- if (!thread) throw new Error('Thread not found')
283
-
284
- // Find the index of the message to redo
285
- const messageIndex = thread.messages.findIndex(m => m.id === messageId)
286
- if (messageIndex === -1) throw new Error('Message not found')
287
-
288
- // Keep only messages up to and including the target message
289
- const updatedMessages = thread.messages.slice(0, messageIndex + 1)
290
-
291
- // Update the thread with the new messages
292
- await updateThread(threadId, { messages: updatedMessages })
293
- }
294
-
295
- // Get all threads
296
- async function loadThreads() {
297
- await initDB()
298
- isLoading.value = true
299
-
300
- try {
301
- const tx = db.transaction(['threads'], 'readonly')
302
- const store = tx.objectStore('threads')
303
- const index = store.index('updatedAt')
304
-
305
- const allThreads = await index.getAll()
306
- threads.value = allThreads.reverse() // Most recent first
307
-
308
- return threads.value
309
- } finally {
310
- isLoading.value = false
311
- }
312
- }
313
-
314
- // Get single thread
315
- async function getThread(threadId) {
316
- await initDB()
317
-
318
- const tx = db.transaction(['threads'], 'readonly')
319
- const thread = await tx.objectStore('threads').get(threadId)
320
-
321
- return thread
322
- }
323
-
324
- // Delete thread
325
- async function deleteThread(threadId) {
326
- await initDB()
327
-
328
- const tx = db.transaction(['threads'], 'readwrite')
329
- await tx.objectStore('threads').delete(threadId)
330
- await tx.complete
331
-
332
- threads.value = threads.value.filter(t => t.id !== threadId)
333
-
334
- if (currentThread.value?.id === threadId) {
335
- currentThread.value = null
336
- }
337
- }
338
-
339
- // Set current thread
340
- async function setCurrentThread(threadId) {
341
- const thread = await getThread(threadId)
342
- if (thread) {
343
- currentThread.value = thread
344
- }
345
- return thread
346
- }
347
-
348
- // Set current thread from router params (router-aware version)
349
- async function setCurrentThreadFromRoute(threadId, router) {
350
- if (!threadId) {
351
- currentThread.value = null
352
- return null
353
- }
354
-
355
- const thread = await getThread(threadId)
356
- if (thread) {
357
- currentThread.value = thread
358
- return thread
359
- } else {
360
- // Thread not found, redirect to home
361
- if (router) {
362
- router.push((globalThis.ai?.base || '') + '/')
363
- }
364
- currentThread.value = null
365
- return null
366
- }
367
- }
368
-
369
- // Clear current thread (go back to initial state)
370
- function clearCurrentThread() {
371
- currentThread.value = null
372
- }
373
-
374
- function getGroupedThreads(total) {
375
- const now = new Date()
376
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
377
- const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
378
- const lastMonth = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
379
-
380
- const groups = {
381
- today: [],
382
- lastWeek: [],
383
- lastMonth: [],
384
- older: {}
385
- }
386
-
387
- const takeThreads = threads.value.slice(0, total)
388
-
389
- takeThreads.forEach(thread => {
390
- const threadDate = new Date(thread.updatedAt)
391
-
392
- if (threadDate >= today) {
393
- groups.today.push(thread)
394
- } else if (threadDate >= lastWeek) {
395
- groups.lastWeek.push(thread)
396
- } else if (threadDate >= lastMonth) {
397
- groups.lastMonth.push(thread)
398
- } else {
399
- const year = threadDate.getFullYear()
400
- const month = threadDate.toLocaleString('default', { month: 'long' })
401
- const key = `${month} ${year}`
402
-
403
- if (!groups.older[key]) {
404
- groups.older[key] = []
405
- }
406
- groups.older[key].push(thread)
407
- }
408
- })
409
-
410
- return groups
411
- }
412
-
413
- // Group threads by time periods
414
- const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
415
-
416
- async function getAllRequests() {
417
- await initDB()
418
-
419
- const tx = db.transaction(['requests'], 'readonly')
420
- const store = tx.objectStore('requests')
421
- const allRequests = await store.getAll()
422
- return allRequests
423
- }
424
-
425
- async function getRequest(requestId) {
426
- await initDB()
427
-
428
- const tx = db.transaction(['requests'], 'readonly')
429
- const store = tx.objectStore('requests')
430
- const request = await store.get(requestId)
431
- return request
432
- }
433
-
434
- async function getAllRequestIds() {
435
- await initDB()
436
-
437
- const tx = db.transaction(['requests'], 'readonly')
438
- const store = tx.objectStore('requests')
439
- const ids = await store.getAllKeys()
440
- return ids
441
- }
442
-
443
- async function getAllThreadIds() {
444
- await initDB()
445
- const tx = db.transaction(['threads'], 'readonly')
446
- const store = tx.objectStore('threads')
447
- const ids = await store.getAllKeys()
448
- return ids
449
- }
450
-
451
- // Query requests with pagination and filtering
452
- async function getRequests(filters = {}, limit = 20, offset = 0) {
453
- try {
454
- await initDB()
455
-
456
- const {
457
- model = null,
458
- provider = null,
459
- threadId = null,
460
- sortBy = 'created',
461
- sortOrder = 'desc',
462
- startDate = null,
463
- endDate = null
464
- } = filters
465
-
466
- const tx = db.transaction(['requests'], 'readonly')
467
- const store = tx.objectStore('requests')
468
-
469
- // Get all requests and filter in memory (IndexedDB limitations)
470
- const allRequests = await store.getAll()
471
-
472
- // Apply filters
473
- let results = allRequests.filter(req => {
474
- if (model && req.model !== model) return false
475
- if (provider && req.provider !== provider) return false
476
- if (threadId && req.threadId !== threadId) return false
477
- if (startDate && req.created < startDate) return false
478
- if (endDate && req.created > endDate) return false
479
- return true
480
- })
481
-
482
- // Sort
483
- results.sort((a, b) => {
484
- let aVal = a[sortBy]
485
- let bVal = b[sortBy]
486
-
487
- if (sortOrder === 'desc') {
488
- return bVal - aVal
489
- } else {
490
- return aVal - bVal
491
- }
492
- })
493
-
494
- // Paginate
495
- const total = results.length
496
- const paginatedResults = results.slice(offset, offset + limit)
497
-
498
- return {
499
- requests: paginatedResults,
500
- total,
501
- hasMore: offset + limit < total
502
- }
503
- } catch (error) {
504
- console.error('Error in getRequests:', error)
505
- return {
506
- requests: [],
507
- total: 0,
508
- hasMore: false
509
- }
510
- }
511
- }
512
-
513
- // Get unique values for filter options
514
- async function getFilterOptions() {
515
- try {
516
- await initDB()
517
-
518
- const tx = db.transaction(['requests'], 'readonly')
519
- const store = tx.objectStore('requests')
520
- const allRequests = await store.getAll()
521
-
522
- const models = [...new Set(allRequests.map(r => r.model).filter(m => m))].sort()
523
- const providers = [...new Set(allRequests.map(r => r.provider).filter(p => p))].sort()
524
-
525
- return {
526
- models,
527
- providers
528
- }
529
- } catch (error) {
530
- console.error('Error in getFilterOptions:', error)
531
- return {
532
- models: [],
533
- providers: []
534
- }
535
- }
536
- }
537
-
538
- // Delete a request by ID
539
- async function deleteRequest(requestId) {
540
- await initDB()
541
-
542
- const tx = db.transaction(['requests'], 'readwrite')
543
- await tx.objectStore('requests').delete(requestId)
544
- await tx.complete
545
- }
546
-
547
- function getLatestCachedThread() {
548
- return threads.value[0]
549
- }
550
- async function getLatestThread() {
551
- await initDB()
552
- const tx = db.transaction(['threads'], 'readonly')
553
- const store = tx.objectStore('threads')
554
- // Open cursor in reverse order (prev = previous/descending)
555
- const cursor = await store.openCursor(null, 'prev');
556
- return cursor ? cursor.value : null;
557
- }
558
-
559
- async function startNewThread({ title, model }) {
560
- if (!model) {
561
- console.error('No model selected')
562
- return
563
- }
564
- if (!title) {
565
- title = 'New Chat'
566
- }
567
- const latestThread = getLatestCachedThread()
568
-
569
- console.log('startNewThread', title, model.name, ctx.router.currentRoute.value?.path, latestThread?.messages?.length)
570
- ctx.setLayout({ left: 'ThreadsSidebar' })
571
-
572
- if (latestThread && latestThread.title == title && !latestThread.messages?.length) {
573
- if (ctx.router.currentRoute.value?.path != `/c/${latestThread.id}`) {
574
- ctx.to(`/c/${latestThread.id}`)
575
- }
576
- return latestThread
577
- }
578
- const newThread = await createThread({
579
- title,
580
- model: model.name,
581
- info: ctx.utils.toModelInfo(model),
582
- })
583
-
584
- console.log('newThread', newThread, model)
585
- // Navigate to the new thread URL
586
- ctx.to(`/c/${newThread.id}`)
587
-
588
- // Get the thread to check for duplicates
589
- let thread = await getThread(newThread.id)
590
- return thread
591
- }
592
-
593
-
594
- // Export the store
595
- export function useThreadStore() {
596
- return {
597
- // State
598
- threads,
599
- currentThread,
600
- isLoading,
601
- groupedThreads,
602
-
603
- // Actions
604
- initDB,
605
- logRequest,
606
- createThread,
607
- updateThread,
608
- addMessageToThread,
609
- deleteMessageFromThread,
610
- updateMessageInThread,
611
- redoMessageFromThread,
612
- loadThreads,
613
- getThread,
614
- deleteThread,
615
- setCurrentThread,
616
- setCurrentThreadFromRoute,
617
- clearCurrentThread,
618
- getGroupedThreads,
619
- getRequest,
620
- getRequests,
621
- getAllRequests,
622
- getFilterOptions,
623
- deleteRequest,
624
- getAllRequestIds,
625
- getAllThreadIds,
626
- getLatestThread,
627
- getLatestCachedThread,
628
- generateThreadId,
629
- startNewThread,
630
- }
631
- }
632
-
633
- export default {
634
- install(context) {
635
- ctx = context
636
- ctx.setGlobals({
637
- threads: useThreadStore()
638
- })
639
- }
640
- }