llms-py 3.0.0__py3-none-any.whl → 3.0.0b2__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/__pycache__/main.cpython-314.pyc +0 -0
- llms/index.html +37 -26
- llms/llms.json +21 -70
- llms/main.py +731 -1426
- llms/providers.json +1 -1
- llms/{extensions/analytics/ui/index.mjs → ui/Analytics.mjs} +238 -154
- llms/ui/App.mjs +63 -133
- llms/ui/Avatar.mjs +86 -0
- llms/ui/Brand.mjs +52 -0
- llms/ui/ChatPrompt.mjs +597 -0
- llms/ui/Main.mjs +862 -0
- llms/ui/OAuthSignIn.mjs +61 -0
- llms/ui/ProviderIcon.mjs +36 -0
- llms/ui/ProviderStatus.mjs +104 -0
- llms/{extensions/app/ui → ui}/Recents.mjs +57 -82
- llms/ui/{modules/chat/SettingsDialog.mjs → SettingsDialog.mjs} +9 -9
- llms/{extensions/app/ui/index.mjs → ui/Sidebar.mjs} +57 -122
- llms/ui/SignIn.mjs +65 -0
- llms/ui/Welcome.mjs +8 -0
- llms/ui/ai.mjs +13 -117
- llms/ui/app.css +49 -1776
- llms/ui/index.mjs +171 -87
- llms/ui/lib/charts.mjs +13 -9
- llms/ui/lib/servicestack-vue.mjs +3 -3
- llms/ui/lib/vue.min.mjs +9 -10
- llms/ui/lib/vue.mjs +1602 -1763
- llms/ui/markdown.mjs +2 -10
- llms/ui/model-selector.mjs +686 -0
- llms/ui/tailwind.input.css +1 -55
- llms/ui/threadStore.mjs +583 -0
- llms/ui/utils.mjs +118 -113
- llms/ui.json +1069 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/METADATA +1 -1
- llms_py-3.0.0b2.dist-info/RECORD +58 -0
- llms/extensions/app/README.md +0 -20
- llms/extensions/app/__init__.py +0 -530
- llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
- llms/extensions/app/db.py +0 -644
- llms/extensions/app/db_manager.py +0 -195
- llms/extensions/app/requests.json +0 -9073
- llms/extensions/app/threads.json +0 -15290
- llms/extensions/app/ui/threadStore.mjs +0 -411
- llms/extensions/core_tools/CALCULATOR.md +0 -32
- llms/extensions/core_tools/__init__.py +0 -598
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -201
- llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -185
- llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -101
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -160
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -66
- llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -27
- llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -72
- llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -119
- llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -98
- llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -225
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
- llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -942
- llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -118
- llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -962
- llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -62
- llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -402
- llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -40
- llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -135
- llms/extensions/core_tools/ui/index.mjs +0 -650
- llms/extensions/gallery/README.md +0 -61
- llms/extensions/gallery/__init__.py +0 -61
- llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/gallery/db.py +0 -298
- llms/extensions/gallery/ui/index.mjs +0 -482
- llms/extensions/katex/README.md +0 -39
- llms/extensions/katex/__init__.py +0 -6
- llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/katex/ui/README.md +0 -125
- llms/extensions/katex/ui/contrib/auto-render.js +0 -338
- llms/extensions/katex/ui/contrib/auto-render.min.js +0 -1
- llms/extensions/katex/ui/contrib/auto-render.mjs +0 -244
- llms/extensions/katex/ui/contrib/copy-tex.js +0 -127
- llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -1
- llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -105
- llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -109
- llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -1
- llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -24
- llms/extensions/katex/ui/contrib/mhchem.js +0 -3213
- llms/extensions/katex/ui/contrib/mhchem.min.js +0 -1
- llms/extensions/katex/ui/contrib/mhchem.mjs +0 -3109
- llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -887
- llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -1
- llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -800
- 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 +0 -92
- llms/extensions/katex/ui/katex-swap.css +0 -1230
- llms/extensions/katex/ui/katex-swap.min.css +0 -1
- llms/extensions/katex/ui/katex.css +0 -1230
- llms/extensions/katex/ui/katex.js +0 -19080
- llms/extensions/katex/ui/katex.min.css +0 -1
- llms/extensions/katex/ui/katex.min.js +0 -1
- llms/extensions/katex/ui/katex.min.mjs +0 -1
- llms/extensions/katex/ui/katex.mjs +0 -18547
- llms/extensions/providers/__init__.py +0 -18
- llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
- llms/extensions/providers/anthropic.py +0 -229
- llms/extensions/providers/chutes.py +0 -155
- llms/extensions/providers/google.py +0 -378
- llms/extensions/providers/nvidia.py +0 -105
- llms/extensions/providers/openai.py +0 -156
- llms/extensions/providers/openrouter.py +0 -72
- llms/extensions/system_prompts/README.md +0 -22
- llms/extensions/system_prompts/__init__.py +0 -45
- llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/system_prompts/ui/index.mjs +0 -280
- llms/extensions/system_prompts/ui/prompts.json +0 -1067
- llms/extensions/tools/__init__.py +0 -5
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/ui/index.mjs +0 -204
- llms/providers-extra.json +0 -356
- llms/ui/ctx.mjs +0 -365
- llms/ui/modules/chat/ChatBody.mjs +0 -691
- llms/ui/modules/chat/index.mjs +0 -828
- llms/ui/modules/layout.mjs +0 -243
- llms/ui/modules/model-selector.mjs +0 -851
- llms_py-3.0.0.dist-info/RECORD +0 -202
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/WHEEL +0 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { onMounted, inject } from 'vue'
|
|
2
2
|
import { useRouter } from 'vue-router'
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import { useFormatters } from '@servicestack/vue'
|
|
4
|
+
import { useThreadStore } from './threadStore.mjs'
|
|
5
|
+
import Brand from './Brand.mjs'
|
|
6
|
+
import { statsTitle, formatCost } from './utils.mjs'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
const { humanifyNumber, humanifyMs } = useFormatters()
|
|
8
9
|
|
|
9
10
|
// Thread Item Component
|
|
10
11
|
const ThreadItem = {
|
|
@@ -20,10 +21,10 @@ const ThreadItem = {
|
|
|
20
21
|
{{ thread.title }}
|
|
21
22
|
</div>
|
|
22
23
|
<div class="text-xs text-gray-500 dark:text-gray-400 truncate">
|
|
23
|
-
<span>{{
|
|
24
|
-
<span v-if="thread.stats?.inputTokens" :title="
|
|
25
|
-
• {{
|
|
26
|
-
{{ thread.stats.cost ? ' ' +
|
|
24
|
+
<span>{{ formatRelativeTime(thread.updatedAt) }} • {{ thread.messages.length }} msgs</span>
|
|
25
|
+
<span v-if="thread.stats?.inputTokens" :title="statsTitle(thread.stats)">
|
|
26
|
+
• {{ humanifyNumber(thread.stats.inputTokens + thread.stats.outputTokens) }} toks
|
|
27
|
+
{{ thread.stats.cost ? ' ' + formatCost(thread.stats.cost) : '' }}
|
|
27
28
|
</span>
|
|
28
29
|
</div>
|
|
29
30
|
<div v-if="thread.model" class="text-xs text-blue-600 dark:text-blue-400 truncate">
|
|
@@ -59,12 +60,32 @@ const ThreadItem = {
|
|
|
59
60
|
emits: ['select', 'delete'],
|
|
60
61
|
|
|
61
62
|
setup() {
|
|
63
|
+
const formatRelativeTime = (timestamp) => {
|
|
64
|
+
const now = new Date()
|
|
65
|
+
const date = new Date(timestamp)
|
|
66
|
+
const diffInSeconds = Math.floor((now - date) / 1000)
|
|
67
|
+
|
|
68
|
+
if (diffInSeconds < 60) return 'Just now'
|
|
69
|
+
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`
|
|
70
|
+
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`
|
|
71
|
+
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`
|
|
72
|
+
|
|
73
|
+
return date.toLocaleDateString()
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
return {
|
|
77
|
+
formatRelativeTime,
|
|
78
|
+
humanifyNumber,
|
|
79
|
+
statsTitle,
|
|
80
|
+
formatCost,
|
|
63
81
|
}
|
|
64
82
|
}
|
|
65
83
|
}
|
|
66
84
|
|
|
67
85
|
const GroupedThreads = {
|
|
86
|
+
components: {
|
|
87
|
+
ThreadItem,
|
|
88
|
+
},
|
|
68
89
|
template: `
|
|
69
90
|
<!-- Today -->
|
|
70
91
|
<div v-if="groupedThreads.today.length > 0" class="mb-4">
|
|
@@ -73,20 +94,7 @@ const GroupedThreads = {
|
|
|
73
94
|
v-for="thread in groupedThreads.today"
|
|
74
95
|
:key="thread.id"
|
|
75
96
|
:thread="thread"
|
|
76
|
-
:is-active="currentThread?.id
|
|
77
|
-
@select="$emit('select', $event)"
|
|
78
|
-
@delete="$emit('delete', $event)"
|
|
79
|
-
/>
|
|
80
|
-
</div>
|
|
81
|
-
|
|
82
|
-
<!-- Yesterday -->
|
|
83
|
-
<div v-if="groupedThreads.yesterday.length > 0" class="mb-4">
|
|
84
|
-
<h3 class="px-4 py-2 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider select-none">Yesterday</h3>
|
|
85
|
-
<ThreadItem
|
|
86
|
-
v-for="thread in groupedThreads.yesterday"
|
|
87
|
-
:key="thread.id"
|
|
88
|
-
:thread="thread"
|
|
89
|
-
:is-active="currentThread?.id == thread.id"
|
|
97
|
+
:is-active="currentThread?.id === thread.id"
|
|
90
98
|
@select="$emit('select', $event)"
|
|
91
99
|
@delete="$emit('delete', $event)"
|
|
92
100
|
/>
|
|
@@ -99,7 +107,7 @@ const GroupedThreads = {
|
|
|
99
107
|
v-for="thread in groupedThreads.lastWeek"
|
|
100
108
|
:key="thread.id"
|
|
101
109
|
:thread="thread"
|
|
102
|
-
:is-active="currentThread?.id
|
|
110
|
+
:is-active="currentThread?.id === thread.id"
|
|
103
111
|
@select="$emit('select', $event)"
|
|
104
112
|
@delete="$emit('delete', $event)"
|
|
105
113
|
/>
|
|
@@ -112,7 +120,7 @@ const GroupedThreads = {
|
|
|
112
120
|
v-for="thread in groupedThreads.lastMonth"
|
|
113
121
|
:key="thread.id"
|
|
114
122
|
:thread="thread"
|
|
115
|
-
:is-active="currentThread?.id
|
|
123
|
+
:is-active="currentThread?.id === thread.id"
|
|
116
124
|
@select="$emit('select', $event)"
|
|
117
125
|
@delete="$emit('delete', $event)"
|
|
118
126
|
/>
|
|
@@ -125,7 +133,7 @@ const GroupedThreads = {
|
|
|
125
133
|
v-for="thread in monthThreads"
|
|
126
134
|
:key="thread.id"
|
|
127
135
|
:thread="thread"
|
|
128
|
-
:is-active="currentThread?.id
|
|
136
|
+
:is-active="currentThread?.id === thread.id"
|
|
129
137
|
@select="$emit('select', $event)"
|
|
130
138
|
@delete="$emit('delete', $event)"
|
|
131
139
|
/>
|
|
@@ -148,10 +156,15 @@ const GroupedThreads = {
|
|
|
148
156
|
emits: ['select', 'delete'],
|
|
149
157
|
}
|
|
150
158
|
|
|
151
|
-
const
|
|
159
|
+
const Sidebar = {
|
|
160
|
+
components: {
|
|
161
|
+
Brand,
|
|
162
|
+
GroupedThreads,
|
|
163
|
+
ThreadItem,
|
|
164
|
+
},
|
|
152
165
|
template: `
|
|
153
|
-
<div class="flex flex-col h-full">
|
|
154
|
-
<Brand />
|
|
166
|
+
<div class="flex flex-col h-full bg-gray-50 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700">
|
|
167
|
+
<Brand @home="goToInitialState" @new="createNewThread" @analytics="goToAnalytics" @toggle-sidebar="$emit('toggle-sidebar')" />
|
|
155
168
|
<!-- Thread List -->
|
|
156
169
|
<div class="flex-1 overflow-y-auto">
|
|
157
170
|
<div v-if="isLoading" class="p-4 text-center text-gray-500 dark:text-gray-400">
|
|
@@ -167,28 +180,19 @@ const ThreadsSidebar = {
|
|
|
167
180
|
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">Start a new chat to begin</p>
|
|
168
181
|
</div>
|
|
169
182
|
|
|
170
|
-
<div v-else class="
|
|
171
|
-
|
|
172
|
-
<div class="flex items-center space-x-2 absolute top-2 right-2">
|
|
173
|
-
<button type="button"
|
|
174
|
-
@click="createNewThread"
|
|
175
|
-
class="text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
|
|
176
|
-
title="New Chat"
|
|
177
|
-
>
|
|
178
|
-
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></g></svg>
|
|
179
|
-
</button>
|
|
180
|
-
</div>
|
|
181
|
-
|
|
182
|
-
<GroupedThreads :currentThread="currentThread" :groupedThreads="$threads.getGroupedThreads(50)"
|
|
183
|
+
<div v-else class="py-2">
|
|
184
|
+
<GroupedThreads :currentThread="currentThread" :groupedThreads="threadStore.getGroupedThreads(18)"
|
|
183
185
|
@select="selectThread" @delete="deleteThread" />
|
|
184
186
|
</div>
|
|
185
187
|
</div>
|
|
186
188
|
</div>
|
|
187
189
|
`,
|
|
188
|
-
|
|
190
|
+
emits: ['thread-selected', 'toggle-sidebar'],
|
|
191
|
+
setup(props, { emit }) {
|
|
189
192
|
const ctx = inject('ctx')
|
|
190
193
|
const ai = ctx.ai
|
|
191
194
|
const router = useRouter()
|
|
195
|
+
const threadStore = useThreadStore()
|
|
192
196
|
const {
|
|
193
197
|
threads,
|
|
194
198
|
currentThread,
|
|
@@ -198,7 +202,7 @@ const ThreadsSidebar = {
|
|
|
198
202
|
createThread,
|
|
199
203
|
deleteThread: deleteThreadFromStore,
|
|
200
204
|
clearCurrentThread
|
|
201
|
-
} =
|
|
205
|
+
} = threadStore
|
|
202
206
|
|
|
203
207
|
onMounted(async () => {
|
|
204
208
|
await loadThreads()
|
|
@@ -206,6 +210,7 @@ const ThreadsSidebar = {
|
|
|
206
210
|
|
|
207
211
|
const selectThread = async (threadId) => {
|
|
208
212
|
router.push(`${ai.base}/c/${threadId}`)
|
|
213
|
+
emit('thread-selected')
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
const deleteThread = async (threadId) => {
|
|
@@ -219,19 +224,24 @@ const ThreadsSidebar = {
|
|
|
219
224
|
}
|
|
220
225
|
|
|
221
226
|
const createNewThread = async () => {
|
|
222
|
-
|
|
227
|
+
const newThread = await createThread()
|
|
228
|
+
router.push(`${ai.base}/c/${newThread.id}`)
|
|
229
|
+
emit('thread-selected')
|
|
223
230
|
}
|
|
224
231
|
|
|
225
232
|
const goToInitialState = () => {
|
|
226
233
|
clearCurrentThread()
|
|
227
|
-
|
|
234
|
+
router.push(`${ai.base}/`)
|
|
235
|
+
emit('thread-selected')
|
|
228
236
|
}
|
|
229
237
|
|
|
230
238
|
const goToAnalytics = () => {
|
|
231
|
-
|
|
239
|
+
router.push(`${ai.base}/analytics`)
|
|
240
|
+
emit('thread-selected')
|
|
232
241
|
}
|
|
233
242
|
|
|
234
243
|
return {
|
|
244
|
+
threadStore,
|
|
235
245
|
threads,
|
|
236
246
|
currentThread,
|
|
237
247
|
isLoading,
|
|
@@ -245,79 +255,4 @@ const ThreadsSidebar = {
|
|
|
245
255
|
}
|
|
246
256
|
}
|
|
247
257
|
|
|
248
|
-
|
|
249
|
-
async function query(query) {
|
|
250
|
-
return (await ext.getJson(appendQueryString(`/requests`, query))).response || []
|
|
251
|
-
}
|
|
252
|
-
async function deleteById(requestId) {
|
|
253
|
-
if (!requestId) {
|
|
254
|
-
throw new Error('Request ID is required')
|
|
255
|
-
}
|
|
256
|
-
return await ext.deleteJson(`/requests/${requestId}`)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
async function getThreadIds(query) {
|
|
260
|
-
return (await ext.getJson(appendQueryString(`/requests?fields=threadId¬_null=threadId&as=column&take=10000`, query))).response || []
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async function getSummary() {
|
|
264
|
-
return (await ext.getJson(`/requests/summary`)).response
|
|
265
|
-
}
|
|
266
|
-
async function getDailySummary(day) {
|
|
267
|
-
return (await ext.getJson(`/requests/summary/${day}`)).response
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Get unique values for filter options
|
|
271
|
-
async function getFilterOptions() {
|
|
272
|
-
const results = await query({
|
|
273
|
-
select: 'distinct',
|
|
274
|
-
fields: 'model,provider',
|
|
275
|
-
not_null: 'model,provider',
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
if (results) {
|
|
279
|
-
const models = [...new Set(results.map(r => r.model).filter(m => m))].sort()
|
|
280
|
-
const providers = [...new Set(results.map(r => r.provider).filter(p => p))].sort()
|
|
281
|
-
console.log('getFilterOptions', models, providers)
|
|
282
|
-
return {
|
|
283
|
-
models,
|
|
284
|
-
providers
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return {
|
|
290
|
-
query,
|
|
291
|
-
deleteById,
|
|
292
|
-
getThreadIds,
|
|
293
|
-
getSummary,
|
|
294
|
-
getDailySummary,
|
|
295
|
-
getFilterOptions,
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export default {
|
|
300
|
-
order: -100,
|
|
301
|
-
|
|
302
|
-
install(ctx) {
|
|
303
|
-
ext = ctx.scope('app')
|
|
304
|
-
ctx.components({
|
|
305
|
-
ThreadsSidebar,
|
|
306
|
-
ThreadItem,
|
|
307
|
-
GroupedThreads,
|
|
308
|
-
Recents,
|
|
309
|
-
})
|
|
310
|
-
ctx.routes.push(...[
|
|
311
|
-
{ path: '/recents', component: Recents },
|
|
312
|
-
])
|
|
313
|
-
ThreadStore.install(ctx)
|
|
314
|
-
|
|
315
|
-
ctx.setGlobals({
|
|
316
|
-
requests: useRequests(ext)
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
ctx.setLayout({
|
|
320
|
-
left: 'ThreadsSidebar',
|
|
321
|
-
})
|
|
322
|
-
}
|
|
323
|
-
}
|
|
258
|
+
export default Sidebar
|
llms/ui/SignIn.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { inject, ref } from "vue"
|
|
2
|
+
import { toJsonObject } from "./utils.mjs"
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
template: `
|
|
6
|
+
<div class="min-h-full -mt-12 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
|
7
|
+
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
|
8
|
+
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-gray-50">
|
|
9
|
+
Sign In
|
|
10
|
+
</h2>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
|
13
|
+
<ErrorSummary v-if="errorSummary" class="mb-3" :status="errorSummary" />
|
|
14
|
+
<div class="bg-white dark:bg-black py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
|
15
|
+
<form @submit.prevent="submit">
|
|
16
|
+
<div class="flex flex-1 flex-col justify-between">
|
|
17
|
+
<div class="space-y-6">
|
|
18
|
+
<fieldset class="grid grid-cols-12 gap-6">
|
|
19
|
+
<div class="w-full col-span-12">
|
|
20
|
+
<TextInput id="apiKey" name="apiKey" label="API Key" v-model="apiKey" />
|
|
21
|
+
</div>
|
|
22
|
+
</fieldset>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="mt-8">
|
|
26
|
+
<PrimaryButton class="w-full">Sign In</PrimaryButton>
|
|
27
|
+
</div>
|
|
28
|
+
</form>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
`,
|
|
33
|
+
emits: ['done'],
|
|
34
|
+
setup(props, { emit }) {
|
|
35
|
+
const ctx = inject('ctx')
|
|
36
|
+
const ai = ctx.ai
|
|
37
|
+
const apiKey = ref('')
|
|
38
|
+
const errorSummary = ref()
|
|
39
|
+
async function submit() {
|
|
40
|
+
const r = await ai.get('/auth', {
|
|
41
|
+
headers: {
|
|
42
|
+
'Authorization': `Bearer ${apiKey.value}`
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
const txt = await r.text()
|
|
46
|
+
const json = toJsonObject(txt)
|
|
47
|
+
// console.log('json', json)
|
|
48
|
+
if (r.ok) {
|
|
49
|
+
json.apiKey = apiKey.value
|
|
50
|
+
emit('done', json)
|
|
51
|
+
} else {
|
|
52
|
+
errorSummary.value = json.responseStatus || {
|
|
53
|
+
errorCode: "Unauthorized",
|
|
54
|
+
message: 'Invalid API Key'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
apiKey,
|
|
61
|
+
submit,
|
|
62
|
+
errorSummary,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
llms/ui/Welcome.mjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
template: `
|
|
3
|
+
<div class="mb-2 flex justify-center">
|
|
4
|
+
<svg class="size-20 text-gray-700 dark:text-gray-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M8 2.19c3.13 0 5.68 2.25 5.68 5s-2.55 5-5.68 5a5.7 5.7 0 0 1-1.89-.29l-.75-.26l-.56.56a14 14 0 0 1-2 1.55a.13.13 0 0 1-.07 0v-.06a6.58 6.58 0 0 0 .15-4.29a5.25 5.25 0 0 1-.55-2.16c0-2.77 2.55-5 5.68-5M8 .94c-3.83 0-6.93 2.81-6.93 6.27a6.4 6.4 0 0 0 .64 2.64a5.53 5.53 0 0 1-.18 3.48a1.32 1.32 0 0 0 2 1.5a15 15 0 0 0 2.16-1.71a6.8 6.8 0 0 0 2.31.36c3.83 0 6.93-2.81 6.93-6.27S11.83.94 8 .94"/><ellipse cx="5.2" cy="7.7" fill="currentColor" rx=".8" ry=".75"/><ellipse cx="8" cy="7.7" fill="currentColor" rx=".8" ry=".75"/><ellipse cx="10.8" cy="7.7" fill="currentColor" rx=".8" ry=".75"/></svg>
|
|
5
|
+
</div>
|
|
6
|
+
<h2 class="text-2xl font-semibold text-gray-900 dark:text-gray-100 mb-2">{{ $ai.welcome }}</h2>
|
|
7
|
+
`
|
|
8
|
+
}
|
llms/ui/ai.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { reactive } from "vue"
|
|
2
|
-
import {
|
|
2
|
+
import { useThreadStore } from "./threadStore.mjs"
|
|
3
3
|
|
|
4
4
|
const base = ''
|
|
5
5
|
const headers = { 'Accept': 'application/json' }
|
|
6
6
|
const prefsKey = 'llms.prefs'
|
|
7
7
|
|
|
8
8
|
export const o = {
|
|
9
|
-
version: '3.0.
|
|
9
|
+
version: '3.0.0b2',
|
|
10
10
|
base,
|
|
11
11
|
prefsKey,
|
|
12
12
|
welcome: 'Welcome to llms.py',
|
|
@@ -15,7 +15,6 @@ export const o = {
|
|
|
15
15
|
authType: 'apikey', // 'oauth' or 'apikey' - controls which SignIn component to use
|
|
16
16
|
headers,
|
|
17
17
|
isSidebarOpen: true, // Shared sidebar state (default open for lg+ screens)
|
|
18
|
-
cacheUrlInfo: {},
|
|
19
18
|
|
|
20
19
|
get hasAccess() {
|
|
21
20
|
return !this.requiresAuth || this.auth
|
|
@@ -30,73 +29,14 @@ export const o = {
|
|
|
30
29
|
headers: Object.assign({}, this.headers, options?.headers),
|
|
31
30
|
})
|
|
32
31
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return await this.createJsonResult(res, url)
|
|
36
|
-
},
|
|
37
|
-
async post(url, options) {
|
|
38
|
-
return await fetch(this.resolveUrl(url), {
|
|
32
|
+
post(url, options) {
|
|
33
|
+
return fetch(this.resolveUrl(url), {
|
|
39
34
|
method: 'POST',
|
|
40
35
|
...options,
|
|
41
36
|
headers: Object.assign({ 'Content-Type': 'application/json' }, this.headers, options?.headers),
|
|
42
37
|
})
|
|
43
38
|
},
|
|
44
|
-
|
|
45
|
-
return await fetch(this.resolveUrl(url), {
|
|
46
|
-
method: 'POST',
|
|
47
|
-
...options,
|
|
48
|
-
headers: Object.assign({}, options?.headers),
|
|
49
|
-
})
|
|
50
|
-
},
|
|
51
|
-
async postJson(url, options) {
|
|
52
|
-
const res = await this.post(url, options)
|
|
53
|
-
return await this.createJsonResult(res, url)
|
|
54
|
-
},
|
|
55
|
-
async createJsonResult(res, msg = null) {
|
|
56
|
-
let txt = ''
|
|
57
|
-
try {
|
|
58
|
-
txt = await res.text()
|
|
59
|
-
const response = JSON.parse(txt)
|
|
60
|
-
if (response?.responseStatus?.errorCode) {
|
|
61
|
-
return new ApiResult({ error: response.responseStatus })
|
|
62
|
-
}
|
|
63
|
-
if (!res.ok) {
|
|
64
|
-
return new ApiResult({ error: { errorCode: 'Error', message: res.statusText } })
|
|
65
|
-
}
|
|
66
|
-
return new ApiResult({ response })
|
|
67
|
-
} catch (e) {
|
|
68
|
-
console.error('Failed to parse JSON', e, msg, txt)
|
|
69
|
-
const responseStatus = {
|
|
70
|
-
errorCode: 'Error',
|
|
71
|
-
message: `${e.message ?? e}`,
|
|
72
|
-
stackTrace: msg ? `${msg}\n${txt}` : txt,
|
|
73
|
-
}
|
|
74
|
-
return { responseStatus }
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
createErrorStatus({ message, errorCode, stackTrace, errors, meta }) {
|
|
78
|
-
const ret = {
|
|
79
|
-
errorCode: errorCode || 'Error',
|
|
80
|
-
message: message,
|
|
81
|
-
}
|
|
82
|
-
if (stackTrace) {
|
|
83
|
-
ret.stackTrace = stackTrace
|
|
84
|
-
}
|
|
85
|
-
if (errors && Array.isArray(errors)) {
|
|
86
|
-
ret.errors = errors
|
|
87
|
-
}
|
|
88
|
-
if (meta) {
|
|
89
|
-
ret.meta = meta
|
|
90
|
-
}
|
|
91
|
-
return ret
|
|
92
|
-
},
|
|
93
|
-
createErrorResult(e) {
|
|
94
|
-
return new ApiResult({
|
|
95
|
-
error: e.errorCode
|
|
96
|
-
? this.createErrorStatus(e)
|
|
97
|
-
: this.createErrorStatus({ message: `${e.message ?? e}` })
|
|
98
|
-
})
|
|
99
|
-
},
|
|
39
|
+
|
|
100
40
|
async getConfig() {
|
|
101
41
|
return this.get('/config')
|
|
102
42
|
},
|
|
@@ -132,17 +72,18 @@ export const o = {
|
|
|
132
72
|
if (this.headers.Authorization) {
|
|
133
73
|
delete this.headers.Authorization
|
|
134
74
|
}
|
|
75
|
+
localStorage.removeItem('llms:auth')
|
|
135
76
|
},
|
|
136
|
-
async init(
|
|
77
|
+
async init() {
|
|
137
78
|
// Load models and prompts
|
|
138
|
-
const
|
|
79
|
+
const { initDB } = useThreadStore()
|
|
80
|
+
const [_, configRes, modelsRes] = await Promise.all([
|
|
81
|
+
initDB(),
|
|
139
82
|
this.getConfig(),
|
|
140
83
|
this.getModels(),
|
|
141
|
-
this.get('/ext'),
|
|
142
84
|
])
|
|
143
85
|
const config = await configRes.json()
|
|
144
86
|
const models = await modelsRes.json()
|
|
145
|
-
const extensions = await extensionsRes.json()
|
|
146
87
|
|
|
147
88
|
// Update auth settings from server config
|
|
148
89
|
if (config.requiresAuth != null) {
|
|
@@ -159,59 +100,14 @@ export const o = {
|
|
|
159
100
|
: null
|
|
160
101
|
if (auth?.responseStatus?.errorCode) {
|
|
161
102
|
console.error(auth.responseStatus.errorCode, auth.responseStatus.message)
|
|
103
|
+
// Clear invalid session from localStorage
|
|
104
|
+
localStorage.removeItem('llms:auth')
|
|
162
105
|
} else {
|
|
163
106
|
this.signIn(auth)
|
|
164
107
|
}
|
|
165
|
-
return { config, models,
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
async uploadFile(file) {
|
|
170
|
-
const formData = new FormData()
|
|
171
|
-
formData.append('file', file)
|
|
172
|
-
const response = await fetch(this.resolveUrl('/upload'), {
|
|
173
|
-
method: 'POST',
|
|
174
|
-
body: formData
|
|
175
|
-
})
|
|
176
|
-
if (!response.ok) {
|
|
177
|
-
throw new Error(`Upload failed: ${response.statusText}`)
|
|
178
|
-
}
|
|
179
|
-
return response.json()
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
getCacheInfo(url) {
|
|
184
|
-
return this.cacheUrlInfo[url]
|
|
185
|
-
},
|
|
186
|
-
async fetchCacheInfos(urls) {
|
|
187
|
-
const infos = {}
|
|
188
|
-
const fetchInfos = []
|
|
189
|
-
for (const url of urls) {
|
|
190
|
-
const info = this.getCacheInfo(url)
|
|
191
|
-
if (info) {
|
|
192
|
-
infos[url] = info
|
|
193
|
-
} else {
|
|
194
|
-
fetchInfos.push(fetch(this.resolveUrl(url + "?info")))
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
const responses = await Promise.all(fetchInfos)
|
|
198
|
-
for (let i = 0; i < urls.length; i++) {
|
|
199
|
-
try {
|
|
200
|
-
const info = await responses[i].json()
|
|
201
|
-
this.setCacheInfo(urls[i], info)
|
|
202
|
-
infos[urls[i]] = info
|
|
203
|
-
} catch (e) {
|
|
204
|
-
console.error('Failed to fetch info for', urls[i], e)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return infos
|
|
208
|
-
},
|
|
209
|
-
setCacheInfo(url, info) {
|
|
210
|
-
this.cacheUrlInfo[url] = info
|
|
108
|
+
return { config, models, auth }
|
|
211
109
|
}
|
|
212
|
-
|
|
213
110
|
}
|
|
214
111
|
|
|
215
|
-
|
|
216
112
|
let ai = reactive(o)
|
|
217
113
|
export default ai
|