llms-py 3.0.0b2__py3-none-any.whl → 3.0.0b4__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 (51) hide show
  1. llms/__pycache__/main.cpython-314.pyc +0 -0
  2. llms/index.html +2 -1
  3. llms/llms.json +50 -17
  4. llms/main.py +484 -544
  5. llms/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  6. llms/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  7. llms/providers/__pycache__/google.cpython-314.pyc +0 -0
  8. llms/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
  9. llms/providers/__pycache__/openai.cpython-314.pyc +0 -0
  10. llms/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  11. llms/providers/anthropic.py +189 -0
  12. llms/providers/chutes.py +152 -0
  13. llms/providers/google.py +306 -0
  14. llms/providers/nvidia.py +107 -0
  15. llms/providers/openai.py +159 -0
  16. llms/providers/openrouter.py +70 -0
  17. llms/providers-extra.json +356 -0
  18. llms/providers.json +1 -1
  19. llms/ui/App.mjs +132 -60
  20. llms/ui/ai.mjs +76 -10
  21. llms/ui/app.css +65 -28
  22. llms/ui/ctx.mjs +196 -0
  23. llms/ui/index.mjs +75 -171
  24. llms/ui/lib/charts.mjs +9 -13
  25. llms/ui/markdown.mjs +6 -0
  26. llms/ui/{Analytics.mjs → modules/analytics.mjs} +76 -64
  27. llms/ui/{Main.mjs → modules/chat/ChatBody.mjs} +59 -135
  28. llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +8 -8
  29. llms/ui/{ChatPrompt.mjs → modules/chat/index.mjs} +242 -46
  30. llms/ui/modules/layout.mjs +267 -0
  31. llms/ui/modules/model-selector.mjs +851 -0
  32. llms/ui/{Recents.mjs → modules/threads/Recents.mjs} +0 -2
  33. llms/ui/{Sidebar.mjs → modules/threads/index.mjs} +46 -44
  34. llms/ui/{threadStore.mjs → modules/threads/threadStore.mjs} +10 -7
  35. llms/ui/utils.mjs +82 -123
  36. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b4.dist-info}/METADATA +1 -1
  37. llms_py-3.0.0b4.dist-info/RECORD +65 -0
  38. llms/ui/Avatar.mjs +0 -86
  39. llms/ui/Brand.mjs +0 -52
  40. llms/ui/OAuthSignIn.mjs +0 -61
  41. llms/ui/ProviderIcon.mjs +0 -36
  42. llms/ui/ProviderStatus.mjs +0 -104
  43. llms/ui/SignIn.mjs +0 -65
  44. llms/ui/Welcome.mjs +0 -8
  45. llms/ui/model-selector.mjs +0 -686
  46. llms/ui.json +0 -1069
  47. llms_py-3.0.0b2.dist-info/RECORD +0 -58
  48. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b4.dist-info}/WHEEL +0 -0
  49. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b4.dist-info}/entry_points.txt +0 -0
  50. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b4.dist-info}/licenses/LICENSE +0 -0
  51. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,267 @@
