llms-py 2.0.20__py3-none-any.whl → 3.0.10__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 (190) hide show
  1. llms/__init__.py +3 -1
  2. llms/db.py +359 -0
  3. llms/{ui/Analytics.mjs → extensions/analytics/ui/index.mjs} +254 -327
  4. llms/extensions/app/README.md +20 -0
  5. llms/extensions/app/__init__.py +589 -0
  6. llms/extensions/app/db.py +536 -0
  7. llms/{ui → extensions/app/ui}/Recents.mjs +99 -73
  8. llms/{ui/Sidebar.mjs → extensions/app/ui/index.mjs} +139 -68
  9. llms/extensions/app/ui/threadStore.mjs +433 -0
  10. llms/extensions/core_tools/CALCULATOR.md +32 -0
  11. llms/extensions/core_tools/__init__.py +637 -0
  12. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
  13. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
  14. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
  15. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
  16. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
  17. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
  18. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
  19. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
  20. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
  21. llms/extensions/core_tools/ui/codemirror/codemirror.css +344 -0
  22. llms/extensions/core_tools/ui/codemirror/codemirror.js +9884 -0
  23. llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
  24. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  25. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
  26. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
  27. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
  28. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
  29. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
  30. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
  31. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
  32. llms/extensions/core_tools/ui/index.mjs +650 -0
  33. llms/extensions/gallery/README.md +61 -0
  34. llms/extensions/gallery/__init__.py +63 -0
  35. llms/extensions/gallery/db.py +243 -0
  36. llms/extensions/gallery/ui/index.mjs +482 -0
  37. llms/extensions/katex/README.md +39 -0
  38. llms/extensions/katex/__init__.py +6 -0
  39. llms/extensions/katex/ui/README.md +125 -0
  40. llms/extensions/katex/ui/contrib/auto-render.js +338 -0
  41. llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
  42. llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
  43. llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
  44. llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
  45. llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
  46. llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
  47. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
  48. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
  49. llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
  50. llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
  51. llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
  52. llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
  53. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
  54. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
  55. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  56. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  57. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  58. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  59. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  60. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  61. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  62. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  63. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  64. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  65. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  66. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  67. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  68. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  69. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  70. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  71. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  72. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  73. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  74. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  75. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  76. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  77. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  78. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  79. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  80. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  81. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  82. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  83. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  84. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  85. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  86. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  87. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  88. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  89. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  90. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  91. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  92. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  93. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  94. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  115. llms/extensions/katex/ui/index.mjs +92 -0
  116. llms/extensions/katex/ui/katex-swap.css +1230 -0
  117. llms/extensions/katex/ui/katex-swap.min.css +1 -0
  118. llms/extensions/katex/ui/katex.css +1230 -0
  119. llms/extensions/katex/ui/katex.js +19080 -0
  120. llms/extensions/katex/ui/katex.min.css +1 -0
  121. llms/extensions/katex/ui/katex.min.js +1 -0
  122. llms/extensions/katex/ui/katex.min.mjs +1 -0
  123. llms/extensions/katex/ui/katex.mjs +18547 -0
  124. llms/extensions/providers/__init__.py +22 -0
  125. llms/extensions/providers/anthropic.py +233 -0
  126. llms/extensions/providers/cerebras.py +37 -0
  127. llms/extensions/providers/chutes.py +153 -0
  128. llms/extensions/providers/google.py +481 -0
  129. llms/extensions/providers/nvidia.py +103 -0
  130. llms/extensions/providers/openai.py +154 -0
  131. llms/extensions/providers/openrouter.py +74 -0
  132. llms/extensions/providers/zai.py +182 -0
  133. llms/extensions/system_prompts/README.md +22 -0
  134. llms/extensions/system_prompts/__init__.py +45 -0
  135. llms/extensions/system_prompts/ui/index.mjs +280 -0
  136. llms/extensions/system_prompts/ui/prompts.json +1067 -0
  137. llms/extensions/tools/__init__.py +144 -0
  138. llms/extensions/tools/ui/index.mjs +706 -0
  139. llms/index.html +36 -62
  140. llms/llms.json +180 -879
  141. llms/main.py +3640 -899
  142. llms/providers-extra.json +394 -0
  143. llms/providers.json +1 -0
  144. llms/ui/App.mjs +176 -8
  145. llms/ui/ai.mjs +156 -20
  146. llms/ui/app.css +3161 -244
  147. llms/ui/ctx.mjs +412 -0
  148. llms/ui/index.mjs +131 -0
  149. llms/ui/lib/chart.js +14 -0
  150. llms/ui/lib/charts.mjs +16 -0
  151. llms/ui/lib/color.js +14 -0
  152. llms/ui/lib/highlight.min.mjs +1243 -0
  153. llms/ui/lib/idb.min.mjs +8 -0
  154. llms/ui/lib/marked.min.mjs +8 -0
  155. llms/ui/lib/servicestack-client.mjs +1 -0
  156. llms/ui/lib/servicestack-vue.mjs +37 -0
  157. llms/ui/lib/vue-router.min.mjs +6 -0
  158. llms/ui/lib/vue.min.mjs +13 -0
  159. llms/ui/lib/vue.mjs +18530 -0
  160. llms/ui/markdown.mjs +25 -14
  161. llms/ui/modules/chat/ChatBody.mjs +976 -0
  162. llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +74 -74
  163. llms/ui/modules/chat/index.mjs +991 -0
  164. llms/ui/modules/icons.mjs +46 -0
  165. llms/ui/modules/layout.mjs +271 -0
  166. llms/ui/modules/model-selector.mjs +811 -0
  167. llms/ui/tailwind.input.css +550 -78
  168. llms/ui/typography.css +54 -36
  169. llms/ui/utils.mjs +197 -92
  170. llms_py-3.0.10.dist-info/METADATA +49 -0
  171. llms_py-3.0.10.dist-info/RECORD +177 -0
  172. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/licenses/LICENSE +1 -2
  173. llms/ui/Avatar.mjs +0 -28
  174. llms/ui/Brand.mjs +0 -34
  175. llms/ui/ChatPrompt.mjs +0 -443
  176. llms/ui/Main.mjs +0 -740
  177. llms/ui/ModelSelector.mjs +0 -60
  178. llms/ui/ProviderIcon.mjs +0 -29
  179. llms/ui/ProviderStatus.mjs +0 -105
  180. llms/ui/SignIn.mjs +0 -64
  181. llms/ui/SystemPromptEditor.mjs +0 -31
  182. llms/ui/SystemPromptSelector.mjs +0 -36
  183. llms/ui/Welcome.mjs +0 -8
  184. llms/ui/threadStore.mjs +0 -524
  185. llms/ui.json +0 -1069
  186. llms_py-2.0.20.dist-info/METADATA +0 -931
  187. llms_py-2.0.20.dist-info/RECORD +0 -36
  188. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/WHEEL +0 -0
  189. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/entry_points.txt +0 -0
  190. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/top_level.txt +0 -0
