llms-py 2.0.20__py3-none-any.whl → 3.0.18__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 (207) 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 +588 -0
  6. llms/extensions/app/db.py +540 -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 +440 -0
  10. llms/extensions/computer/README.md +96 -0
  11. llms/extensions/computer/__init__.py +59 -0
  12. llms/extensions/computer/base.py +80 -0
  13. llms/extensions/computer/bash.py +185 -0
  14. llms/extensions/computer/computer.py +523 -0
  15. llms/extensions/computer/edit.py +299 -0
  16. llms/extensions/computer/filesystem.py +542 -0
  17. llms/extensions/computer/platform.py +461 -0
  18. llms/extensions/computer/run.py +37 -0
  19. llms/extensions/core_tools/CALCULATOR.md +32 -0
  20. llms/extensions/core_tools/__init__.py +599 -0
  21. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
  22. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
  23. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
  24. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
  25. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
  26. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
  27. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
  28. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
  29. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
  30. llms/extensions/core_tools/ui/codemirror/codemirror.css +344 -0
  31. llms/extensions/core_tools/ui/codemirror/codemirror.js +9884 -0
  32. llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
  33. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  34. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
  35. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
  36. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
  37. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
  38. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
  39. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
  40. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
  41. llms/extensions/core_tools/ui/index.mjs +650 -0
  42. llms/extensions/gallery/README.md +61 -0
  43. llms/extensions/gallery/__init__.py +63 -0
  44. llms/extensions/gallery/db.py +243 -0
  45. llms/extensions/gallery/ui/index.mjs +482 -0
  46. llms/extensions/katex/README.md +39 -0
  47. llms/extensions/katex/__init__.py +6 -0
  48. llms/extensions/katex/ui/README.md +125 -0
  49. llms/extensions/katex/ui/contrib/auto-render.js +338 -0
  50. llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
  51. llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
  52. llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
  53. llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
  54. llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
  55. llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
  56. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
  57. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
  58. llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
  59. llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
  60. llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
  61. llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
  62. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
  63. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
  64. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  65. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  66. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  67. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  68. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  69. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  70. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  71. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  72. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  73. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  74. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  75. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  76. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  77. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  78. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  79. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  80. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  81. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  82. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  83. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  84. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  85. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  86. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  87. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  88. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  89. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  90. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  91. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  92. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  93. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  94. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  116. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  117. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  118. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  119. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  120. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  121. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  122. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  123. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  124. llms/extensions/katex/ui/index.mjs +92 -0
  125. llms/extensions/katex/ui/katex-swap.css +1230 -0
  126. llms/extensions/katex/ui/katex-swap.min.css +1 -0
  127. llms/extensions/katex/ui/katex.css +1230 -0
  128. llms/extensions/katex/ui/katex.js +19080 -0
  129. llms/extensions/katex/ui/katex.min.css +1 -0
  130. llms/extensions/katex/ui/katex.min.js +1 -0
  131. llms/extensions/katex/ui/katex.min.mjs +1 -0
  132. llms/extensions/katex/ui/katex.mjs +18547 -0
  133. llms/extensions/providers/__init__.py +22 -0
  134. llms/extensions/providers/anthropic.py +260 -0
  135. llms/extensions/providers/cerebras.py +36 -0
  136. llms/extensions/providers/chutes.py +153 -0
  137. llms/extensions/providers/google.py +559 -0
  138. llms/extensions/providers/nvidia.py +103 -0
  139. llms/extensions/providers/openai.py +154 -0
  140. llms/extensions/providers/openrouter.py +74 -0
  141. llms/extensions/providers/zai.py +182 -0
  142. llms/extensions/skills/LICENSE +202 -0
  143. llms/extensions/skills/__init__.py +130 -0
  144. llms/extensions/skills/errors.py +25 -0
  145. llms/extensions/skills/models.py +39 -0
  146. llms/extensions/skills/parser.py +178 -0
  147. llms/extensions/skills/ui/index.mjs +376 -0
  148. llms/extensions/skills/ui/skills/create-plan/SKILL.md +74 -0
  149. llms/extensions/skills/validator.py +177 -0
  150. llms/extensions/system_prompts/README.md +22 -0
  151. llms/extensions/system_prompts/__init__.py +45 -0
  152. llms/extensions/system_prompts/ui/index.mjs +276 -0
  153. llms/extensions/system_prompts/ui/prompts.json +1067 -0
  154. llms/extensions/tools/__init__.py +67 -0
  155. llms/extensions/tools/ui/index.mjs +837 -0
  156. llms/index.html +36 -62
  157. llms/llms.json +180 -879
  158. llms/main.py +4009 -912
  159. llms/providers-extra.json +394 -0
  160. llms/providers.json +1 -0
  161. llms/ui/App.mjs +176 -8
  162. llms/ui/ai.mjs +156 -20
  163. llms/ui/app.css +3768 -321
  164. llms/ui/ctx.mjs +459 -0
  165. llms/ui/index.mjs +131 -0
  166. llms/ui/lib/chart.js +14 -0
  167. llms/ui/lib/charts.mjs +16 -0
  168. llms/ui/lib/color.js +14 -0
  169. llms/ui/lib/highlight.min.mjs +1243 -0
  170. llms/ui/lib/idb.min.mjs +8 -0
  171. llms/ui/lib/marked.min.mjs +8 -0
  172. llms/ui/lib/servicestack-client.mjs +1 -0
  173. llms/ui/lib/servicestack-vue.mjs +37 -0
  174. llms/ui/lib/vue-router.min.mjs +6 -0
  175. llms/ui/lib/vue.min.mjs +13 -0
  176. llms/ui/lib/vue.mjs +18530 -0
  177. llms/ui/markdown.mjs +25 -14
  178. llms/ui/modules/chat/ChatBody.mjs +1156 -0
  179. llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +74 -74
  180. llms/ui/modules/chat/index.mjs +995 -0
  181. llms/ui/modules/icons.mjs +46 -0
  182. llms/ui/modules/layout.mjs +271 -0
  183. llms/ui/modules/model-selector.mjs +811 -0
  184. llms/ui/tailwind.input.css +560 -78
  185. llms/ui/typography.css +54 -36
  186. llms/ui/utils.mjs +221 -92
  187. llms_py-3.0.18.dist-info/METADATA +49 -0
  188. llms_py-3.0.18.dist-info/RECORD +194 -0
  189. {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/WHEEL +1 -1
  190. {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/licenses/LICENSE +1 -2
  191. llms/ui/Avatar.mjs +0 -28
  192. llms/ui/Brand.mjs +0 -34
  193. llms/ui/ChatPrompt.mjs +0 -443
  194. llms/ui/Main.mjs +0 -740
  195. llms/ui/ModelSelector.mjs +0 -60
  196. llms/ui/ProviderIcon.mjs +0 -29
  197. llms/ui/ProviderStatus.mjs +0 -105
  198. llms/ui/SignIn.mjs +0 -64
  199. llms/ui/SystemPromptEditor.mjs +0 -31
  200. llms/ui/SystemPromptSelector.mjs +0 -36
  201. llms/ui/Welcome.mjs +0 -8
  202. llms/ui/threadStore.mjs +0 -524
  203. llms/ui.json +0 -1069
  204. llms_py-2.0.20.dist-info/METADATA +0 -931
  205. llms_py-2.0.20.dist-info/RECORD +0 -36
  206. {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/entry_points.txt +0 -0
  207. {llms_py-2.0.20.dist-info → llms_py-3.0.18.dist-info}/top_level.txt +0 -0
llms/ui/ctx.mjs ADDED
@@ -0,0 +1,459 @@
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
+ if (content && content.startsWith('---')) {
396
+ const headerEnd = content.indexOf('---', 3)
397
+ const header = content.substring(3, headerEnd).trim()
398
+ content = '<div class="frontmatter">' + header + '</div>\n' + content.substring(headerEnd + 3)
399
+ }
400
+ return this.marked.parse(content || '')
401
+ }
402
+
403
+ renderContent(content) {
404
+ // Check for HTML tags to detect HTML content
405
+ if (isHtml(content)) {
406
+ // If this is HTML content, return it in an iframe so it doesn't break the page
407
+ return `<iframe src="data:text/html;charset=utf-8,${encodeURIComponent(content)}"></iframe>`
408
+ }
409
+ return this.renderMarkdown(content)
410
+ }
411
+
412
+ createChatContext({ request, thread, context }) {
413
+ if (!request.messages) request.messages = []
414
+ if (!request.metadata) request.metadata = {}
415
+ if (!context) context = {}
416
+ Object.assign(context, {
417
+ systemPrompt: '',
418
+ requiredSystemPrompts: [],
419
+ }, context)
420
+ return {
421
+ request,
422
+ thread,
423
+ context,
424
+ }
425
+ }
426
+
427
+ completeChatContext({ request, thread, context }) {
428
+
429
+ let existingSystemPrompt = request.messages.find(m => m.role === 'system')?.content
430
+
431
+ let existingMessages = request.messages.filter(m => m.role == 'assistant' || m.role == 'tool')
432
+ if (existingMessages.length) {
433
+ const messageTypes = {}
434
+ request.messages.forEach(m => {
435
+ messageTypes[m.role] = (messageTypes[m.role] || 0) + 1
436
+ })
437
+ const summary = JSON.stringify(messageTypes).replace(/"/g, '')
438
+ console.debug(`completeChatContext(${summary})`, request)
439
+ return
440
+ }
441
+
442
+ let newSystemPrompts = context.requiredSystemPrompts ?? []
443
+ if (context.systemPrompt) {
444
+ newSystemPrompts.push(context.systemPrompt)
445
+ }
446
+ if (existingSystemPrompt) {
447
+ newSystemPrompts.push(existingSystemPrompt)
448
+ }
449
+
450
+ let newSystemPrompt = newSystemPrompts.join('\n\n')
451
+ if (newSystemPrompt) {
452
+ // add or replace system prompt
453
+ request.messages = request.messages.filter(m => m.role !== 'system')
454
+ request.messages.unshift({ role: 'system', content: newSystemPrompt })
455
+ }
456
+
457
+ console.debug(`completeChatContext()`, request)
458
+ }
459
+ }
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
+ }