llms-py 2.0.20__py3-none-any.whl → 3.0.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. llms/__init__.py +3 -1
  2. llms/db.py +359 -0
  3. llms/{ui/Analytics.mjs → extensions/analytics/ui/index.mjs} +254 -327
  4. llms/extensions/app/README.md +20 -0
  5. llms/extensions/app/__init__.py +589 -0
  6. llms/extensions/app/db.py +536 -0
  7. llms/{ui → extensions/app/ui}/Recents.mjs +99 -73
  8. llms/{ui/Sidebar.mjs → extensions/app/ui/index.mjs} +139 -68
  9. llms/extensions/app/ui/threadStore.mjs +433 -0
  10. llms/extensions/core_tools/CALCULATOR.md +32 -0
  11. llms/extensions/core_tools/__init__.py +637 -0
  12. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
  13. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
  14. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
  15. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
  16. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
  17. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
  18. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
  19. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
  20. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
  21. llms/extensions/core_tools/ui/codemirror/codemirror.css +344 -0
  22. llms/extensions/core_tools/ui/codemirror/codemirror.js +9884 -0
  23. llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
  24. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  25. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
  26. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
  27. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
  28. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
  29. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
  30. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
  31. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
  32. llms/extensions/core_tools/ui/index.mjs +650 -0
  33. llms/extensions/gallery/README.md +61 -0
  34. llms/extensions/gallery/__init__.py +63 -0
  35. llms/extensions/gallery/db.py +243 -0
  36. llms/extensions/gallery/ui/index.mjs +482 -0
  37. llms/extensions/katex/README.md +39 -0
  38. llms/extensions/katex/__init__.py +6 -0
  39. llms/extensions/katex/ui/README.md +125 -0
  40. llms/extensions/katex/ui/contrib/auto-render.js +338 -0
  41. llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
  42. llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
  43. llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
  44. llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
  45. llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
  46. llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
  47. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
  48. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
  49. llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
  50. llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
  51. llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
  52. llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
  53. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
  54. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
  55. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  56. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  57. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  58. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  59. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  60. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  61. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  62. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  63. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  64. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  65. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  66. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  67. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  68. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  69. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  70. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  71. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  72. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  73. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  74. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  75. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  76. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  77. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  78. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  79. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  80. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  81. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  82. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  83. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  84. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  85. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  86. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  87. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  88. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  89. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  90. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  91. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  92. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  93. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  94. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  115. llms/extensions/katex/ui/index.mjs +92 -0
  116. llms/extensions/katex/ui/katex-swap.css +1230 -0
  117. llms/extensions/katex/ui/katex-swap.min.css +1 -0
  118. llms/extensions/katex/ui/katex.css +1230 -0
  119. llms/extensions/katex/ui/katex.js +19080 -0
  120. llms/extensions/katex/ui/katex.min.css +1 -0
  121. llms/extensions/katex/ui/katex.min.js +1 -0
  122. llms/extensions/katex/ui/katex.min.mjs +1 -0
  123. llms/extensions/katex/ui/katex.mjs +18547 -0
  124. llms/extensions/providers/__init__.py +22 -0
  125. llms/extensions/providers/anthropic.py +233 -0
  126. llms/extensions/providers/cerebras.py +37 -0
  127. llms/extensions/providers/chutes.py +153 -0
  128. llms/extensions/providers/google.py +481 -0
  129. llms/extensions/providers/nvidia.py +103 -0
  130. llms/extensions/providers/openai.py +154 -0
  131. llms/extensions/providers/openrouter.py +74 -0
  132. llms/extensions/providers/zai.py +182 -0
  133. llms/extensions/system_prompts/README.md +22 -0
  134. llms/extensions/system_prompts/__init__.py +45 -0
  135. llms/extensions/system_prompts/ui/index.mjs +280 -0
  136. llms/extensions/system_prompts/ui/prompts.json +1067 -0
  137. llms/extensions/tools/__init__.py +144 -0
  138. llms/extensions/tools/ui/index.mjs +706 -0
  139. llms/index.html +36 -62
  140. llms/llms.json +180 -879
  141. llms/main.py +3640 -899
  142. llms/providers-extra.json +394 -0
  143. llms/providers.json +1 -0
  144. llms/ui/App.mjs +176 -8
  145. llms/ui/ai.mjs +156 -20
  146. llms/ui/app.css +3161 -244
  147. llms/ui/ctx.mjs +412 -0
  148. llms/ui/index.mjs +131 -0
  149. llms/ui/lib/chart.js +14 -0
  150. llms/ui/lib/charts.mjs +16 -0
  151. llms/ui/lib/color.js +14 -0
  152. llms/ui/lib/highlight.min.mjs +1243 -0
  153. llms/ui/lib/idb.min.mjs +8 -0
  154. llms/ui/lib/marked.min.mjs +8 -0
  155. llms/ui/lib/servicestack-client.mjs +1 -0
  156. llms/ui/lib/servicestack-vue.mjs +37 -0
  157. llms/ui/lib/vue-router.min.mjs +6 -0
  158. llms/ui/lib/vue.min.mjs +13 -0
  159. llms/ui/lib/vue.mjs +18530 -0
  160. llms/ui/markdown.mjs +25 -14
  161. llms/ui/modules/chat/ChatBody.mjs +976 -0
  162. llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +74 -74
  163. llms/ui/modules/chat/index.mjs +991 -0
  164. llms/ui/modules/icons.mjs +46 -0
  165. llms/ui/modules/layout.mjs +271 -0
  166. llms/ui/modules/model-selector.mjs +811 -0
  167. llms/ui/tailwind.input.css +550 -78
  168. llms/ui/typography.css +54 -36
  169. llms/ui/utils.mjs +197 -92
  170. llms_py-3.0.10.dist-info/METADATA +49 -0
  171. llms_py-3.0.10.dist-info/RECORD +177 -0
  172. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/licenses/LICENSE +1 -2
  173. llms/ui/Avatar.mjs +0 -28
  174. llms/ui/Brand.mjs +0 -34
  175. llms/ui/ChatPrompt.mjs +0 -443
  176. llms/ui/Main.mjs +0 -740
  177. llms/ui/ModelSelector.mjs +0 -60
  178. llms/ui/ProviderIcon.mjs +0 -29
  179. llms/ui/ProviderStatus.mjs +0 -105
  180. llms/ui/SignIn.mjs +0 -64
  181. llms/ui/SystemPromptEditor.mjs +0 -31
  182. llms/ui/SystemPromptSelector.mjs +0 -36
  183. llms/ui/Welcome.mjs +0 -8
  184. llms/ui/threadStore.mjs +0 -524
  185. llms/ui.json +0 -1069
  186. llms_py-2.0.20.dist-info/METADATA +0 -931
  187. llms_py-2.0.20.dist-info/RECORD +0 -36
  188. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/WHEEL +0 -0
  189. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/entry_points.txt +0 -0
  190. {llms_py-2.0.20.dist-info → llms_py-3.0.10.dist-info}/top_level.txt +0 -0
