llms-py 3.0.0__py3-none-any.whl → 3.0.0b2__py3-none-any.whl

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