1
+ import { computed, inject, ref, onMounted, onUnmounted } from "vue"
2
+ import { toJsonObject } from "../utils.mjs"
3
+
4
+ const ToggleSidebarButton = {
5
+ template: `
6
+ <button type="button"
7
+ @click="$emit('toggle-sidebar')"
8
+ class="group relative text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
9
+ title="Collapse sidebar"
10
+ >
11
+ <div class="relative size-5">
12
+ <!-- Default sidebar icon -->
13
+ <svg class="absolute inset-0 group-hover:hidden" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
14
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
15
+ <line x1="9" y1="3" x2="9" y2="21"></line>
16
+ </svg>
17
+ <!-- Hover state: |← icon -->
18
+ <svg class="absolute inset-0 hidden group-hover:block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m10.071 4.929l1.414 1.414L6.828 11H16v2H6.828l4.657 4.657l-1.414 1.414L3 12zM18.001 19V5h2v14z"/></svg>
19
+ </div>
20
+ </button>
21
+ `,
22
+ emits: ['toggle-sidebar'],
23
+ }
24
+
25
+ const Brand = {
26
+ template: `
27
+ <div class="flex-shrink-0 p-2 border-b border-gray-200 dark:border-gray-700 select-none">
28
+ <div class="flex items-center justify-between">
29
+ <div class="flex items-center space-x-2">
30
+ <button type="button"
31
+ @click="$router.push('/')"
32
+ class="text-lg font-semibold text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
33
+ title="Go back home"
34
+ >
35
+ {{ $state.title }}
36
+ </button>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ `,
41
+ emits: ['home', 'toggle-sidebar'],
42
+ }
43
+
44
+ const Welcome = {
45
+ template: `
46
+ <div class="mb-2 flex justify-center">
47
+ <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>
48
+ </div>
49
+ <h2 class="text-2xl font-semibold text-gray-900 dark:text-gray-100 mb-2">{{ $ai.welcome }}</h2>
50
+ `
51
+ }
52
+
53
+ const Avatar = {
54
+ template: `
55
+ <div v-if="$ai.auth?.profileUrl" class="relative" ref="avatarContainer">
56
+ <img
57
+ @click.stop="toggleMenu"
58
+ :src="$ai.auth.profileUrl"
59
+ :title="authTitle"
60
+ class="size-8 rounded-full cursor-pointer hover:ring-2 hover:ring-gray-300"
61
+ />
62
+ <div
63
+ v-if="showMenu"
64
+ @click.stop
65
+ class="absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-md shadow-lg py-1 z-50 border border-gray-200 dark:border-gray-700"
66
+ >
67
+ <div class="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 border-b border-gray-200 dark:border-gray-700">
68
+ <div class="font-medium whitespace-nowrap overflow-hidden text-ellipsis">{{ $ai.auth.displayName || $ai.auth.userName }}</div>
69
+ <div class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap overflow-hidden text-ellipsis">{{ $ai.auth.email }}</div>
70
+ </div>
71
+ <button type="button"
72
+ @click="handleLogout"
73
+ class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center whitespace-nowrap"
74
+ >
75
+ <svg class="w-4 h-4 mr-2 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
76
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
77
+ </svg>
78
+ Sign Out
79
+ </button>
80
+ </div>
81
+ </div>
82
+ `,
83
+ setup() {
84
+ const ctx = inject('ctx')
85
+ const ai = ctx.ai
86
+ const showMenu = ref(false)
87
+ const avatarContainer = ref(null)
88
+
89
+ const authTitle = computed(() => {
90
+ if (!ai.auth) return ''
91
+ const { userId, userName, displayName, bearerToken, roles } = ai.auth
92
+ const name = userName || displayName
93
+ const prefix = roles && roles.includes('Admin') ? 'Admin' : 'Name'
94
+ const sb = [
95
+ name ? `${prefix}: ${name}` : '',
96
+ `API Key: ${bearerToken}`,
97
+ `${userId}`,
98
+ ]
99
+ return sb.filter(x => x).join('\n')
100
+ })
101
+
102
+ function toggleMenu() {
103
+ showMenu.value = !showMenu.value
104
+ }
105
+
106
+ async function handleLogout() {
107
+ showMenu.value = false
108
+ await ai.signOut()
109
+ // Reload the page to show sign-in screen
110
+ window.location.reload()
111
+ }
112
+
113
+ // Close menu when clicking outside
114
+ const handleClickOutside = (event) => {
115
+ if (showMenu.value && avatarContainer.value && !avatarContainer.value.contains(event.target)) {
116
+ showMenu.value = false
117
+ }
118
+ }
119
+
120
+ onMounted(() => {
121
+ document.addEventListener('click', handleClickOutside)
122
+ })
123
+
124
+ onUnmounted(() => {
125
+ document.removeEventListener('click', handleClickOutside)
126
+ })
127
+
128
+ return {
129
+ authTitle,
130
+ handleLogout,
131
+ showMenu,
132
+ toggleMenu,
133
+ avatarContainer,
134
+ }
135
+ }
136
+ }
137
+
138
+ const OAuthSignIn = {
139
+ template: `
140
+ <div class="min-h-full -mt-36 flex flex-col justify-center sm:px-6 lg:px-8">
141
+ <div class="sm:mx-auto sm:w-full sm:max-w-md text-center">
142
+ <Welcome />
143
+ </div>
144
+ <div class="sm:mx-auto sm:w-full sm:max-w-md">
145
+ <div v-if="errorMessage" class="mb-3 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-200 rounded-lg px-4 py-3">
146
+ <div class="flex items-start space-x-2">
147
+ <div class="flex-1">
148
+ <div class="text-base font-medium">{{ errorMessage }}</div>
149
+ </div>
150
+ <button type="button"
151
+ @click="errorMessage = null"
152
+ class="text-red-400 dark:text-red-300 hover:text-red-600 dark:hover:text-red-100 flex-shrink-0"
153
+ >
154
+ <svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
155
+ <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
156
+ </svg>
157
+ </button>
158
+ </div>
159
+ </div>
160
+ <div class="py-8 px-4 sm:px-10">
161
+ <div class="space-y-4">
162
+ <button
163
+ type="button"
164
+ @click="signInWithGitHub"
165
+ class="w-full inline-flex items-center justify-center px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-base font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-colors"
166
+ >
167
+ <svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
168
+ <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" />
169
+ </svg>
170
+ Sign in with GitHub
171
+ </button>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ `,
177
+ emits: ['done'],
178
+ setup(props, { emit }) {
179
+ const errorMessage = ref(null)
180
+
181
+ function signInWithGitHub() {
182
+ // Redirect to GitHub OAuth endpoint
183
+ window.location.href = '/auth/github'
184
+ }
185
+
186
+ return {
187
+ signInWithGitHub,
188
+ errorMessage,
189
+ }
190
+ }
191
+ }
192
+
193
+ const SignIn = {
194
+ template: `
195
+ <div class="min-h-full -mt-12 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
196
+ <div class="sm:mx-auto sm:w-full sm:max-w-md">
197
+ <h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-gray-50">
198
+ Sign In
199
+ </h2>
200
+ </div>
201
+ <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
202
+ <ErrorSummary v-if="errorSummary" class="mb-3" :status="errorSummary" />
203
+ <div class="bg-white dark:bg-black py-8 px-4 shadow sm:rounded-lg sm:px-10">
204
+ <form @submit.prevent="submit">
205
+ <div class="flex flex-1 flex-col justify-between">
206
+ <div class="space-y-6">
207
+ <fieldset class="grid grid-cols-12 gap-6">
208
+ <div class="w-full col-span-12">
209
+ <TextInput id="apiKey" name="apiKey" label="API Key" v-model="apiKey" />
210
+ </div>
211
+ </fieldset>
212
+ </div>
213
+ </div>
214
+ <div class="mt-8">
215
+ <PrimaryButton class="w-full">Sign In</PrimaryButton>
216
+ </div>
217
+ </form>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ `,
222
+ emits: ['done'],
223
+ setup(props, { emit }) {
224
+ const ctx = inject('ctx')
225
+ const ai = ctx.ai
226
+ const apiKey = ref('')
227
+ const errorSummary = ref()
228
+ async function submit() {
229
+ const r = await ai.get('/auth', {
230
+ headers: {
231
+ 'Authorization': `Bearer ${apiKey.value}`
232
+ },
233
+ })
234
+ const txt = await r.text()
235
+ const json = toJsonObject(txt)
236
+ // console.log('json', json)
237
+ if (r.ok) {
238
+ json.apiKey = apiKey.value
239
+ emit('done', json)
240
+ } else {
241
+ errorSummary.value = json.responseStatus || {
242
+ errorCode: "Unauthorized",
243
+ message: 'Invalid API Key'
244
+ }
245
+ }
246
+ }
247
+
248
+ return {
249
+ apiKey,
250
+ submit,
251
+ errorSummary,
252
+ }
253
+ }
254
+ }
255
+
256
+ export default {
257
+ install(ctx) {
258
+ ctx.components({
259
+ ToggleSidebarButton,
260
+ Brand,
261
+ Welcome,
262
+ Avatar,
263
+ OAuthSignIn,
264
+ SignIn,
265
+ })
266
+ }
267
+ }