llms/ui/ctx.mjs ADDED
@@ -0,0 +1,412 @@
1
+
2
+ import { reactive, markRaw } from 'vue'
3
+ import { EventBus, humanize, combinePaths } from "@servicestack/client"
4
+ import { storageObject, isHtml } from './utils.mjs'
5
+
6
+ export class ExtensionScope {
7
+ constructor(ctx, id) {
8
+ /**@type {AppContext} */
9
+ this.ctx = ctx
10
+ this.router = ctx.router
11
+ this.id = id
12
+ this.baseUrl = `${ctx.ai.base}/ext/${this.id}`
13
+ this.storageKey = `llms.${this.id}`
14
+ this.state = reactive({})
15
+ this.prefs = reactive(storageObject(this.storageKey))
16
+ }
17
+ getPrefs() {
18
+ return this.prefs
19
+ }
20
+ setPrefs(o) {
21
+ storageObject(this.storageKey, Object.assign(this.prefs, o))
22
+ }
23
+ savePrefs() {
24
+ storageObject(this.storageKey, this.prefs)
25
+ }
26
+ setState(o) {
27
+ Object.assign(this.state, o)
28
+ }
29
+ get(url, options) {
30
+ return this.ctx.ai.get(combinePaths(this.baseUrl, url), options)
31
+ }
32
+ delete(url, options) {
33
+ this.ctx.clearError()
34
+ return this.ctx.ai.get(combinePaths(this.baseUrl, url), {
35
+ ...options,
36
+ method: 'DELETE'
37
+ })
38
+ }
39
+ async getJson(url, options) {
40
+ return this.ctx.ai.getJson(combinePaths(this.baseUrl, url), options)
41
+ }
42
+ async deleteJson(url, options) {
43
+ this.ctx.clearError()
44
+ return this.ctx.ai.getJson(combinePaths(this.baseUrl, url), {
45
+ ...options,
46
+ method: 'DELETE'
47
+ })
48
+ }
49
+ post(url, options) {
50
+ this.ctx.clearError()
51
+ return this.ctx.ai.post(combinePaths(this.baseUrl, url), options)
52
+ }
53
+ put(url, options) {
54
+ this.ctx.clearError()
55
+ return this.ctx.ai.post(combinePaths(this.baseUrl, url), {
56
+ ...options,
57
+ method: 'PUT'
58
+ })
59
+ }
60
+ patch(url, options) {
61
+ this.ctx.clearError()
62
+ return this.ctx.ai.post(combinePaths(this.baseUrl, url), {
63
+ ...options,
64
+ method: 'PATCH'
65
+ })
66
+ }
67
+ async postForm(url, options) {
68
+ this.ctx.clearError()
69
+ return await this.ctx.ai.postForm(combinePaths(this.baseUrl, url), options)
70
+ }
71
+ async postJson(url, body) {
72
+ this.ctx.clearError()
73
+ return this.ctx.ai.postJson(combinePaths(this.baseUrl, url), {
74
+ body: body instanceof FormData ? body : JSON.stringify(body)
75
+ })
76
+ }
77
+ async putJson(url, body) {
78
+ this.ctx.clearError()
79
+ return this.ctx.ai.postJson(combinePaths(this.baseUrl, url), {
80
+ method: 'PUT',
81
+ body: body instanceof FormData ? body : JSON.stringify(body)
82
+ })
83
+ }
84
+ async patchJson(url, body) {
85
+ this.ctx.clearError()
86
+ return this.ctx.ai.postJson(combinePaths(this.baseUrl, url), {
87
+ method: 'PATCH',
88
+ body: body instanceof FormData ? body : JSON.stringify(body)
89
+ })
90
+ }
91
+ async createJsonResult(res) {
92
+ return this.ctx.ai.createJsonResult(res)
93
+ }
94
+ createErrorStatus(status) {
95
+ return this.ctx.ai.createErrorStatus(status)
96
+ }
97
+ createErrorResult(e) {
98
+ return this.ctx.ai.createErrorResult(e)
99
+ }
100
+ setError(e, msg = null) {
101
+ const prefix = this.id ? `[${this.id}] ` : ''
102
+ this.ctx.setError(e, msg ? `${prefix} ${msg}` : prefix)
103
+ }
104
+ clearError() {
105
+ this.ctx.clearError()
106
+ }
107
+ toast(msg) {
108
+ this.ctx.toast(msg)
109
+ }
110
+ to(route) {
111
+ if (typeof route == 'string') {
112
+ route = route.startsWith(this.baseUrl)
113
+ ? route
114
+ : combinePaths(this.baseUrl, route)
115
+ const path = { path: route }
116
+ console.log(`to/${this.id}`, path)
117
+ this.router.push(path)
118
+ } else {
119
+ route.path = route.path.startsWith(this.baseUrl)
120
+ ? route.path
121
+ : combinePaths(this.baseUrl, route.path)
122
+ console.log(`to/${this.id}`, route)
123
+ this.router.push(route)
124
+ }
125
+ }
126
+ }
127
+
128
+ export class AppContext {
129
+ constructor({ app, routes, ai, fmt, utils, marked }) {
130
+ this.app = app
131
+ this.routes = routes
132
+ this.ai = ai
133
+ this.fmt = fmt
134
+ this.utils = utils
135
+ this._components = {}
136
+ this.marked = marked
137
+
138
+ this.state = reactive({})
139
+ this.events = new EventBus()
140
+ this.modalComponents = {}
141
+ this.extensions = []
142
+ this.markedFilters = []
143
+ this.chatRequestFilters = []
144
+ this.chatResponseFilters = []
145
+ this.chatErrorFilters = []
146
+ this.createThreadFilters = []
147
+ this.updateThreadFilters = []
148
+ this.threadHeaderComponents = {}
149
+ this.threadFooterComponents = {}
150
+ this.top = {}
151
+ this.left = {}
152
+ this.layout = reactive(storageObject(`llms.layout`))
153
+ this.prefs = reactive(storageObject(ai.prefsKey))
154
+ this._onRouterBeforeEach = []
155
+ this._onClass = []
156
+
157
+ if (!Array.isArray(this.layout.hide)) {
158
+ this.layout.hide = []
159
+ }
160
+ Object.assign(app.config.globalProperties, {
161
+ $ctx: this,
162
+ $prefs: this.prefs,
163
+ $state: this.state,
164
+ $layout: this.layout,
165
+ $ai: ai,
166
+ $fmt: fmt,
167
+ $utils: utils,
168
+ })
169
+ Object.keys(app.config.globalProperties).forEach(key => {
170
+ globalThis[key] = app.config.globalProperties[key]
171
+ })
172
+ document.addEventListener('keydown', (e) => this.handleKeydown(e))
173
+ }
174
+ async init() {
175
+ Object.assign(this.state, await this.ai.init(this))
176
+ Object.assign(this.fmt, {
177
+ markdown: this.renderMarkdown.bind(this),
178
+ content: this.renderContent.bind(this),
179
+ })
180
+ }
181
+ setGlobals(globals) {
182
+ Object.entries(globals).forEach(([name, global]) => {
183
+ const globalName = '$' + name
184
+ globalThis[globalName] = this.app.config.globalProperties[globalName] = global
185
+ this[name] = global
186
+ })
187
+ }
188
+ getPrefs() {
189
+ return this.prefs
190
+ }
191
+ setPrefs(o) {
192
+ storageObject(this.ai.prefsKey, Object.assign(this.prefs, o))
193
+ }
194
+ _validateIcons(icons) {
195
+ Object.entries(icons).forEach(([id, icon]) => {
196
+ if (!icon.component) {
197
+ console.error(`Icon ${id} is missing component property`)
198
+ }
199
+ icon.id = id
200
+ if (!icon.name) {
201
+ icon.name = humanize(id)
202
+ }
203
+ if (typeof icon.isActive != 'function') {
204
+ icon.isActive = () => false
205
+ }
206
+ })
207
+ return icons
208
+ }
209
+ setTopIcons(icons) {
210
+ Object.assign(this.top, this._validateIcons(icons))
211
+ }
212
+ setLeftIcons(icons) {
213
+ Object.assign(this.left, this._validateIcons(icons))
214
+ }
215
+ component(name, component) {
216
+ if (!name) return name
217
+ if (component) {
218
+ this._components[name] = component
219
+ }
220
+ return component || this._components[name] || this.app.component(name)
221
+ }
222
+ components(components) {
223
+ if (components) {
224
+ Object.keys(components).forEach(name => {
225
+ this._components[name] = components[name]
226
+ })
227
+ }
228
+ return this._components
229
+ }
230
+ scope(extension) {
231
+ return new ExtensionScope(this, extension)
232
+ }
233
+ modals(modals) {
234
+ Object.keys(modals).forEach(name => {
235
+ const modal = markRaw(modals[name])
236
+ this.modalComponents[name] = modal
237
+ this.component(name, modal)
238
+ })
239
+ }
240
+ openModal(name) {
241
+ const component = this.modalComponents[name]
242
+ if (!component) {
243
+ console.error(`Modal ${name} not found`)
244
+ return
245
+ }
246
+ console.debug('openModal', name)
247
+ this.router.push({ query: { open: name } })
248
+ this.events.publish('modal:open', name)
249
+ return component
250
+ }
251
+ closeModal(name) {
252
+ console.debug('closeModal', name)
253
+ this.router.push({ query: { open: undefined } })
254
+ this.events.publish('modal:close', name)
255
+ }
256
+ handleKeydown(e) {
257
+ if (e.key === 'Escape') {
258
+ const modal = this.router.currentRoute.value?.query?.open
259
+ if (modal) {
260
+ this.closeModal(modal)
261
+ }
262
+ this.events.publish(`keydown:Escape`, e)
263
+ }
264
+ }
265
+ setState(o) {
266
+ Object.assign(this.state, o)
267
+ }
268
+ setLayout(o) {
269
+ Object.assign(this.layout, o)
270
+ storageObject(`llms.layout`, this.layout)
271
+ }
272
+ toggleLayout(key, toggle = undefined) {
273
+ const hide = toggle == undefined
274
+ ? !this.layout.hide.includes(key)
275
+ : !toggle
276
+ console.log('toggleLayout', key, hide)
277
+ if (hide) {
278
+ this.layout.hide.push(key)
279
+ } else {
280
+ this.layout.hide = this.layout.hide.filter(k => k != key)
281
+ }
282
+ storageObject(`llms.layout`, this.layout)
283
+ }
284
+ layoutVisible(key) {
285
+ return !this.layout.hide.includes(key)
286
+ }
287
+ toggleTop(name, toggle) {
288
+ if (toggle === false) {
289
+ this.layout.top = undefined
290
+ } else if (toggle === true) {
291
+ this.layout.top = name
292
+ } else {
293
+ this.layout.top = this.layout.top == name ? undefined : name
294
+ }
295
+ storageObject(`llms.layout`, this.layout)
296
+ console.log('toggleTop', name, toggle, this.layout.top, this.layout.top === name)
297
+ return this.layout.top === name
298
+ }
299
+ togglePath(path, toggle) {
300
+ const currentPath = this.router.currentRoute.value?.path
301
+ console.log('togglePath', path, currentPath, toggle)
302
+ if (currentPath != path) {
303
+ if (toggle === undefined) {
304
+ toggle = true
305
+ }
306
+ this.router.push({ path })
307
+ }
308
+ this.toggleLayout('left', toggle)
309
+ return toggle
310
+ }
311
+ setThreadHeaders(components) {
312
+ Object.assign(this.threadHeaderComponents, components)
313
+ }
314
+ setThreadFooters(components) {
315
+ Object.assign(this.threadFooterComponents, components)
316
+ }
317
+
318
+ createErrorStatus(status) {
319
+ return this.ai.createErrorStatus(status)
320
+ }
321
+ createErrorResult(e) {
322
+ return this.ai.createErrorResult(e)
323
+ }
324
+ setError(error, msg = null) {
325
+ this.state.error = error
326
+ if (error) {
327
+ if (msg) {
328
+ console.error(error.message, msg, error)
329
+ } else {
330
+ console.error(error.message, error)
331
+ }
332
+ }
333
+ }
334
+ clearError() {
335
+ this.state.error = null
336
+ }
337
+
338
+ resolveUrl(url) {
339
+ return this.ai.resolveUrl(url)
340
+ }
341
+ async getJson(url, options) {
342
+ return await this.ai.getJson(url, options)
343
+ }
344
+ async post(url, options) {
345
+ return await this.ai.post(url, options)
346
+ }
347
+ async postForm(url, options) {
348
+ return await this.ai.postForm(url, options)
349
+ }
350
+ async postJson(url, options) {
351
+ return await this.ai.postJson(url, options)
352
+ }
353
+ to(route) {
354
+ if (typeof route == 'string') {
355
+ route = route.startsWith(this.ai.base)
356
+ ? route
357
+ : combinePaths(this.ai.base, route)
358
+ const path = { path: route }
359
+ console.log('to', path)
360
+ this.router.push(path)
361
+ } else {
362
+ route.path = route.path.startsWith(this.ai.base)
363
+ ? route.path
364
+ : combinePaths(this.ai.base, route.path)
365
+ console.log('to', route)
366
+ this.router.push(route)
367
+ }
368
+ }
369
+
370
+ // Events
371
+ onRouterBeforeEach(callback) {
372
+ this._onRouterBeforeEach.push(callback)
373
+ }
374
+
375
+ onClass(callback) {
376
+ this._onClass.push(callback)
377
+ }
378
+
379
+ cls(id, cls) {
380
+ if (this._onClass.length) {
381
+ this._onClass.forEach(callback => {
382
+ cls = callback(id, cls) ?? cls
383
+ })
384
+ }
385
+ return cls
386
+ }
387
+ toast(msg) {
388
+ this.setState({ toast: msg })
389
+ }
390
+
391
+ renderMarkdown(content) {
392
+ if (Array.isArray(content)) {
393
+ content = content.filter(c => c.type === 'text').map(c => c.text).join('\n')
394
+ }
395
+ // Handled by katex
396
+ // if (content) {
397
+ // content = content
398
+ // .replaceAll(`\\[ \\boxed{`, '\n<span class="inline-block text-xl text-blue-500 bg-blue-50 dark:text-blue-400 dark:bg-blue-950 px-3 py-1 rounded">')
399
+ // .replaceAll('} \\]', '</span>\n')
400
+ // }
401
+ return this.marked.parse(content || '')
402
+ }
403
+
404
+ renderContent(content) {
405
+ // Check for HTML tags to detect HTML content
406
+ if (isHtml(content)) {
407
+ // If this is HTML content, return it in an iframe so it doesn't break the page
408
+ return `<iframe src="data:text/html;charset=utf-8,${encodeURIComponent(content)}"></iframe>`
409
+ }
410
+ return this.renderMarkdown(content)
411
+ }
412
+ }
llms/ui/index.mjs ADDED
@@ -0,0 +1,131 @@
1
+
2
+ import { createApp } from 'vue'
3
+ import { createWebHistory, createRouter } from "vue-router"
4
+ import ServiceStackVue, { useFormatters } from "@servicestack/vue"
5
+ import App from './App.mjs'
6
+ import ai from './ai.mjs'
7
+ import LayoutModule from './modules/layout.mjs'
8
+ import ChatModule from './modules/chat/index.mjs'
9
+ import ModelSelectorModule from './modules/model-selector.mjs'
10
+ import IconsModule from './modules/icons.mjs'
11
+ import { utilsFunctions, utilsFormatters } from './utils.mjs'
12
+ import { marked } from './markdown.mjs'
13
+ import { AppContext } from './ctx.mjs'
14
+
15
+ const Components = {
16
+ }
17
+
18
+ const BuiltInModules = {
19
+ LayoutModule,
20
+ ChatModule,
21
+ ModelSelectorModule,
22
+ IconsModule,
23
+ }
24
+
25
+
26
+ export async function createContext() {
27
+ const app = createApp(App)
28
+
29
+ app.use(ServiceStackVue)
30
+ Object.keys(Components).forEach(name => {
31
+ app.component(name, Components[name])
32
+ })
33
+
34
+ const fmt = Object.assign({}, useFormatters(), utilsFormatters())
35
+ const utils = Object.assign({}, utilsFunctions())
36
+ const routes = []
37
+
38
+ const ctx = new AppContext({ app, routes, ai, fmt, utils, marked })
39
+ app.provide('ctx', ctx)
40
+ await ctx.init()
41
+
42
+ // Load modules in parallel
43
+ const validExtensions = ctx.state.extensions.filter(x => x.path);
44
+ ctx.modules = await Promise.all(validExtensions.map(async extension => {
45
+ try {
46
+ const module = await import(extension.path)
47
+ const order = module.default.order || 0
48
+ return { extension, module, order }
49
+ } catch (e) {
50
+ console.error(`Failed to load extension module ${extension.name}:`, e)
51
+ return null
52
+ }
53
+ }))
54
+
55
+ // sort modules by order
56
+ ctx.modules.sort((a, b) => a.order - b.order)
57
+
58
+ const installedModules = []
59
+
60
+ // Install built-in modules sequentially
61
+ Object.entries(BuiltInModules).forEach(([name, module]) => {
62
+ try {
63
+ module.install(ctx)
64
+ installedModules.push({ extension: { id: name }, module: { default: module } })
65
+ console.log(`Installed built-in: ${name}`)
66
+ } catch (e) {
67
+ console.error(`Failed to install built-in ${name}:`, e)
68
+ }
69
+ })
70
+
71
+ // Install extensions sequentially
72
+ for (const result of ctx.modules) {
73
+ if (result && result.module.default && result.module.default.install) {
74
+ try {
75
+ result.module.default.install(ctx)
76
+ installedModules.push(result)
77
+ console.log(`Installed extension: ${result.extension.id}`)
78
+ } catch (e) {
79
+ console.error(`Failed to install extension ${result.extension.id}:`, e)
80
+ }
81
+ }
82
+ }
83
+
84
+ // Register all components with Vue
85
+ Object.entries(ctx._components).forEach(([name, component]) => {
86
+ app.component(name, component)
87
+ })
88
+
89
+ // Add fallback route and create router
90
+ routes.push({ path: '/:fallback(.*)*', component: ctx.component('Home') })
91
+ routes.forEach(r => r.path = ai.base + r.path)
92
+ ctx.router = createRouter({
93
+ history: createWebHistory(),
94
+ routes,
95
+ })
96
+ app.use(ctx.router)
97
+
98
+ ctx.router.beforeEach((to, from) => {
99
+ const title = to.meta.title || 'Chat'
100
+ console.debug('router:change', to.path, title)
101
+ ctx.setLayout({ path: to.path })
102
+ ctx.setState({ title })
103
+ document.title = title
104
+ return true
105
+ })
106
+ ctx._onRouterBeforeEach.forEach(ctx.router.beforeEach)
107
+
108
+ if (ai.hasAccess) {
109
+ if (ctx.layout.path && location.pathname === '/' && !location.search) {
110
+ console.log('redirecting to saved path: ', ctx.layout.path)
111
+ ctx.router.push({ path: ctx.layout.path })
112
+ }
113
+ } else {
114
+ ctx.router.push({ path: '/' })
115
+ }
116
+
117
+ const loadModules = installedModules.filter(x => x.module.default && x.module.default.load)
118
+ console.log('Loading modules: ', loadModules.map(x => x.extension.id))
119
+
120
+ // Load all extensions in parallel
121
+ await Promise.all(loadModules.map(async result => {
122
+ try {
123
+ await result.module.default.load(ctx)
124
+ console.log(`Loaded extension: ${result.extension.id}`)
125
+ } catch (e) {
126
+ console.error(`Failed to load extension ${result.extension.id}:`, e)
127
+ }
128
+ }))
129
+
130
+ return ctx
131
+ }