llms-py 2.0.35__py3-none-any.whl → 3.0.0__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__/__init__.cpython-312.pyc +0 -0
- llms/__pycache__/__init__.cpython-313.pyc +0 -0
- llms/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/__pycache__/__main__.cpython-312.pyc +0 -0
- llms/__pycache__/__main__.cpython-314.pyc +0 -0
- llms/__pycache__/llms.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-313.pyc +0 -0
- llms/__pycache__/main.cpython-314.pyc +0 -0
- llms/__pycache__/plugins.cpython-314.pyc +0 -0
- llms/{ui/Analytics.mjs → extensions/analytics/ui/index.mjs} +154 -238
- llms/extensions/app/README.md +20 -0
- llms/extensions/app/__init__.py +530 -0
- 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 +644 -0
- llms/extensions/app/db_manager.py +195 -0
- llms/extensions/app/requests.json +9073 -0
- llms/extensions/app/threads.json +15290 -0
- llms/{ui → extensions/app/ui}/Recents.mjs +91 -65
- llms/{ui/Sidebar.mjs → extensions/app/ui/index.mjs} +124 -58
- llms/extensions/app/ui/threadStore.mjs +411 -0
- llms/extensions/core_tools/CALCULATOR.md +32 -0
- llms/extensions/core_tools/__init__.py +598 -0
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -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/doc/docs.css +225 -0
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +344 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +9884 -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 +61 -0
- 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 +298 -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/__pycache__/__init__.cpython-314.pyc +0 -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 +18 -0
- 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 +229 -0
- llms/extensions/providers/chutes.py +155 -0
- llms/extensions/providers/google.py +378 -0
- llms/extensions/providers/nvidia.py +105 -0
- llms/extensions/providers/openai.py +156 -0
- llms/extensions/providers/openrouter.py +72 -0
- llms/extensions/system_prompts/README.md +22 -0
- llms/extensions/system_prompts/__init__.py +45 -0
- llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/system_prompts/ui/index.mjs +280 -0
- llms/extensions/system_prompts/ui/prompts.json +1067 -0
- llms/extensions/tools/__init__.py +5 -0
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/ui/index.mjs +204 -0
- llms/index.html +35 -77
- llms/llms.json +357 -1186
- llms/main.py +2349 -591
- llms/providers-extra.json +356 -0
- llms/providers.json +1 -0
- llms/ui/App.mjs +151 -60
- llms/ui/ai.mjs +132 -60
- llms/ui/app.css +2173 -161
- llms/ui/ctx.mjs +365 -0
- llms/ui/index.mjs +129 -0
- llms/ui/lib/charts.mjs +9 -13
- llms/ui/lib/servicestack-vue.mjs +3 -3
- llms/ui/lib/vue.min.mjs +10 -9
- llms/ui/lib/vue.mjs +1796 -1635
- llms/ui/markdown.mjs +18 -7
- llms/ui/modules/chat/ChatBody.mjs +691 -0
- llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +9 -9
- llms/ui/modules/chat/index.mjs +828 -0
- llms/ui/modules/layout.mjs +243 -0
- llms/ui/modules/model-selector.mjs +851 -0
- llms/ui/tailwind.input.css +496 -80
- llms/ui/utils.mjs +161 -93
- {llms_py-2.0.35.dist-info → llms_py-3.0.0.dist-info}/METADATA +1 -1
- llms_py-3.0.0.dist-info/RECORD +202 -0
- llms/ui/Avatar.mjs +0 -85
- llms/ui/Brand.mjs +0 -52
- llms/ui/ChatPrompt.mjs +0 -590
- llms/ui/Main.mjs +0 -823
- llms/ui/ModelSelector.mjs +0 -78
- llms/ui/OAuthSignIn.mjs +0 -92
- llms/ui/ProviderIcon.mjs +0 -30
- llms/ui/ProviderStatus.mjs +0 -105
- llms/ui/SignIn.mjs +0 -64
- llms/ui/SystemPromptEditor.mjs +0 -31
- llms/ui/SystemPromptSelector.mjs +0 -56
- llms/ui/Welcome.mjs +0 -8
- llms/ui/threadStore.mjs +0 -563
- llms/ui.json +0 -1069
- llms_py-2.0.35.dist-info/RECORD +0 -48
- {llms_py-2.0.35.dist-info → llms_py-3.0.0.dist-info}/WHEEL +0 -0
- {llms_py-2.0.35.dist-info → llms_py-3.0.0.dist-info}/entry_points.txt +0 -0
- {llms_py-2.0.35.dist-info → llms_py-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {llms_py-2.0.35.dist-info → llms_py-3.0.0.dist-info}/top_level.txt +0 -0
llms/ui/App.mjs
CHANGED
|
@@ -1,97 +1,188 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { ref, computed, watch, inject, onMounted, onUnmounted } from "vue"
|
|
2
|
+
import { useRouter, useRoute } from "vue-router"
|
|
3
|
+
import { AppContext } from "./ctx.mjs"
|
|
4
|
+
|
|
5
|
+
// Vertical Sidebar Icons
|
|
6
|
+
const LeftBar = {
|
|
7
|
+
template: `
|
|
8
|
+
<div class="select-none flex flex-col space-y-2 pt-2.5 px-1">
|
|
9
|
+
<div v-for="(icon, id) in $ctx.left" :key="id" class="relative flex items-center justify-center">
|
|
10
|
+
<component :is="icon.component"
|
|
11
|
+
class="size-7 p-1 cursor-pointer text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 rounded block"
|
|
12
|
+
:class="{ 'bg-gray-200 dark:bg-gray-700' : icon.isActive({ ...$layout }) }"
|
|
13
|
+
@mouseenter="tooltip = icon.id"
|
|
14
|
+
@mouseleave="tooltip = ''"
|
|
15
|
+
/>
|
|
16
|
+
<div v-if="tooltip === icon.id && !icon.isActive({ ...$layout })"
|
|
17
|
+
class="absolute left-full top-1/2 -translate-y-1/2 ml-2 px-2 py-1 text-xs text-white bg-gray-900 dark:bg-gray-800 rounded shadow-md z-50 whitespace-nowrap pointer-events-none" style="z-index: 60">
|
|
18
|
+
{{icon.title ?? icon.name}}
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
`,
|
|
23
|
+
setup() {
|
|
24
|
+
const tooltip = ref('')
|
|
25
|
+
return {
|
|
26
|
+
tooltip,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const LeftPanel = {
|
|
32
|
+
template: `
|
|
33
|
+
<div v-if="component" class="flex flex-col h-full border-r border-gray-200 dark:border-gray-700">
|
|
34
|
+
<button type="button" class="absolute top-2 right-2 p-1 rounded-md text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 lg:hidden z-20">
|
|
35
|
+
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
|
36
|
+
</button>
|
|
37
|
+
<component :is="component" />
|
|
38
|
+
</div>
|
|
39
|
+
`,
|
|
40
|
+
setup() {
|
|
41
|
+
/**@type {AppContext} */
|
|
42
|
+
const ctx = inject('ctx')
|
|
43
|
+
const component = computed(() => ctx.component(ctx.layout.left))
|
|
44
|
+
return {
|
|
45
|
+
component,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const TopBar = {
|
|
51
|
+
template: `
|
|
52
|
+
<div class="select-none flex space-x-1">
|
|
53
|
+
<div v-for="(icon, id) in $ctx.top" :key="id" class="relative flex items-center justify-center">
|
|
54
|
+
<component :is="icon.component"
|
|
55
|
+
class="size-7 p-1 cursor-pointer text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 block border border-transparent"
|
|
56
|
+
:class="{ 'bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-600 rounded' : icon.isActive({ ...$layout }) }"
|
|
57
|
+
@mouseenter="tooltip = icon.id"
|
|
58
|
+
@mouseleave="tooltip = ''"
|
|
59
|
+
/>
|
|
60
|
+
<div v-if="tooltip === icon.id && !icon.isActive({ ...$layout })"
|
|
61
|
+
class="absolute top-full mt-2 px-2 py-1 text-xs text-white bg-gray-900 dark:bg-gray-800 rounded shadow-md z-50 whitespace-nowrap pointer-events-none"
|
|
62
|
+
:class="last2.includes(id) ? 'right-0' : 'left-1/2 -translate-x-1/2'">
|
|
63
|
+
{{icon.title ?? icon.name}}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
`,
|
|
68
|
+
setup() {
|
|
69
|
+
const tooltip = ref('')
|
|
70
|
+
const last2 = ref(Object.keys($ctx.top).slice(-2))
|
|
71
|
+
return {
|
|
72
|
+
tooltip,
|
|
73
|
+
last2,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const TopPanel = {
|
|
79
|
+
template: `
|
|
80
|
+
<component v-if="component" :is="component" class="mb-2" />
|
|
81
|
+
`,
|
|
82
|
+
setup() {
|
|
83
|
+
/**@type {AppContext} */
|
|
84
|
+
const ctx = inject('ctx')
|
|
85
|
+
const component = computed(() => ctx.component(ctx.layout.top))
|
|
86
|
+
return {
|
|
87
|
+
component,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
3
91
|
|
|
4
92
|
export default {
|
|
5
93
|
components: {
|
|
6
|
-
|
|
94
|
+
LeftBar,
|
|
95
|
+
LeftPanel,
|
|
96
|
+
TopBar,
|
|
97
|
+
TopPanel,
|
|
7
98
|
},
|
|
8
99
|
setup() {
|
|
9
|
-
const
|
|
100
|
+
const router = useRouter()
|
|
101
|
+
const route = useRoute()
|
|
102
|
+
|
|
103
|
+
/**@type {AppContext} */
|
|
104
|
+
const ctx = inject('ctx')
|
|
105
|
+
const ai = ctx.ai
|
|
10
106
|
const isMobile = ref(false)
|
|
107
|
+
const modal = ref()
|
|
11
108
|
|
|
12
109
|
const checkMobile = () => {
|
|
13
|
-
const wasMobile = isMobile.value
|
|
14
|
-
isMobile.value = window.innerWidth <
|
|
110
|
+
//const wasMobile = isMobile.value
|
|
111
|
+
isMobile.value = window.innerWidth < 640 // sm breakpoint
|
|
15
112
|
|
|
113
|
+
//console.log('checkMobile', wasMobile, isMobile.value)
|
|
16
114
|
// Only auto-adjust sidebar state when transitioning between mobile/desktop
|
|
17
|
-
if (wasMobile !== isMobile.value) {
|
|
18
|
-
if (isMobile.value) {
|
|
19
|
-
ai.isSidebarOpen = false
|
|
20
|
-
} else {
|
|
21
|
-
ai.isSidebarOpen = true
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const toggleSidebar = () => {
|
|
27
|
-
ai.isSidebarOpen = !ai.isSidebarOpen
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const closeSidebar = () => {
|
|
31
115
|
if (isMobile.value) {
|
|
32
|
-
|
|
116
|
+
ctx.toggleLayout('left', false)
|
|
33
117
|
}
|
|
34
118
|
}
|
|
35
119
|
|
|
36
120
|
onMounted(() => {
|
|
37
121
|
checkMobile()
|
|
38
122
|
window.addEventListener('resize', checkMobile)
|
|
123
|
+
if (route.query.open) {
|
|
124
|
+
modal.value = ctx.openModal(route.query.open)
|
|
125
|
+
}
|
|
39
126
|
})
|
|
40
127
|
|
|
41
128
|
onUnmounted(() => {
|
|
42
129
|
window.removeEventListener('resize', checkMobile)
|
|
43
130
|
})
|
|
44
131
|
|
|
45
|
-
|
|
132
|
+
function closeModal() {
|
|
133
|
+
ctx.closeModal(route.query.open)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
watch(() => route.query.open, (newVal) => {
|
|
137
|
+
modal.value = ctx.modalComponents[newVal]
|
|
138
|
+
console.log('open', newVal, modal.value)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
watch(() => ctx.state.selectedModel, (newVal) => {
|
|
142
|
+
ctx.chat.setSelectedModel(ctx.chat.getModel(newVal))
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
return { ai, modal, isMobile, closeModal }
|
|
46
146
|
},
|
|
47
147
|
template: `
|
|
48
|
-
<div class="flex h-screen
|
|
148
|
+
<div class="flex h-screen">
|
|
49
149
|
<!-- Mobile Overlay -->
|
|
50
|
-
<div
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
|
150
|
+
<div v-if="isMobile && $ctx.layoutVisible('left') && $ai.hasAccess"
|
|
151
|
+
@click="$ctx.toggleLayout('left')"
|
|
152
|
+
:class="$ctx.cls('mobile-overlay', 'fixed inset-0 bg-black/50 z-40 lg:hidden')"
|
|
54
153
|
></div>
|
|
55
154
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
155
|
+
<div v-if="$ai.hasAccess" id="sidebar" :class="$ctx.cls('sidebar', 'z-100 relative flex bg-gray-50 dark:bg-gray-800')">
|
|
156
|
+
<LeftBar id="left-bar" />
|
|
157
|
+
<LeftPanel id="left-panel"
|
|
158
|
+
v-if="$ai.hasAccess && $ctx.layoutVisible('left')"
|
|
159
|
+
:class="[
|
|
160
|
+
'transition-transform duration-300 ease-in-out z-50',
|
|
161
|
+
'w-72 xl:w-80 flex-shrink-0',
|
|
162
|
+
'lg:relative',
|
|
163
|
+
'fixed inset-y-0 left-[2.25rem] lg:left-0',
|
|
164
|
+
'bg-gray-50 dark:bg-gray-800'
|
|
165
|
+
]"
|
|
166
|
+
/>
|
|
67
167
|
</div>
|
|
68
168
|
|
|
69
169
|
<!-- Main Area -->
|
|
70
|
-
<div class="flex-1 flex flex-col">
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
>
|
|
76
|
-
<button type="button"
|
|
77
|
-
@click="toggleSidebar"
|
|
78
|
-
class="group p-1 text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
|
79
|
-
title="Open sidebar"
|
|
80
|
-
>
|
|
81
|
-
<div class="relative w-5 h-5">
|
|
82
|
-
<!-- Default sidebar icon -->
|
|
83
|
-
<svg class="absolute inset-0 group-hover:hidden" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
84
|
-
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
|
85
|
-
<line x1="9" y1="3" x2="9" y2="21"></line>
|
|
86
|
-
</svg>
|
|
87
|
-
<!-- Hover state: |→ icon -->
|
|
88
|
-
<svg class="absolute inset-0 hidden group-hover:block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m17.172 11l-4.657-4.657l1.414-1.414L21 12l-7.071 7.071l-1.414-1.414L17.172 13H8v-2zM4 19V5h2v14z"/></svg>
|
|
170
|
+
<div id="main" :class="$ctx.cls('main', 'flex-1 flex flex-col')">
|
|
171
|
+
<div id="main-inner" :class="$ctx.cls('main-inner', 'flex flex-col h-full w-full overflow-hidden')">
|
|
172
|
+
<div v-if="$ai.hasAccess" id="header" :class="$ctx.cls('header', 'py-1 pr-1 flex items-center justify-between shrink-0')">
|
|
173
|
+
<div>
|
|
174
|
+
<ModelSelector :models="$state.models" v-model="$state.selectedModel" />
|
|
89
175
|
</div>
|
|
90
|
-
|
|
176
|
+
<TopBar id="top-bar" />
|
|
177
|
+
</div>
|
|
178
|
+
<TopPanel v-if="$ai.hasAccess" id="top-panel" :class="$ctx.cls('top-panel', 'shrink-0')" />
|
|
179
|
+
<div id="page" :class="$ctx.cls('page', 'flex-1 overflow-y-auto min-h-0 flex flex-col')">
|
|
180
|
+
<RouterView class="h-full" />
|
|
181
|
+
</div>
|
|
91
182
|
</div>
|
|
92
|
-
|
|
93
|
-
<RouterView />
|
|
94
183
|
</div>
|
|
184
|
+
|
|
185
|
+
<component v-if="modal" :is="modal" :class="$ctx.cls('modal', '!z-[200]')" @done="closeModal" />
|
|
95
186
|
</div>
|
|
96
187
|
`,
|
|
97
188
|
}
|
llms/ui/ai.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { reactive } from "vue"
|
|
2
|
-
import {
|
|
2
|
+
import { ApiResult } from "@servicestack/client"
|
|
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: '
|
|
9
|
+
version: '3.0.0',
|
|
10
10
|
base,
|
|
11
11
|
prefsKey,
|
|
12
12
|
welcome: 'Welcome to llms.py',
|
|
@@ -15,24 +15,88 @@ 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
|
-
|
|
19
|
-
|
|
18
|
+
cacheUrlInfo: {},
|
|
19
|
+
|
|
20
|
+
get hasAccess() {
|
|
21
|
+
return !this.requiresAuth || this.auth
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
resolveUrl(url) {
|
|
20
25
|
return url.startsWith('http') || url.startsWith('/v1') ? url : base + url
|
|
21
26
|
},
|
|
22
27
|
get(url, options) {
|
|
23
|
-
return fetch(this.resolveUrl(url), {
|
|
28
|
+
return fetch(this.resolveUrl(url), {
|
|
24
29
|
...options,
|
|
25
30
|
headers: Object.assign({}, this.headers, options?.headers),
|
|
26
31
|
})
|
|
27
32
|
},
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
async getJson(url, options) {
|
|
34
|
+
const res = await this.get(url, options)
|
|
35
|
+
return await this.createJsonResult(res, url)
|
|
36
|
+
},
|
|
37
|
+
async post(url, options) {
|
|
38
|
+
return await fetch(this.resolveUrl(url), {
|
|
30
39
|
method: 'POST',
|
|
31
40
|
...options,
|
|
32
|
-
headers: Object.assign({'Content-Type': 'application/json'}, this.headers, options?.headers),
|
|
41
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, this.headers, options?.headers),
|
|
42
|
+
})
|
|
43
|
+
},
|
|
44
|
+
async postForm(url, options) {
|
|
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}` })
|
|
33
98
|
})
|
|
34
99
|
},
|
|
35
|
-
|
|
36
100
|
async getConfig() {
|
|
37
101
|
return this.get('/config')
|
|
38
102
|
},
|
|
@@ -40,63 +104,45 @@ export const o = {
|
|
|
40
104
|
return this.get('/models')
|
|
41
105
|
},
|
|
42
106
|
async getAuth() {
|
|
43
|
-
return this.requiresAuth
|
|
107
|
+
return this.requiresAuth
|
|
44
108
|
? this.get('/auth')
|
|
45
|
-
: new Promise(resolve => resolve({ json: () => ({ responseStatus: { errorCode: '!requiresAuth' } })}))
|
|
109
|
+
: new Promise(resolve => resolve({ json: () => ({ responseStatus: { errorCode: '!requiresAuth' } }) }))
|
|
46
110
|
},
|
|
47
111
|
get isAdmin() {
|
|
48
112
|
return !this.requiresAuth || this.auth && this.auth.roles?.includes('Admin')
|
|
49
113
|
},
|
|
50
|
-
|
|
114
|
+
|
|
51
115
|
signIn(auth) {
|
|
52
116
|
this.auth = auth
|
|
53
117
|
if (auth?.apiKey) {
|
|
54
118
|
this.headers.Authorization = `Bearer ${auth.apiKey}`
|
|
55
|
-
//localStorage.setItem('llms:auth', JSON.stringify({ apiKey: auth.apiKey }))
|
|
56
|
-
} else if (auth?.sessionToken) {
|
|
57
|
-
this.headers['X-Session-Token'] = auth.sessionToken
|
|
58
|
-
localStorage.setItem('llms:auth', JSON.stringify({ sessionToken: auth.sessionToken }))
|
|
59
119
|
} else {
|
|
60
120
|
if (this.headers.Authorization) {
|
|
61
121
|
delete this.headers.Authorization
|
|
62
122
|
}
|
|
63
|
-
if (this.headers['X-Session-Token']) {
|
|
64
|
-
delete this.headers['X-Session-Token']
|
|
65
|
-
}
|
|
66
123
|
}
|
|
67
124
|
},
|
|
68
125
|
async signOut() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
headers: {
|
|
74
|
-
'X-Session-Token': this.auth.sessionToken
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.error('Logout error:', error)
|
|
79
|
-
}
|
|
126
|
+
try {
|
|
127
|
+
await this.post('/auth/logout')
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error('Logout error:', error)
|
|
80
130
|
}
|
|
81
131
|
this.auth = null
|
|
82
132
|
if (this.headers.Authorization) {
|
|
83
133
|
delete this.headers.Authorization
|
|
84
134
|
}
|
|
85
|
-
if (this.headers['X-Session-Token']) {
|
|
86
|
-
delete this.headers['X-Session-Token']
|
|
87
|
-
}
|
|
88
|
-
localStorage.removeItem('llms:auth')
|
|
89
135
|
},
|
|
90
|
-
async init() {
|
|
136
|
+
async init(ctx) {
|
|
91
137
|
// Load models and prompts
|
|
92
|
-
const
|
|
93
|
-
const [_, configRes, modelsRes] = await Promise.all([
|
|
94
|
-
initDB(),
|
|
138
|
+
const [configRes, modelsRes, extensionsRes] = await Promise.all([
|
|
95
139
|
this.getConfig(),
|
|
96
140
|
this.getModels(),
|
|
141
|
+
this.get('/ext'),
|
|
97
142
|
])
|
|
98
143
|
const config = await configRes.json()
|
|
99
144
|
const models = await modelsRes.json()
|
|
145
|
+
const extensions = await extensionsRes.json()
|
|
100
146
|
|
|
101
147
|
// Update auth settings from server config
|
|
102
148
|
if (config.requiresAuth != null) {
|
|
@@ -106,25 +152,6 @@ export const o = {
|
|
|
106
152
|
this.authType = config.authType
|
|
107
153
|
}
|
|
108
154
|
|
|
109
|
-
// Try to restore session from localStorage
|
|
110
|
-
if (this.requiresAuth) {
|
|
111
|
-
const storedAuth = localStorage.getItem('llms:auth')
|
|
112
|
-
if (storedAuth) {
|
|
113
|
-
try {
|
|
114
|
-
const authData = JSON.parse(storedAuth)
|
|
115
|
-
if (authData.sessionToken) {
|
|
116
|
-
this.headers['X-Session-Token'] = authData.sessionToken
|
|
117
|
-
}
|
|
118
|
-
// else if (authData.apiKey) {
|
|
119
|
-
// this.headers.Authorization = `Bearer ${authData.apiKey}`
|
|
120
|
-
// }
|
|
121
|
-
} catch (e) {
|
|
122
|
-
console.error('Failed to restore auth from localStorage:', e)
|
|
123
|
-
localStorage.removeItem('llms:auth')
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
155
|
// Get auth status
|
|
129
156
|
const authRes = await this.getAuth()
|
|
130
157
|
const auth = this.requiresAuth
|
|
@@ -132,14 +159,59 @@ export const o = {
|
|
|
132
159
|
: null
|
|
133
160
|
if (auth?.responseStatus?.errorCode) {
|
|
134
161
|
console.error(auth.responseStatus.errorCode, auth.responseStatus.message)
|
|
135
|
-
// Clear invalid session from localStorage
|
|
136
|
-
localStorage.removeItem('llms:auth')
|
|
137
162
|
} else {
|
|
138
163
|
this.signIn(auth)
|
|
139
164
|
}
|
|
140
|
-
return { config, models, auth }
|
|
165
|
+
return { config, models, extensions, auth }
|
|
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
|
|
141
211
|
}
|
|
212
|
+
|
|
142
213
|
}
|
|
143
214
|
|
|
215
|
+
|
|
144
216
|
let ai = reactive(o)
|
|
145
217
|
export default ai
|