llms/ui/threadStore.mjs DELETED
@@ -1,524 +0,0 @@
1
- import { ref, computed, unref } from 'vue'
2
- import { openDB } from 'idb'
3
- import { nextId, toModelInfo } 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
-
12
- // Initialize IndexedDB
13
- async function initDB() {
14
- if (db) return db
15
-
16
- db = await openDB('LlmsThreads', 3, {
17
- upgrade(db, _oldVersion, _newVersion, transaction) {
18
- if (!db.objectStoreNames.contains('threads')) {
19
- // Create threads store
20
- const threadStore = db.createObjectStore('threads', {
21
- keyPath: 'id',
22
- autoIncrement: false
23
- })
24
-
25
- // Create indexes for efficient querying
26
- threadStore.createIndex('createdAt', 'createdAt')
27
- threadStore.createIndex('updatedAt', 'updatedAt')
28
- threadStore.createIndex('title', 'title')
29
- }
30
- if (!db.objectStoreNames.contains('requests')) {
31
- // Create requests store
32
- const requestStore = db.createObjectStore('requests', {
33
- keyPath: 'id',
34
- autoIncrement: false
35
- })
36
- requestStore.createIndex('threadId', 'threadId')
37
- requestStore.createIndex('model', 'model')
38
- requestStore.createIndex('provider', 'provider')
39
- requestStore.createIndex('inputTokens', 'inputTokens')
40
- requestStore.createIndex('outputTokens', 'outputTokens')
41
- requestStore.createIndex('cost', 'cost')
42
- requestStore.createIndex('duration', 'duration')
43
- requestStore.createIndex('created', 'created')
44
- }
45
- }
46
- })
47
-
48
- return db
49
- }
50
-
51
- // Generate unique thread ID
52
- function generateThreadId() {
53
- return Date.now().toString()
54
- }
55
-
56
- async function logRequest(threadId, model, request, response) {
57
- await initDB()
58
- const metadata = response.metadata || {}
59
- const usage = response.usage || {}
60
- const [inputPrice, outputPrice] = metadata.pricing ? metadata.pricing.split('/') : [0, 0]
61
- const lastUserContent = request.messages?.slice().reverse().find(m => m.role === 'user')?.content
62
- const content = Array.isArray(lastUserContent)
63
- ? lastUserContent.filter(c => c?.text).map(c => c.text).join(' ')
64
- : lastUserContent
65
- const title = content.slice(0, 100) + (content.length > 100 ? '...' : '')
66
- const inputTokens = usage?.prompt_tokens ?? 0
67
- const outputTokens = usage?.completion_tokens ?? 0
68
- const inputCachedTokens = usage?.prompt_token_details?.cached_tokens ?? 0
69
- const finishReason = response.choices[0]?.finish_reason || 'unknown'
70
-
71
- const subtractDays = (date, days) => {
72
- const result = new Date(date * 1000)
73
- result.setDate(result.getDate() - days)
74
- return parseInt(result.valueOf() / 1000)
75
- }
76
-
77
- const log = {
78
- id: nextId(),
79
- threadId: threadId,
80
- model: model.id,
81
- provider: model.provider,
82
- providerModel: response.model || model.provider_model,
83
- title,
84
- inputTokens,
85
- outputTokens,
86
- inputCachedTokens,
87
- totalTokens: usage.total_tokens ?? (inputTokens + outputTokens),
88
- inputPrice,
89
- outputPrice,
90
- cost: (parseFloat(inputPrice) * inputTokens) + (parseFloat(outputPrice) * outputTokens),
91
- duration: metadata.duration ?? 0,
92
- created: response.created ?? Math.floor(Date.now() / 1000),
93
- finishReason,
94
- providerRef: response.provider,
95
- ref: response.id || undefined,
96
- usage: usage,
97
- }
98
- console.debug('logRequest', log)
99
- const tx = db.transaction(['requests'], 'readwrite')
100
- await tx.objectStore('requests').add(log)
101
- await tx.complete
102
- return log
103
- }
104
-
105
- // Create a new thread
106
- async function createThread(title = 'New Chat', model = null, systemPrompt = '') {
107
- await initDB()
108
-
109
- const thread = {
110
- id: generateThreadId(),
111
- title: title,
112
- model: model?.id ?? '',
113
- info: toModelInfo(model),
114
- systemPrompt: systemPrompt,
115
- messages: [],
116
- createdAt: new Date().toISOString(),
117
- updatedAt: new Date().toISOString()
118
- }
119
-
120
- const tx = db.transaction(['threads'], 'readwrite')
121
- await tx.objectStore('threads').add(thread)
122
- await tx.complete
123
-
124
- threads.value.unshift(thread)
125
- // Note: currentThread will be set by router navigation
126
-
127
- return thread
128
- }
129
-
130
- // Update thread
131
- async function updateThread(threadId, updates) {
132
- await initDB()
133
-
134
- const tx = db.transaction(['threads'], 'readwrite')
135
- const store = tx.objectStore('threads')
136
-
137
- const thread = await store.get(threadId)
138
- if (!thread) throw new Error('Thread not found')
139
-
140
- const updatedThread = {
141
- ...thread,
142
- ...updates,
143
- updatedAt: new Date().toISOString()
144
- }
145
-
146
- await store.put(updatedThread)
147
- await tx.complete
148
-
149
- // Update in memory
150
- const index = threads.value.findIndex(t => t.id === threadId)
151
- if (index !== -1) {
152
- threads.value[index] = updatedThread
153
- }
154
-
155
- if (currentThread.value?.id === threadId) {
156
- currentThread.value = updatedThread
157
- }
158
-
159
- return updatedThread
160
- }
161
-
162
- async function calculateThreadStats(threadId) {
163
- await initDB()
164
-
165
- const tx = db.transaction(['requests'], 'readonly')
166
- const store = tx.objectStore('requests')
167
- const index = store.index('threadId')
168
-
169
- const requests = await index.getAll(threadId)
170
-
171
- let inputTokens = 0
172
- let outputTokens = 0
173
- let cost = 0.0
174
- let duration = 0
175
-
176
- requests.forEach(req => {
177
- inputTokens += req.inputTokens || 0
178
- outputTokens += req.outputTokens || 0
179
- cost += req.cost || 0.0
180
- duration += req.duration || 0
181
- })
182
-
183
- return {
184
- inputTokens,
185
- outputTokens,
186
- cost,
187
- duration,
188
- requests: requests.length
189
- }
190
- }
191
-
192
- // Add message to thread
193
- async function addMessageToThread(threadId, message, usage) {
194
- const thread = await getThread(threadId)
195
- if (!thread) throw new Error('Thread not found')
196
-
197
- const newMessage = {
198
- id: nextId(),
199
- timestamp: new Date().toISOString(),
200
- ...message
201
- }
202
-
203
- // Add input and output token usage to previous 'input' message
204
- if (usage?.prompt_tokens != null) {
205
- const lastMessage = thread.messages[thread.messages.length - 1]
206
- if (lastMessage && lastMessage.role === 'user') {
207
- lastMessage.usage = {
208
- tokens: parseInt(usage.prompt_tokens),
209
- price: usage.input || '0',
210
- }
211
- }
212
- }
213
- if (usage?.completion_tokens != null) {
214
- newMessage.usage = {
215
- tokens: parseInt(usage.completion_tokens),
216
- price: usage.output || '0',
217
- duration: usage.duration || undefined,
218
- }
219
- }
220
-
221
- const updatedMessages = [...thread.messages, newMessage]
222
-
223
- // Auto-generate title from first user message if still "New Chat"
224
- let title = thread.title
225
- if (title === 'New Chat' && message.role === 'user' && updatedMessages.length <= 2) {
226
- title = message.content.slice(0, 200) + (message.content.length > 200 ? '...' : '')
227
- }
228
-
229
- const stats = await calculateThreadStats(threadId)
230
-
231
- await updateThread(threadId, {
232
- messages: updatedMessages,
233
- title: title,
234
- stats,
235
- })
236
-
237
- return newMessage
238
- }
239
-
240
- async function deleteMessageFromThread(threadId, messageId) {
241
- const thread = await getThread(threadId)
242
- if (!thread) throw new Error('Thread not found')
243
- const updatedMessages = thread.messages.filter(m => m.id !== messageId)
244
- await updateThread(threadId, { messages: updatedMessages })
245
- }
246
-
247
- async function updateMessageInThread(threadId, messageId, updates) {
248
- const thread = await getThread(threadId)
249
- if (!thread) throw new Error('Thread not found')
250
-
251
- const messageIndex = thread.messages.findIndex(m => m.id === messageId)
252
- if (messageIndex === -1) throw new Error('Message not found')
253
-
254
- const updatedMessages = [...thread.messages]
255
- updatedMessages[messageIndex] = {
256
- ...updatedMessages[messageIndex],
257
- ...updates
258
- }
259
-
260
- await updateThread(threadId, { messages: updatedMessages })
261
- }
262
-
263
- async function redoMessageFromThread(threadId, messageId) {
264
- const thread = await getThread(threadId)
265
- if (!thread) throw new Error('Thread not found')
266
-
267
- // Find the index of the message to redo
268
- const messageIndex = thread.messages.findIndex(m => m.id === messageId)
269
- if (messageIndex === -1) throw new Error('Message not found')
270
-
271
- // Keep only messages up to and including the target message
272
- const updatedMessages = thread.messages.slice(0, messageIndex + 1)
273
-
274
- // Update the thread with the new messages
275
- await updateThread(threadId, { messages: updatedMessages })
276
- }
277
-
278
- // Get all threads
279
- async function loadThreads() {
280
- await initDB()
281
- isLoading.value = true
282
-
283
- try {
284
- const tx = db.transaction(['threads'], 'readonly')
285
- const store = tx.objectStore('threads')
286
- const index = store.index('updatedAt')
287
-
288
- const allThreads = await index.getAll()
289
- threads.value = allThreads.reverse() // Most recent first
290
-
291
- return threads.value
292
- } finally {
293
- isLoading.value = false
294
- }
295
- }
296
-
297
- // Get single thread
298
- async function getThread(threadId) {
299
- await initDB()
300
-
301
- const tx = db.transaction(['threads'], 'readonly')
302
- const thread = await tx.objectStore('threads').get(threadId)
303
-
304
- return thread
305
- }
306
-
307
- // Delete thread
308
- async function deleteThread(threadId) {
309
- await initDB()
310
-
311
- const tx = db.transaction(['threads'], 'readwrite')
312
- await tx.objectStore('threads').delete(threadId)
313
- await tx.complete
314
-
315
- threads.value = threads.value.filter(t => t.id !== threadId)
316
-
317
- if (currentThread.value?.id === threadId) {
318
- currentThread.value = null
319
- }
320
- }
321
-
322
- // Set current thread
323
- async function setCurrentThread(threadId) {
324
- const thread = await getThread(threadId)
325
- if (thread) {
326
- currentThread.value = thread
327
- }
328
- return thread
329
- }
330
-
331
- // Set current thread from router params (router-aware version)
332
- async function setCurrentThreadFromRoute(threadId, router) {
333
- if (!threadId) {
334
- currentThread.value = null
335
- return null
336
- }
337
-
338
- const thread = await getThread(threadId)
339
- if (thread) {
340
- currentThread.value = thread
341
- return thread
342
- } else {
343
- // Thread not found, redirect to home
344
- if (router) {
345
- router.push((globalThis.ai?.base || '') + '/')
346
- }
347
- currentThread.value = null
348
- return null
349
- }
350
- }
351
-
352
- // Clear current thread (go back to initial state)
353
- function clearCurrentThread() {
354
- currentThread.value = null
355
- }
356
-
357
- function getGroupedThreads(total) {
358
- const now = new Date()
359
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
360
- const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
361
- const lastMonth = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
362
-
363
- const groups = {
364
- today: [],
365
- lastWeek: [],
366
- lastMonth: [],
367
- older: {}
368
- }
369
-
370
- const takeThreads = threads.value.slice(0, total)
371
-
372
- takeThreads.forEach(thread => {
373
- const threadDate = new Date(thread.updatedAt)
374
-
375
- if (threadDate >= today) {
376
- groups.today.push(thread)
377
- } else if (threadDate >= lastWeek) {
378
- groups.lastWeek.push(thread)
379
- } else if (threadDate >= lastMonth) {
380
- groups.lastMonth.push(thread)
381
- } else {
382
- const year = threadDate.getFullYear()
383
- const month = threadDate.toLocaleString('default', { month: 'long' })
384
- const key = `${month} ${year}`
385
-
386
- if (!groups.older[key]) {
387
- groups.older[key] = []
388
- }
389
- groups.older[key].push(thread)
390
- }
391
- })
392
-
393
- return groups
394
- }
395
-
396
- // Group threads by time periods
397
- const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
398
-
399
- // Query requests with pagination and filtering
400
- async function getRequests(filters = {}, limit = 20, offset = 0) {
401
- try {
402
- await initDB()
403
-
404
- const {
405
- model = null,
406
- provider = null,
407
- threadId = null,
408
- sortBy = 'created',
409
- sortOrder = 'desc',
410
- startDate = null,
411
- endDate = null
412
- } = filters
413
-
414
- const tx = db.transaction(['requests'], 'readonly')
415
- const store = tx.objectStore('requests')
416
-
417
- // Get all requests and filter in memory (IndexedDB limitations)
418
- const allRequests = await store.getAll()
419
-
420
- // Apply filters
421
- let results = allRequests.filter(req => {
422
- if (model && req.model !== model) return false
423
- if (provider && req.provider !== provider) return false
424
- if (threadId && req.threadId !== threadId) return false
425
- if (startDate && req.created < startDate) return false
426
- if (endDate && req.created > endDate) return false
427
- return true
428
- })
429
-
430
- // Sort
431
- results.sort((a, b) => {
432
- let aVal = a[sortBy]
433
- let bVal = b[sortBy]
434
-
435
- if (sortOrder === 'desc') {
436
- return bVal - aVal
437
- } else {
438
- return aVal - bVal
439
- }
440
- })
441
-
442
- // Paginate
443
- const total = results.length
444
- const paginatedResults = results.slice(offset, offset + limit)
445
-
446
- return {
447
- requests: paginatedResults,
448
- total,
449
- hasMore: offset + limit < total
450
- }
451
- } catch (error) {
452
- console.error('Error in getRequests:', error)
453
- return {
454
- requests: [],
455
- total: 0,
456
- hasMore: false
457
- }
458
- }
459
- }
460
-
461
- // Get unique values for filter options
462
- async function getFilterOptions() {
463
- try {
464
- await initDB()
465
-
466
- const tx = db.transaction(['requests'], 'readonly')
467
- const store = tx.objectStore('requests')
468
- const allRequests = await store.getAll()
469
-
470
- const models = [...new Set(allRequests.map(r => r.model).filter(m => m))].sort()
471
- const providers = [...new Set(allRequests.map(r => r.provider).filter(p => p))].sort()
472
-
473
- return {
474
- models,
475
- providers
476
- }
477
- } catch (error) {
478
- console.error('Error in getFilterOptions:', error)
479
- return {
480
- models: [],
481
- providers: []
482
- }
483
- }
484
- }
485
-
486
- // Delete a request by ID
487
- async function deleteRequest(requestId) {
488
- await initDB()
489
-
490
- const tx = db.transaction(['requests'], 'readwrite')
491
- await tx.objectStore('requests').delete(requestId)
492
- await tx.complete
493
- }
494
-
495
- // Export the store
496
- export function useThreadStore() {
497
- return {
498
- // State
499
- threads,
500
- currentThread,
501
- isLoading,
502
- groupedThreads,
503
-
504
- // Actions
505
- initDB,
506
- logRequest,
507
- createThread,
508
- updateThread,
509
- addMessageToThread,
510
- deleteMessageFromThread,
511
- updateMessageInThread,
512
- redoMessageFromThread,
513
- loadThreads,
514
- getThread,
515
- deleteThread,
516
- setCurrentThread,
517
- setCurrentThreadFromRoute,
518
- clearCurrentThread,
519
- getGroupedThreads,
520
- getRequests,
521
- getFilterOptions,
522
- deleteRequest,
523
- }
524
- }