llms-py 2.0.9__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 (194) hide show
  1. llms/__init__.py +4 -0
  2. llms/__main__.py +9 -0
  3. llms/db.py +359 -0
  4. llms/extensions/analytics/ui/index.mjs +1444 -0
  5. llms/extensions/app/README.md +20 -0
  6. llms/extensions/app/__init__.py +589 -0
  7. llms/extensions/app/db.py +536 -0
  8. {llms_py-2.0.9.data/data → llms/extensions/app}/ui/Recents.mjs +100 -73
  9. llms_py-2.0.9.data/data/ui/Sidebar.mjs → llms/extensions/app/ui/index.mjs +150 -79
  10. llms/extensions/app/ui/threadStore.mjs +433 -0
  11. llms/extensions/core_tools/CALCULATOR.md +32 -0
  12. llms/extensions/core_tools/__init__.py +637 -0
  13. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
  14. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
  15. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
  16. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
  17. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
  18. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
  19. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
  20. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
  21. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
  22. llms/extensions/core_tools/ui/codemirror/codemirror.css +344 -0
  23. llms/extensions/core_tools/ui/codemirror/codemirror.js +9884 -0
  24. llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
  25. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  26. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
  27. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
  28. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
  29. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
  30. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
  31. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
  32. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
  33. llms/extensions/core_tools/ui/index.mjs +650 -0
  34. llms/extensions/gallery/README.md +61 -0
  35. llms/extensions/gallery/__init__.py +63 -0
  36. llms/extensions/gallery/db.py +243 -0
  37. llms/extensions/gallery/ui/index.mjs +482 -0
  38. llms/extensions/katex/README.md +39 -0
  39. llms/extensions/katex/__init__.py +6 -0
  40. llms/extensions/katex/ui/README.md +125 -0
  41. llms/extensions/katex/ui/contrib/auto-render.js +338 -0
  42. llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
  43. llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
  44. llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
  45. llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
  46. llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
  47. llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
  48. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
  49. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
  50. llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
  51. llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
  52. llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
  53. llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
  54. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
  55. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
  56. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  57. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  58. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  59. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  60. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  61. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  62. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  63. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  64. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  65. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  66. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  67. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  68. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  69. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  70. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  71. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  72. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  73. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  74. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  75. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  76. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  77. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  78. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  79. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  80. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  81. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  82. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  83. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  84. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  85. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  86. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  87. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  88. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  89. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  90. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  91. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  92. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  93. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  94. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  116. llms/extensions/katex/ui/index.mjs +92 -0
  117. llms/extensions/katex/ui/katex-swap.css +1230 -0
  118. llms/extensions/katex/ui/katex-swap.min.css +1 -0
  119. llms/extensions/katex/ui/katex.css +1230 -0
  120. llms/extensions/katex/ui/katex.js +19080 -0
  121. llms/extensions/katex/ui/katex.min.css +1 -0
  122. llms/extensions/katex/ui/katex.min.js +1 -0
  123. llms/extensions/katex/ui/katex.min.mjs +1 -0
  124. llms/extensions/katex/ui/katex.mjs +18547 -0
  125. llms/extensions/providers/__init__.py +22 -0
  126. llms/extensions/providers/anthropic.py +233 -0
  127. llms/extensions/providers/cerebras.py +37 -0
  128. llms/extensions/providers/chutes.py +153 -0
  129. llms/extensions/providers/google.py +481 -0
  130. llms/extensions/providers/nvidia.py +103 -0
  131. llms/extensions/providers/openai.py +154 -0
  132. llms/extensions/providers/openrouter.py +74 -0
  133. llms/extensions/providers/zai.py +182 -0
  134. llms/extensions/system_prompts/README.md +22 -0
  135. llms/extensions/system_prompts/__init__.py +45 -0
  136. llms/extensions/system_prompts/ui/index.mjs +280 -0
  137. llms/extensions/system_prompts/ui/prompts.json +1067 -0
  138. llms/extensions/tools/__init__.py +144 -0
  139. llms/extensions/tools/ui/index.mjs +706 -0
  140. llms/index.html +58 -0
  141. llms/llms.json +400 -0
  142. llms/main.py +4407 -0
  143. llms/providers-extra.json +394 -0
  144. llms/providers.json +1 -0
  145. llms/ui/App.mjs +188 -0
  146. llms/ui/ai.mjs +217 -0
  147. llms/ui/app.css +7081 -0
  148. llms/ui/ctx.mjs +412 -0
  149. llms/ui/index.mjs +131 -0
  150. llms/ui/lib/chart.js +14 -0
  151. llms/ui/lib/charts.mjs +16 -0
  152. llms/ui/lib/color.js +14 -0
  153. llms/ui/lib/servicestack-vue.mjs +37 -0
  154. llms/ui/lib/vue.min.mjs +13 -0
  155. llms/ui/lib/vue.mjs +18530 -0
  156. {llms_py-2.0.9.data/data → llms}/ui/markdown.mjs +33 -15
  157. llms/ui/modules/chat/ChatBody.mjs +976 -0
  158. llms/ui/modules/chat/SettingsDialog.mjs +374 -0
  159. llms/ui/modules/chat/index.mjs +991 -0
  160. llms/ui/modules/icons.mjs +46 -0
  161. llms/ui/modules/layout.mjs +271 -0
  162. llms/ui/modules/model-selector.mjs +811 -0
  163. llms/ui/tailwind.input.css +742 -0
  164. {llms_py-2.0.9.data/data → llms}/ui/typography.css +133 -7
  165. llms/ui/utils.mjs +261 -0
  166. llms_py-3.0.10.dist-info/METADATA +49 -0
  167. llms_py-3.0.10.dist-info/RECORD +177 -0
  168. llms_py-3.0.10.dist-info/entry_points.txt +2 -0
  169. {llms_py-2.0.9.dist-info → llms_py-3.0.10.dist-info}/licenses/LICENSE +1 -2
  170. llms.py +0 -1402
  171. llms_py-2.0.9.data/data/index.html +0 -64
  172. llms_py-2.0.9.data/data/llms.json +0 -447
  173. llms_py-2.0.9.data/data/requirements.txt +0 -1
  174. llms_py-2.0.9.data/data/ui/App.mjs +0 -20
  175. llms_py-2.0.9.data/data/ui/ChatPrompt.mjs +0 -389
  176. llms_py-2.0.9.data/data/ui/Main.mjs +0 -680
  177. llms_py-2.0.9.data/data/ui/app.css +0 -3951
  178. llms_py-2.0.9.data/data/ui/lib/servicestack-vue.min.mjs +0 -37
  179. llms_py-2.0.9.data/data/ui/lib/vue.min.mjs +0 -12
  180. llms_py-2.0.9.data/data/ui/tailwind.input.css +0 -261
  181. llms_py-2.0.9.data/data/ui/threadStore.mjs +0 -273
  182. llms_py-2.0.9.data/data/ui/utils.mjs +0 -114
  183. llms_py-2.0.9.data/data/ui.json +0 -1069
  184. llms_py-2.0.9.dist-info/METADATA +0 -941
  185. llms_py-2.0.9.dist-info/RECORD +0 -30
  186. llms_py-2.0.9.dist-info/entry_points.txt +0 -2
  187. {llms_py-2.0.9.data/data → llms}/ui/fav.svg +0 -0
  188. {llms_py-2.0.9.data/data → llms}/ui/lib/highlight.min.mjs +0 -0
  189. {llms_py-2.0.9.data/data → llms}/ui/lib/idb.min.mjs +0 -0
  190. {llms_py-2.0.9.data/data → llms}/ui/lib/marked.min.mjs +0 -0
  191. /llms_py-2.0.9.data/data/ui/lib/servicestack-client.min.mjs → /llms/ui/lib/servicestack-client.mjs +0 -0
  192. {llms_py-2.0.9.data/data → llms}/ui/lib/vue-router.min.mjs +0 -0
  193. {llms_py-2.0.9.dist-info → llms_py-3.0.10.dist-info}/WHEEL +0 -0
  194. {llms_py-2.0.9.dist-info → llms_py-3.0.10.dist-info}/top_level.txt +0 -0
