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.
- llms/__init__.py +3 -1
- llms/db.py +359 -0
- llms/{ui/Analytics.mjs → extensions/analytics/ui/index.mjs} +254 -327
- llms/extensions/app/README.md +20 -0
- llms/extensions/app/__init__.py +589 -0
- llms/extensions/app/db.py +536 -0
- llms/{ui → extensions/app/ui}/Recents.mjs +99 -73
- llms/{ui/Sidebar.mjs → extensions/app/ui/index.mjs} +139 -68
- llms/extensions/app/ui/threadStore.mjs +433 -0
- llms/extensions/core_tools/CALCULATOR.md +32 -0
- llms/extensions/core_tools/__init__.py +637 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
- llms/extensions/core_tools/ui/codemirror/codemirror.css +344 -0
- llms/extensions/core_tools/ui/codemirror/codemirror.js +9884 -0
- llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
- llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
- llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
- llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
- llms/extensions/core_tools/ui/index.mjs +650 -0
- llms/extensions/gallery/README.md +61 -0
- llms/extensions/gallery/__init__.py +63 -0
- llms/extensions/gallery/db.py +243 -0
- llms/extensions/gallery/ui/index.mjs +482 -0
- llms/extensions/katex/README.md +39 -0
- llms/extensions/katex/__init__.py +6 -0
- llms/extensions/katex/ui/README.md +125 -0
- llms/extensions/katex/ui/contrib/auto-render.js +338 -0
- llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
- llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
- llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
- llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
- llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
- llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
- llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
- llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- llms/extensions/katex/ui/index.mjs +92 -0
- llms/extensions/katex/ui/katex-swap.css +1230 -0
- llms/extensions/katex/ui/katex-swap.min.css +1 -0
- llms/extensions/katex/ui/katex.css +1230 -0
- llms/extensions/katex/ui/katex.js +19080 -0
- llms/extensions/katex/ui/katex.min.css +1 -0
- llms/extensions/katex/ui/katex.min.js +1 -0
- llms/extensions/katex/ui/katex.min.mjs +1 -0
- llms/extensions/katex/ui/katex.mjs +18547 -0
- llms/extensions/providers/__init__.py +22 -0
- llms/extensions/providers/anthropic.py +233 -0
- llms/extensions/providers/cerebras.py +37 -0
- llms/extensions/providers/chutes.py +153 -0
- llms/extensions/providers/google.py +481 -0
- llms/extensions/providers/nvidia.py +103 -0
- llms/extensions/providers/openai.py +154 -0
- llms/extensions/providers/openrouter.py +74 -0
- llms/extensions/providers/zai.py +182 -0
- llms/extensions/system_prompts/README.md +22 -0
- llms/extensions/system_prompts/__init__.py +45 -0
- llms/extensions/system_prompts/ui/index.mjs +280 -0
- llms/extensions/system_prompts/ui/prompts.json +1067 -0
- llms/extensions/tools/__init__.py +144 -0
- llms/extensions/tools/ui/index.mjs +706 -0
- llms/index.html +36 -62
- llms/llms.json +180 -879
- llms/main.py +3640 -899
- llms/providers-extra.json +394 -0
- llms/providers.json +1 -0
- llms/ui/App.mjs +176 -8
- llms/ui/ai.mjs +156 -20
- llms/ui/app.css +3161 -244
- llms/ui/ctx.mjs +412 -0
- llms/ui/index.mjs +131 -0
- llms/ui/lib/chart.js +14 -0
- llms/ui/lib/charts.mjs +16 -0
- llms/ui/lib/color.js +14 -0
- llms/ui/lib/highlight.min.mjs +1243 -0
- llms/ui/lib/idb.min.mjs +8 -0
- llms/ui/lib/marked.min.mjs +8 -0
- llms/ui/lib/servicestack-client.mjs +1 -0
- llms/ui/lib/servicestack-vue.mjs +37 -0
- llms/ui/lib/vue-router.min.mjs +6 -0
- llms/ui/lib/vue.min.mjs +13 -0
- llms/ui/lib/vue.mjs +18530 -0
- llms/ui/markdown.mjs +25 -14
- llms/ui/modules/chat/ChatBody.mjs +976 -0
- llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +74 -74
- llms/ui/modules/chat/index.mjs +991 -0
- llms/ui/modules/icons.mjs +46 -0
- llms/ui/modules/layout.mjs +271 -0
- llms/ui/modules/model-selector.mjs +811 -0
- llms/ui/tailwind.input.css +550 -78
- llms/ui/typography.css +54 -36
- llms/ui/utils.mjs +197 -92
- llms_py-3.0.10.dist-info/METADATA +49 -0
- llms_py-3.0.10.dist-info/RECORD +177 -0
- {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/licenses/LICENSE +1 -2
- llms/ui/Avatar.mjs +0 -28
- llms/ui/Brand.mjs +0 -34
- llms/ui/ChatPrompt.mjs +0 -443
- llms/ui/Main.mjs +0 -740
- llms/ui/ModelSelector.mjs +0 -60
- llms/ui/ProviderIcon.mjs +0 -29
- llms/ui/ProviderStatus.mjs +0 -105
- llms/ui/SignIn.mjs +0 -64
- llms/ui/SystemPromptEditor.mjs +0 -31
- llms/ui/SystemPromptSelector.mjs +0 -36
- llms/ui/Welcome.mjs +0 -8
- llms/ui/threadStore.mjs +0 -524
- llms/ui.json +0 -1069
- llms_py-2.0.20.dist-info/METADATA +0 -931
- llms_py-2.0.20.dist-info/RECORD +0 -36
- {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/WHEEL +0 -0
- {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/entry_points.txt +0 -0
- {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { ref, computed } from 'vue'
|
|
2
|
+
import { appendQueryString } 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 threadDetails = ref({})
|
|
19
|
+
const currentThread = ref(null)
|
|
20
|
+
const isLoading = ref(false)
|
|
21
|
+
|
|
22
|
+
let ctx = null
|
|
23
|
+
let ext = null
|
|
24
|
+
|
|
25
|
+
function setError(error, msg = null) {
|
|
26
|
+
ctx?.setError(error, msg)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function query(query) {
|
|
30
|
+
return (await ext.getJson(appendQueryString(`/threads`, query))).response || []
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let watchThreadTimeout = ref(null)
|
|
34
|
+
async function watchThreadUpdates() {
|
|
35
|
+
const thread = currentThread.value
|
|
36
|
+
// console.debug('watchThreadUpdates', thread?.id, thread?.messages?.length, thread?.completedAt)
|
|
37
|
+
if (thread && thread?.messages?.length && !thread.completedAt) {
|
|
38
|
+
const api = await ext.getJson(appendQueryString(`/threads/${thread.id}/updates`, { after: thread.updatedAt }))
|
|
39
|
+
// console.log('watchThreadUpdates', api)
|
|
40
|
+
if (api.response) {
|
|
41
|
+
replaceThread(api.response)
|
|
42
|
+
return
|
|
43
|
+
} else {
|
|
44
|
+
setError(api.error, `watching thread ${thread.id}`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
stopWatchingThread()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function startWatchingThread() {
|
|
51
|
+
stopWatchingThread()
|
|
52
|
+
const thread = currentThread.value
|
|
53
|
+
if (thread && thread?.messages?.length && !thread.completedAt) {
|
|
54
|
+
watchThreadTimeout.value = setTimeout(watchThreadUpdates, 100)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function stopWatchingThread() {
|
|
59
|
+
console.debug('stopWatchingThread')
|
|
60
|
+
if (watchThreadTimeout.value) {
|
|
61
|
+
clearTimeout(watchThreadTimeout.value)
|
|
62
|
+
}
|
|
63
|
+
watchThreadTimeout.value = null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const isWatchingThread = computed(() => watchThreadTimeout.value != null)
|
|
67
|
+
|
|
68
|
+
async function cancelThread() {
|
|
69
|
+
console.log('cancelThread')
|
|
70
|
+
stopWatchingThread()
|
|
71
|
+
const thread = currentThread.value
|
|
72
|
+
if (!thread) return
|
|
73
|
+
const api = await ext.postJson(`/threads/${thread.id}/cancel`)
|
|
74
|
+
if (api.response) {
|
|
75
|
+
replaceThread(api.response)
|
|
76
|
+
} else {
|
|
77
|
+
setError(api.error, `Canceling thread ${thread.id}`)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Create a new thread
|
|
82
|
+
async function createThread(args = {}) {
|
|
83
|
+
const thread = {
|
|
84
|
+
messages: [],
|
|
85
|
+
...args
|
|
86
|
+
}
|
|
87
|
+
if (!thread.title) {
|
|
88
|
+
thread.title = 'New Chat'
|
|
89
|
+
}
|
|
90
|
+
if (thread.title.length > 200) {
|
|
91
|
+
thread.title = thread.title.slice(0, 200) + '...'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ctx.createThreadFilters.forEach(f => f(thread))
|
|
95
|
+
|
|
96
|
+
const api = await ext.postJson("/threads", thread)
|
|
97
|
+
if (api.response) {
|
|
98
|
+
threads.value.unshift(api.response)
|
|
99
|
+
return api.response
|
|
100
|
+
} else {
|
|
101
|
+
setError(api.error, `Creating thread ${thread.title}`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return thread
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function replaceThread(thread) {
|
|
108
|
+
if (!thread) {
|
|
109
|
+
console.error('replaceThread(null)')
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
const index = threads.value.findIndex(t => t.id === thread.id)
|
|
113
|
+
if (index !== -1) {
|
|
114
|
+
threads.value[index] = thread
|
|
115
|
+
}
|
|
116
|
+
if (currentThread.value?.id === thread.id) {
|
|
117
|
+
currentThread.value = thread
|
|
118
|
+
}
|
|
119
|
+
if (thread.completedAt || thread.error) {
|
|
120
|
+
threadDetails.value[thread.id] = thread
|
|
121
|
+
}
|
|
122
|
+
startWatchingThread()
|
|
123
|
+
return thread
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Update thread
|
|
127
|
+
async function updateThread(threadId, updates) {
|
|
128
|
+
|
|
129
|
+
if (!threadId)
|
|
130
|
+
throw new Error('threadId is required')
|
|
131
|
+
|
|
132
|
+
ctx.updateThreadFilters.forEach(f => f(updates))
|
|
133
|
+
|
|
134
|
+
const api = await ext.patchJson(`/threads/${threadId}`, updates)
|
|
135
|
+
if (api.response) {
|
|
136
|
+
return replaceThread(api.response)
|
|
137
|
+
} else {
|
|
138
|
+
setError(api.error, `Updating thread ${threadId}`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function deleteMessageFromThread(threadId, timestamp) {
|
|
143
|
+
const thread = await getThread(threadId)
|
|
144
|
+
if (!thread) throw new Error('Thread not found')
|
|
145
|
+
const updatedMessages = thread.messages.filter(m => m.timestamp !== timestamp)
|
|
146
|
+
console.log('deleteMessageFromThread', threadId, timestamp, updatedMessages)
|
|
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.timestamp === 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({ message: `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 request = { messages: updatedMessages }
|
|
192
|
+
const api = await queueChat({ request, thread })
|
|
193
|
+
if (api.response) {
|
|
194
|
+
replaceThread(api.response)
|
|
195
|
+
} else {
|
|
196
|
+
setError(api.error, `Redoing message ${timestamp} in thread ${threadId}`)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function loadThreads() {
|
|
201
|
+
isLoading.value = true
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const api = await ext.getJson('/threads?take=30')
|
|
205
|
+
threads.value = api.response || []
|
|
206
|
+
return threads.value
|
|
207
|
+
} finally {
|
|
208
|
+
isLoading.value = false
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function getThread(threadId) {
|
|
213
|
+
const cachedThread = threads.value.find(t => t.id == threadId)
|
|
214
|
+
if (cachedThread) return cachedThread
|
|
215
|
+
const api = await ext.getJson(`/threads?id=${threadId}`)
|
|
216
|
+
return api.response && api.response[0] || null
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Delete thread
|
|
220
|
+
async function deleteThread(threadId) {
|
|
221
|
+
await ext.delete(`/threads/${threadId}`)
|
|
222
|
+
|
|
223
|
+
threads.value = threads.value.filter(t => t.id !== threadId)
|
|
224
|
+
|
|
225
|
+
if (currentThread.value?.id === threadId) {
|
|
226
|
+
currentThread.value = null
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Set current thread
|
|
231
|
+
async function setCurrentThread(threadId) {
|
|
232
|
+
const thread = await getThread(threadId)
|
|
233
|
+
if (thread) {
|
|
234
|
+
currentThread.value = thread
|
|
235
|
+
startWatchingThread()
|
|
236
|
+
}
|
|
237
|
+
return thread
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Set current thread from router params (router-aware version)
|
|
241
|
+
async function setCurrentThreadFromRoute(threadId, router) {
|
|
242
|
+
if (!threadId) {
|
|
243
|
+
currentThread.value = null
|
|
244
|
+
return null
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
loadThreadDetails(threadId)
|
|
248
|
+
const thread = setCurrentThread(threadId)
|
|
249
|
+
if (thread) {
|
|
250
|
+
return thread
|
|
251
|
+
} else {
|
|
252
|
+
// Thread not found, redirect to home
|
|
253
|
+
if (router) {
|
|
254
|
+
router.push((globalThis.ai?.base || '') + '/')
|
|
255
|
+
}
|
|
256
|
+
currentThread.value = null
|
|
257
|
+
return null
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Clear current thread (go back to initial state)
|
|
262
|
+
function clearCurrentThread() {
|
|
263
|
+
currentThread.value = null
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function getGroupedThreads(total) {
|
|
267
|
+
const now = new Date()
|
|
268
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
|
269
|
+
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
|
|
270
|
+
const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
271
|
+
const lastMonth = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
|
|
272
|
+
|
|
273
|
+
const groups = {
|
|
274
|
+
today: [],
|
|
275
|
+
yesterday: [],
|
|
276
|
+
lastWeek: [],
|
|
277
|
+
lastMonth: [],
|
|
278
|
+
older: {}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const takeThreads = threads.value.slice(0, total)
|
|
282
|
+
|
|
283
|
+
takeThreads.forEach(thread => {
|
|
284
|
+
const threadDate = new Date(thread.updatedAt)
|
|
285
|
+
|
|
286
|
+
if (threadDate >= today) {
|
|
287
|
+
groups.today.push(thread)
|
|
288
|
+
} else if (threadDate >= yesterday) {
|
|
289
|
+
groups.yesterday.push(thread)
|
|
290
|
+
} else if (threadDate >= lastWeek) {
|
|
291
|
+
groups.lastWeek.push(thread)
|
|
292
|
+
} else if (threadDate >= lastMonth) {
|
|
293
|
+
groups.lastMonth.push(thread)
|
|
294
|
+
} else {
|
|
295
|
+
const year = threadDate.getFullYear()
|
|
296
|
+
const month = threadDate.toLocaleString('default', { month: 'long' })
|
|
297
|
+
const key = `${month} ${year}`
|
|
298
|
+
|
|
299
|
+
if (!groups.older[key]) {
|
|
300
|
+
groups.older[key] = []
|
|
301
|
+
}
|
|
302
|
+
groups.older[key].push(thread)
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
return groups
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Group threads by time periods
|
|
310
|
+
const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
|
|
311
|
+
|
|
312
|
+
function getLatestCachedThread() {
|
|
313
|
+
return threads.value[0]
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function startNewThread({ title, model, tools, redirect }) {
|
|
317
|
+
if (!model) {
|
|
318
|
+
console.error('No model selected')
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
if (!title) {
|
|
322
|
+
title = 'New Chat'
|
|
323
|
+
}
|
|
324
|
+
const latestThread = getLatestCachedThread()
|
|
325
|
+
|
|
326
|
+
console.log('startNewThread', title, model.name, ctx.router.currentRoute.value?.path, latestThread?.messages?.length)
|
|
327
|
+
ctx.setLayout({ left: 'ThreadsSidebar' })
|
|
328
|
+
|
|
329
|
+
if (latestThread && latestThread.title == title && !latestThread.messages?.length) {
|
|
330
|
+
if (ctx.router.currentRoute.value?.path != `/c/${latestThread.id}`) {
|
|
331
|
+
ctx.to(`/c/${latestThread.id}`)
|
|
332
|
+
}
|
|
333
|
+
return latestThread
|
|
334
|
+
}
|
|
335
|
+
const newThread = await createThread({
|
|
336
|
+
title,
|
|
337
|
+
model: model.name,
|
|
338
|
+
info: ctx.utils.toModelInfo(model),
|
|
339
|
+
tools,
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
console.log('newThread', newThread, model)
|
|
343
|
+
if (redirect) {
|
|
344
|
+
// Navigate to the new thread URL
|
|
345
|
+
ctx.to(`/c/${newThread.id}`)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Get the thread to check for duplicates
|
|
349
|
+
let thread = await getThread(newThread.id)
|
|
350
|
+
console.log('thread', thread)
|
|
351
|
+
return thread
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function queueChat(ctxRequest, options = {}) {
|
|
355
|
+
if (!ctxRequest.request) return ctx.createErrorResult({ message: 'No request provided' })
|
|
356
|
+
if (!ctxRequest.thread) return ctx.createErrorResult({ message: 'No thread provided' })
|
|
357
|
+
if (!ctxRequest.request.metadata) {
|
|
358
|
+
ctxRequest.request.metadata = {}
|
|
359
|
+
}
|
|
360
|
+
ctx.chatRequestFilters.forEach(f => f(ctxRequest))
|
|
361
|
+
const { thread, request } = ctxRequest
|
|
362
|
+
const api = await ctx.postJson(`/ext/app/threads/${thread.id}/chat`, {
|
|
363
|
+
...options,
|
|
364
|
+
body: typeof request == 'string'
|
|
365
|
+
? request
|
|
366
|
+
: JSON.stringify(request),
|
|
367
|
+
})
|
|
368
|
+
return api
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function loadThreadDetails(id, opt = null) {
|
|
372
|
+
if (!threadDetails.value[id] || opt?.force) {
|
|
373
|
+
const api = await ctx.getJson(`/ext/app/threads/${id}`)
|
|
374
|
+
if (api.response) {
|
|
375
|
+
threadDetails.value[id] = api.response
|
|
376
|
+
}
|
|
377
|
+
if (api.error) {
|
|
378
|
+
console.error(api.error)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return threadDetails.value[id]
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Export the store
|
|
385
|
+
export function useThreadStore() {
|
|
386
|
+
return {
|
|
387
|
+
// State
|
|
388
|
+
threads,
|
|
389
|
+
currentThread,
|
|
390
|
+
isLoading,
|
|
391
|
+
groupedThreads,
|
|
392
|
+
|
|
393
|
+
// Actions
|
|
394
|
+
query,
|
|
395
|
+
createThread,
|
|
396
|
+
updateThread,
|
|
397
|
+
deleteMessageFromThread,
|
|
398
|
+
updateMessageInThread,
|
|
399
|
+
redoMessageFromThread,
|
|
400
|
+
loadThreads,
|
|
401
|
+
getThread,
|
|
402
|
+
deleteThread,
|
|
403
|
+
setCurrentThread,
|
|
404
|
+
setCurrentThreadFromRoute,
|
|
405
|
+
clearCurrentThread,
|
|
406
|
+
getGroupedThreads,
|
|
407
|
+
getLatestCachedThread,
|
|
408
|
+
startNewThread,
|
|
409
|
+
replaceThread,
|
|
410
|
+
queueChat,
|
|
411
|
+
threadDetails,
|
|
412
|
+
loadThreadDetails,
|
|
413
|
+
isWatchingThread,
|
|
414
|
+
startWatchingThread,
|
|
415
|
+
stopWatchingThread,
|
|
416
|
+
cancelThread,
|
|
417
|
+
get watchingThread() {
|
|
418
|
+
return isWatchingThread.value
|
|
419
|
+
},
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export default {
|
|
424
|
+
install(context) {
|
|
425
|
+
ctx = context
|
|
426
|
+
ext = ctx.scope('app')
|
|
427
|
+
ctx.setGlobals({ threads: useThreadStore() })
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
async load() {
|
|
431
|
+
await ctx.threads.loadThreads()
|
|
432
|
+
}
|
|
433
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Calculator
|
|
2
|
+
|
|
3
|
+
A powerful and safe mathematical expression evaluator with a rich web interface.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### 🖥️ UX Friendly Interface
|
|
8
|
+
Experience a clean, modern interface designed for efficiency. The UI is fully responsive and supports dark mode, seamlessly integrating with the rest of the application.
|
|
9
|
+
|
|
10
|
+
### 💾 Persistent History
|
|
11
|
+
Never lose track of your calculations. The Calculator automatically saves your history to `localStorage`, ensuring your previous expressions and results are preserved between sessions.
|
|
12
|
+
|
|
13
|
+
### ⚡ 1-Click Interaction
|
|
14
|
+
Streamline your workflow with interactive history items:
|
|
15
|
+
- **Load & Copy**: Click on any past expression or answer to instantly load it into the input field and copy it to your clipboard.
|
|
16
|
+
- **Visual Feedback**: Temporary checkmarks confirm successful copy actions.
|
|
17
|
+
|
|
18
|
+
### ⌨️ Keyboard-Free Access
|
|
19
|
+
While full keyboard support is available, you can perform complex calculations entirely via the UI:
|
|
20
|
+
- **Numbers & Constants**: Quick access to digits and mathematical constants like `pi`, `e`, `inf`.
|
|
21
|
+
- **Operators**: Comprehensive set of buttons for arithmetic (`+`, `-`, `*`, `/`, `%`, `^`) and boolean logic (`and`, `or`, `not`).
|
|
22
|
+
- **Functions**: One-click insertion or wrapping of selection for all supported math functions.
|
|
23
|
+
|
|
24
|
+
### 🐍 Python Math Support
|
|
25
|
+
Unlock the power of Python's math library directly in the browser.
|
|
26
|
+
- **Math Functions**: Support for `sin`, `cos`, `tan`, `sqrt`, `log`, `factorial`, and many more.
|
|
27
|
+
- **Statistics**: Built-in functions for `mean`, `median`, `stdev`, and `variance`.
|
|
28
|
+
|
|
29
|
+
### 🛡️ Safe Evaluation
|
|
30
|
+
Security is a priority. Instead of using Python's unsafe `eval()`, the Calculator uses a robust **AST (Abstract Syntax Tree) evaluator**.
|
|
31
|
+
- **Restricted Environment**: Only allowed mathematical operations and functions are executed.
|
|
32
|
+
- **No Side Effects**: Prevents arbitrary code execution, making it safe to evaluate expressions from untrusted sources.
|