llms/ui/App.mjs ADDED
@@ -0,0 +1,188 @@
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" @click="$ctx.toggleLayout('left',false)" 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
+ }
91
+
92
+ export default {
93
+ components: {
94
+ LeftBar,
95
+ LeftPanel,
96
+ TopBar,
97
+ TopPanel,
98
+ },
99
+ setup() {
100
+ const router = useRouter()
101
+ const route = useRoute()
102
+
103
+ /**@type {AppContext} */
104
+ const ctx = inject('ctx')
105
+ const ai = ctx.ai
106
+ const isMobile = ref(false)
107
+ const modal = ref()
108
+
109
+ const checkMobile = () => {
110
+ //const wasMobile = isMobile.value
111
+ isMobile.value = window.innerWidth < 640 // sm breakpoint
112
+
113
+ //console.log('checkMobile', wasMobile, isMobile.value)
114
+ // Only auto-adjust sidebar state when transitioning between mobile/desktop
115
+ if (isMobile.value) {
116
+ ctx.toggleLayout('left', false)
117
+ }
118
+ }
119
+
120
+ onMounted(() => {
121
+ checkMobile()
122
+ window.addEventListener('resize', checkMobile)
123
+ if (route.query.open) {
124
+ modal.value = ctx.openModal(route.query.open)
125
+ }
126
+ })
127
+
128
+ onUnmounted(() => {
129
+ window.removeEventListener('resize', checkMobile)
130
+ })
131
+
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 }
146
+ },
147
+ template: `
148
+ <div class="flex h-screen">
149
+ <!-- Mobile Overlay -->
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')"
153
+ ></div>
154
+
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
+ />
167
+ </div>
168
+
169
+ <!-- Main Area -->
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" />
175
+ </div>
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>
182
+ </div>
183
+ </div>
184
+
185
+ <component v-if="modal" :is="modal" :class="$ctx.cls('modal', '!z-[200]')" @done="closeModal" />
186
+ </div>
187
+ `,
188
+ }
llms/ui/ai.mjs ADDED
@@ -0,0 +1,217 @@
1
+ import { reactive } from "vue"
2
+ import { ApiResult } from "@servicestack/client"
3
+
4
+ const base = ''
5
+ const headers = { 'Accept': 'application/json' }
6
+ const prefsKey = 'llms.prefs'
7
+
8
+ export const o = {
9
+ version: '3.0.10',
10
+ base,
11
+ prefsKey,
12
+ welcome: 'Welcome to llms.py',
13
+ auth: null,
14
+ requiresAuth: false,
15
+ authType: 'apikey', // 'oauth' or 'apikey' - controls which SignIn component to use
16
+ headers,
17
+ isSidebarOpen: true, // Shared sidebar state (default open for lg+ screens)
18
+ cacheUrlInfo: {},
19
+
20
+ get hasAccess() {
21
+ return !this.requiresAuth || this.auth
22
+ },
23
+
24
+ resolveUrl(url) {
25
+ return url.startsWith('http') || url.startsWith('/v1') ? url : base + url
26
+ },
27
+ get(url, options) {
28
+ return fetch(this.resolveUrl(url), {
29
+ ...options,
30
+ headers: Object.assign({}, this.headers, options?.headers),
31
+ })
32
+ },
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), {
39
+ method: 'POST',
40
+ ...options,
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}` })
98
+ })
99
+ },
100
+ async getConfig() {
101
+ return this.get('/config')
102
+ },
103
+ async getModels() {
104
+ return this.get('/models')
105
+ },
106
+ async getAuth() {
107
+ return this.requiresAuth
108
+ ? this.get('/auth')
109
+ : new Promise(resolve => resolve({ json: () => ({ responseStatus: { errorCode: '!requiresAuth' } }) }))
110
+ },
111
+ get isAdmin() {
112
+ return !this.requiresAuth || this.auth && this.auth.roles?.includes('Admin')
113
+ },
114
+
115
+ signIn(auth) {
116
+ this.auth = auth
117
+ if (auth?.apiKey) {
118
+ this.headers.Authorization = `Bearer ${auth.apiKey}`
119
+ } else {
120
+ if (this.headers.Authorization) {
121
+ delete this.headers.Authorization
122
+ }
123
+ }
124
+ },
125
+ async signOut() {
126
+ try {
127
+ await this.post('/auth/logout')
128
+ } catch (error) {
129
+ console.error('Logout error:', error)
130
+ }
131
+ this.auth = null
132
+ if (this.headers.Authorization) {
133
+ delete this.headers.Authorization
134
+ }
135
+ },
136
+ async init(ctx) {
137
+ // Load models and prompts
138
+ const [configRes, modelsRes, extensionsRes] = await Promise.all([
139
+ this.getConfig(),
140
+ this.getModels(),
141
+ this.get('/ext'),
142
+ ])
143
+ const config = await configRes.json()
144
+ const models = await modelsRes.json()
145
+ const extensions = await extensionsRes.json()
146
+
147
+ // Update auth settings from server config
148
+ if (config.requiresAuth != null) {
149
+ this.requiresAuth = config.requiresAuth
150
+ }
151
+ if (config.authType != null) {
152
+ this.authType = config.authType
153
+ }
154
+
155
+ // Get auth status
156
+ const authRes = await this.getAuth()
157
+ const auth = this.requiresAuth
158
+ ? await authRes.json()
159
+ : null
160
+ if (auth?.responseStatus?.errorCode) {
161
+ console.error(auth.responseStatus.errorCode, auth.responseStatus.message)
162
+ } else {
163
+ this.signIn(auth)
164
+ }
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
211
+ }
212
+
213
+ }
214
+
215
+
216
+ let ai = reactive(o)
217
+ export default ai