llms-py 3.0.0b2__py3-none-any.whl → 3.0.0b3__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 +1 -4962
  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} +56 -133
  28. llms/ui/{SettingsDialog.mjs → modules/chat/SettingsDialog.mjs} +8 -8
  29. llms/ui/{ChatPrompt.mjs → modules/chat/index.mjs} +239 -45
  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.0b3.dist-info}/METADATA +1 -1
  37. llms_py-3.0.0b3.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.0b3.dist-info}/WHEEL +0 -0
  49. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b3.dist-info}/entry_points.txt +0 -0
  50. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b3.dist-info}/licenses/LICENSE +0 -0
  51. {llms_py-3.0.0b2.dist-info → llms_py-3.0.0b3.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,6 @@
1
1
  import { ref, onMounted, watch, inject } from 'vue'
2
2
  import { useRouter, useRoute } from 'vue-router'
3
3
  import { useThreadStore } from './threadStore.mjs'
4
- import { renderMarkdown } from './markdown.mjs'
5
4
 
6
5
  const RecentResults = {
7
6
  template: `
@@ -140,7 +139,6 @@ const RecentResults = {
140
139
  snippet,
141
140
  open,
142
141
  formatDate,
143
- renderMarkdown,
144
142
  onScroll,
145
143
  }
146
144
  }
@@ -1,11 +1,7 @@
1
1
  import { onMounted, inject } from 'vue'
2
2
  import { useRouter } from 'vue-router'
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'
7
-
8
- const { humanifyNumber, humanifyMs } = useFormatters()
3
+ import ThreadStore from './threadStore.mjs'
4
+ import Recents from './Recents.mjs'
9
5
 
10
6
  // Thread Item Component
11
7
  const ThreadItem = {
@@ -21,10 +17,10 @@ const ThreadItem = {
21
17
  {{ thread.title }}
22
18
  </div>
23
19
  <div class="text-xs text-gray-500 dark:text-gray-400 truncate">
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) : '' }}
20
+ <span>{{ $fmt.relativeTime(thread.updatedAt) }} • {{ thread.messages.length }} msgs</span>
21
+ <span v-if="thread.stats?.inputTokens" :title="$fmt.statsTitle(thread.stats)">
22
+ &#8226; {{ $fmt.humanifyNumber(thread.stats.inputTokens + thread.stats.outputTokens) }} toks
23
+ {{ thread.stats.cost ? ' ' + $fmt.cost(thread.stats.cost) : '' }}
28
24
  </span>
29
25
  </div>
30
26
  <div v-if="thread.model" class="text-xs text-blue-600 dark:text-blue-400 truncate">
@@ -60,32 +56,12 @@ const ThreadItem = {
60
56
  emits: ['select', 'delete'],
61
57
 
62
58
  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
-
76
59
  return {
77
- formatRelativeTime,
78
- humanifyNumber,
79
- statsTitle,
80
- formatCost,
81
60
  }
82
61
  }
83
62
  }
84
63
 
85
64
  const GroupedThreads = {
86
- components: {
87
- ThreadItem,
88
- },
89
65
  template: `
90
66
  <!-- Today -->
91
67
  <div v-if="groupedThreads.today.length > 0" class="mb-4">
@@ -156,15 +132,10 @@ const GroupedThreads = {
156
132
  emits: ['select', 'delete'],
157
133
  }
158
134
 
159
- const Sidebar = {
160
- components: {
161
- Brand,
162
- GroupedThreads,
163
- ThreadItem,
164
- },
135
+ const ThreadsSidebar = {
165
136
  template: `
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')" />
137
+ <div class="flex flex-col h-full">
138
+ <Brand @home="goToInitialState" @toggle-sidebar="$emit('toggle-sidebar')" />
168
139
  <!-- Thread List -->
169
140
  <div class="flex-1 overflow-y-auto">
170
141
  <div v-if="isLoading" class="p-4 text-center text-gray-500 dark:text-gray-400">
@@ -180,8 +151,19 @@ const Sidebar = {
180
151
  <p class="text-xs text-gray-400 dark:text-gray-500 mt-1">Start a new chat to begin</p>
181
152
  </div>
182
153
 
183
- <div v-else class="py-2">
184
- <GroupedThreads :currentThread="currentThread" :groupedThreads="threadStore.getGroupedThreads(18)"
154
+ <div v-else class="relative py-2">
155
+
156
+ <div class="flex items-center space-x-2 absolute top-2 right-2">
157
+ <button type="button"
158
+ @click="createNewThread"
159
+ class="text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
160
+ title="New Chat"
161
+ >
162
+ <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>
163
+ </button>
164
+ </div>
165
+
166
+ <GroupedThreads :currentThread="currentThread" :groupedThreads="$threads.getGroupedThreads(50)"
185
167
  @select="selectThread" @delete="deleteThread" />
186
168
  </div>
187
169
  </div>
@@ -192,7 +174,6 @@ const Sidebar = {
192
174
  const ctx = inject('ctx')
193
175
  const ai = ctx.ai
194
176
  const router = useRouter()
195
- const threadStore = useThreadStore()
196
177
  const {
197
178
  threads,
198
179
  currentThread,
@@ -202,7 +183,7 @@ const Sidebar = {
202
183
  createThread,
203
184
  deleteThread: deleteThreadFromStore,
204
185
  clearCurrentThread
205
- } = threadStore
186
+ } = ctx.threads
206
187
 
207
188
  onMounted(async () => {
208
189
  await loadThreads()
@@ -241,7 +222,6 @@ const Sidebar = {
241
222
  }
242
223
 
243
224
  return {
244
- threadStore,
245
225
  threads,
246
226
  currentThread,
247
227
  isLoading,
@@ -255,4 +235,26 @@ const Sidebar = {
255
235
  }
256
236
  }
257
237
 
258
- export default Sidebar
238
+ export default {
239
+ install(ctx) {
240
+ ctx.components({
241
+ ThreadsSidebar,
242
+ ThreadItem,
243
+ GroupedThreads,
244
+ Recents,
245
+ })
246
+ ctx.routes.push(...[
247
+ { path: '/recents', component: Recents },
248
+ ])
249
+ ThreadStore.install(ctx)
250
+
251
+ ctx.setLayout({
252
+ left: 'ThreadsSidebar',
253
+ })
254
+ },
255
+
256
+ async load(ctx) {
257
+ const { initDB } = ctx.threads
258
+ await initDB()
259
+ }
260
+ }
@@ -1,6 +1,6 @@
1
1
  import { ref, computed, unref } from 'vue'
2
2
  import { openDB } from 'idb'
3
- import { nextId, toModelInfo } from './utils.mjs'
3
+ import { nextId } from '../../utils.mjs'
4
4
 
5
5
  // Thread store for managing chat threads with IndexedDB
6
6
  const threads = ref([])
@@ -10,12 +10,6 @@ const isLoading = ref(false)
10
10
  let db = null
11
11
  let ctx = null
12
12
 
13
- export default {
14
- install(context) {
15
- ctx = context
16
- }
17
- }
18
-
19
13
  // Initialize IndexedDB
20
14
  async function initDB() {
21
15
  if (db) return db
@@ -581,3 +575,12 @@ export function useThreadStore() {
581
575
  getAllThreadIds,
582
576
  }
583
577
  }
578
+
579
+ export default {
580
+ install(context) {
581
+ ctx = context
582
+ ctx.setGlobals({
583
+ threads: useThreadStore()
584
+ })
585
+ }
586
+ }
llms/ui/utils.mjs CHANGED
@@ -1,37 +1,4 @@
1
- import { $$, createElement, rightPart, pick } from "@servicestack/client"
2
-
3
- const cacheUrlInfo = {}
4
-
5
- export function getCacheInfo(url) {
6
- return cacheUrlInfo[url]
7
- }
8
- export async function fetchCacheInfos(urls) {
9
- const infos = {}
10
- const fetchInfos = []
11
- for (const url of urls) {
12
- const info = getCacheInfo(url)
13
- if (info) {
14
- infos[url] = info
15
- } else {
16
- fetchInfos.push(fetch(url + "?info"))
17
- }
18
- }
19
- const responses = await Promise.all(fetchInfos)
20
- for (let i = 0; i < urls.length; i++) {
21
- try {
22
- const info = await responses[i].json()
23
- setCacheInfo(urls[i], info)
24
- infos[urls[i]] = info
25
- } catch (e) {
26
- console.error('Failed to fetch info for', urls[i], e)
27
- }
28
- }
29
- return infos
30
- }
31
-
32
- export function setCacheInfo(url, info) {
33
- cacheUrlInfo[url] = info
34
- }
1
+ import { rightPart } from "@servicestack/client"
35
2
 
36
3
  export function toJsonArray(json) {
37
4
  try {
@@ -83,19 +50,6 @@ export function fileToDataUri(file) {
83
50
  })
84
51
  }
85
52
 
86
- export async function uploadFile(file) {
87
- const formData = new FormData()
88
- formData.append('file', file)
89
- const response = await fetch('/upload', {
90
- method: 'POST',
91
- body: formData
92
- })
93
- if (!response.ok) {
94
- throw new Error(`Upload failed: ${response.statusText}`)
95
- }
96
- return response.json()
97
- }
98
-
99
53
  export function serializedClone(obj) {
100
54
  try {
101
55
  return JSON.parse(JSON.stringify(obj))
@@ -105,6 +59,7 @@ export function serializedClone(obj) {
105
59
  }
106
60
  }
107
61
 
62
+
108
63
  export function deepClone(o) {
109
64
  if (o === null || typeof o !== 'object') return o
110
65
 
@@ -126,94 +81,82 @@ export function deepClone(o) {
126
81
  return serializedClone(o)
127
82
  }
128
83
 
129
- export function toModelInfo(model) {
130
- if (!model) return undefined
131
- const { id, name, provider, cost, modalities } = model
132
- return deepClone({ id, name, provider, cost, modalities })
133
- }
84
+ const currFmt2 = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 })
85
+ const currFmt6 = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 6 })
134
86
 
135
- const numFmt = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 6 })
136
- export function tokenCost(price) {
87
+ export function tokenCost(price, tokens = 1) {
137
88
  if (!price) return ''
138
- var ret = numFmt.format(parseFloat(price))
89
+ var ret = currFmt2.format(parseFloat(price) * tokens)
139
90
  return ret.endsWith('.00') ? ret.slice(0, -3) : ret
140
91
  }
92
+ export function tokenCostLong(price, tokens = 1) {
93
+ if (!price) return ''
94
+ const ret = currFmt6.format(parseFloat(price) * tokens)
95
+ return ret.endsWith('.000000') ? ret.slice(0, -7) : ret
96
+ }
141
97
  export function formatCost(cost) {
142
98
  if (!cost) return ''
143
- return numFmt.format(parseFloat(cost))
99
+ return currFmt2.format(parseFloat(cost))
144
100
  }
145
- export function statsTitle(stats) {
146
- let title = []
147
- // Each stat on its own line
148
- if (stats.cost) {
149
- title.push(`Total Cost: ${formatCost(stats.cost)}`)
150
- }
151
- if (stats.inputTokens) {
152
- title.push(`Input Tokens: ${stats.inputTokens}`)
153
- }
154
- if (stats.outputTokens) {
155
- title.push(`Output Tokens: ${stats.outputTokens}`)
101
+
102
+ // Accessible in views via $fmt
103
+ export function utilsFormatters() {
104
+ function relativeTime(timestamp) {
105
+ const now = new Date()
106
+ const date = new Date(timestamp)
107
+ const diffInSeconds = Math.floor((now - date) / 1000)
108
+
109
+ if (diffInSeconds < 60) return 'Just now'
110
+ if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`
111
+ if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`
112
+ if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`
113
+
114
+ return date.toLocaleDateString()
156
115
  }
157
- if (stats.requests) {
158
- title.push(`Requests: ${stats.requests}`)
116
+ function costLong(cost) {
117
+ if (!cost) return ''
118
+ const ret = currFmt6.format(parseFloat(cost))
119
+ return ret.endsWith('.000000') ? ret.slice(0, -7) : ret
159
120
  }
160
- if (stats.duration) {
161
- title.push(`Duration: ${stats.duration}ms`)
121
+ function statsTitle(stats) {
122
+ let title = []
123
+ // Each stat on its own line
124
+ if (stats.cost) {
125
+ title.push(`Total Cost: ${costLong(stats.cost)}`)
126
+ }
127
+ if (stats.inputTokens) {
128
+ title.push(`Input Tokens: ${stats.inputTokens}`)
129
+ }
130
+ if (stats.outputTokens) {
131
+ title.push(`Output Tokens: ${stats.outputTokens}`)
132
+ }
133
+ if (stats.requests) {
134
+ title.push(`Requests: ${stats.requests}`)
135
+ }
136
+ if (stats.duration) {
137
+ title.push(`Duration: ${stats.duration}ms`)
138
+ }
139
+ return title.join('\n')
162
140
  }
163
- return title.join('\n')
164
- }
165
-
166
- const svg = {
167
- clipboard: `<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none"><path d="M8 5H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1M8 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M8 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m0 0h2a2 2 0 0 1 2 2v3m2 4H10m0 0l3-3m-3 3l3 3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`,
168
- check: `<svg class="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>`,
169
- }
170
141
 
171
- function copyBlock(btn) {
172
- // console.log('copyBlock',btn)
173
- const label = btn.previousElementSibling
174
- const code = btn.parentElement.nextElementSibling
175
- label.classList.remove('hidden')
176
- label.innerHTML = 'copied'
177
- btn.classList.add('border-gray-600', 'bg-gray-700')
178
- btn.classList.remove('border-gray-700')
179
- btn.innerHTML = svg.check
180
- navigator.clipboard.writeText(code.innerText)
181
- setTimeout(() => {
182
- label.classList.add('hidden')
183
- label.innerHTML = ''
184
- btn.innerHTML = svg.clipboard
185
- btn.classList.remove('border-gray-600', 'bg-gray-700')
186
- btn.classList.add('border-gray-700')
187
- }, 2000)
188
- }
189
-
190
- export function addCopyButtonToCodeBlocks(sel) {
191
- globalThis.copyBlock ??= copyBlock
192
- //console.log('addCopyButtonToCodeBlocks', sel, [...$$(sel)].length)
193
-
194
- $$(sel).forEach(code => {
195
- let pre = code.parentElement;
196
- if (pre.classList.contains('group')) return
197
- pre.classList.add('relative', 'group')
198
-
199
- const div = createElement('div', { attrs: { className: 'opacity-0 group-hover:opacity-100 transition-opacity duration-100 flex absolute right-2 -mt-1 select-none' } })
200
- const label = createElement('div', { attrs: { className: 'hidden font-sans p-1 px-2 mr-1 rounded-md border border-gray-600 bg-gray-700 text-gray-400' } })
201
- const btn = createElement('button', {
202
- attrs: {
203
- type: 'button',
204
- className: 'p-1 rounded-md border block text-gray-500 hover:text-gray-400 border-gray-700 hover:border-gray-600',
205
- onclick: 'copyBlock(this)'
206
- }
142
+ function time(timestamp) {
143
+ return new Date(timestamp).toLocaleTimeString([], {
144
+ hour: '2-digit',
145
+ minute: '2-digit'
207
146
  })
208
- btn.innerHTML = svg.clipboard
209
- div.appendChild(label)
210
- div.appendChild(btn)
211
- pre.insertBefore(div, code)
212
- })
213
- }
147
+ }
148
+
214
149
 
215
- export function addCopyButtons() {
216
- addCopyButtonToCodeBlocks('.prose pre>code')
150
+ return {
151
+ currFmt: currFmt2,
152
+ tokenCost,
153
+ tokenCostLong,
154
+ cost: formatCost,
155
+ costLong,
156
+ statsTitle,
157
+ relativeTime,
158
+ time,
159
+ }
217
160
  }
218
161
 
219
162
  /**
@@ -226,4 +169,20 @@ export const nextId = (() => {
226
169
  last = (now > last) ? now : last + 1
227
170
  return last
228
171
  }
229
- })();
172
+ })();
173
+
174
+ export function utilsFunctions() {
175
+ return {
176
+ nextId,
177
+ deepClone,
178
+ toJsonArray,
179
+ toJsonObject,
180
+ storageArray,
181
+ storageObject,
182
+ fileToBase64,
183
+ fileToDataUri,
184
+ serializedClone,
185
+ deepClone,
186
+ }
187
+ }
188
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 3.0.0b2
3
+ Version: 3.0.0b3
4
4
  Summary: A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers
5
5
  Home-page: https://github.com/ServiceStack/llms
6
6
  Author: ServiceStack
@@ -0,0 +1,65 @@
1
+ llms/__init__.py,sha256=DKwTZDsyYL_wHe7yvLw49Nf8PSgPSyWaeVdotUqSvrQ,84
2
+ llms/__main__.py,sha256=hrBulHIt3lmPm1BCyAEVtB6DQ0Hvc3gnIddhHCmJasg,151
3
+ llms/index.html,sha256=gABCGg8ALHyB8OSH745HXx7U0SrXuNtJry9buUbVDik,2117
4
+ llms/llms.json,sha256=j7CmFdMpzNeo0nCPHHR4djYfzH_AFKKYIAH3cL3TKT8,10641
5
+ llms/main.py,sha256=7Ih99-KuA6fBmrytED6099YFORUVDARWXav2p4bLDOk,124887
6
+ llms/providers-extra.json,sha256=w7_5gB0YUPK0PJNeViM7vRDfNGChXUKMHfGHenVxEkM,10165
7
+ llms/providers.json,sha256=x9Y_17h0aqeFMn3pnbjcZ5SW-RJyxHXH8ZjsUQ3L8aA,253843
8
+ llms/__pycache__/__init__.cpython-312.pyc,sha256=lg2oFc0aKgj536NOJxcIpbCpEWi47ptF8NufPphgUUk,204
9
+ llms/__pycache__/__init__.cpython-313.pyc,sha256=DvmykIYAjV4Xjv5NeC70BA0JIX8qtwEWP2dfzmOBrps,173
10
+ llms/__pycache__/__init__.cpython-314.pyc,sha256=9CxeBz41D_-ezno6pIOsnoMAFzdRX2uZjKkBLUm25RQ,206
11
+ llms/__pycache__/__main__.cpython-312.pyc,sha256=fcu9LVl5nfdSzg9HqoKyDGoDvENeIXKkjygN7FENEGE,321
12
+ llms/__pycache__/__main__.cpython-314.pyc,sha256=IFxtGVpJq_3whKuM5Ln7YMweKFNbHVp8M7VSrLK5h2A,324
13
+ llms/__pycache__/llms.cpython-312.pyc,sha256=S5dFI79JdUe2dQW4ogdB-CCNhudQeFaFGcfKxgJGBms,72080
14
+ llms/__pycache__/main.cpython-312.pyc,sha256=HrqApYAiiBmYN73HIx_Hl2-Xm1Gy7I_0zuR5j86qoRM,104940
15
+ llms/__pycache__/main.cpython-313.pyc,sha256=6NQ__SJ2rC9ItFLKLHL5ewb5RqxLzZabwgczA9wZd-w,74814
16
+ llms/__pycache__/main.cpython-314.pyc,sha256=MYiVCsD1BdoR2lyKK-jmGtT0Y9O_3E7rfPaqJp04CnI,161849
17
+ llms/__pycache__/plugins.cpython-314.pyc,sha256=fer8nTkidG_vQSx80tL2bAvMS0opDom93bewjseFcyg,3560
18
+ llms/providers/anthropic.py,sha256=KBwIPkGFkNCJxlLAY5MzFpEj7AB4d0WwpaOfZfvCrk0,8091
19
+ llms/providers/chutes.py,sha256=TTDfWviHOoNfk2cXnY5UJK_fciRJ2drfz48u67TDmCM,6171
20
+ llms/providers/google.py,sha256=aK9t7iPLmziUx7q6blF_G2MQUHFA1h8t461euxWWtVc,15104
21
+ llms/providers/nvidia.py,sha256=HRcI1-fgMGq5_9s6br9YHVVOIaXRtOGCgUPSCHXB-_0,4355
22
+ llms/providers/openai.py,sha256=zQqxXyF9v7CplmzebFIXd-Xfj7AcP_bIK6G8fPZh2p8,6355
23
+ llms/providers/openrouter.py,sha256=unXvR2KMsosWJe8_7k-JhDM8V_XtVUtnNO2psgtkTNk,3055
24
+ llms/providers/__pycache__/anthropic.cpython-314.pyc,sha256=tRLB3xJoPi0dLXY85JgXcD_DHIwhBcScx08NiHTiwdw,9158
25
+ llms/providers/__pycache__/chutes.cpython-314.pyc,sha256=TFAoyWV3rwvmcVi17ACgdqPY0Vh_kITFJ-IOGAtZp8w,7404
26
+ llms/providers/__pycache__/google.cpython-314.pyc,sha256=mprVq3i-Ma9La9EgUcUh7AXUaPWTBn6STM-w2LZN2Tc,13858
27
+ llms/providers/__pycache__/nvidia.cpython-314.pyc,sha256=pE2P5sz0EjRp3DYy-MiEAf_kq3Ns03Q0f-DQGa8UOXQ,6155
28
+ llms/providers/__pycache__/openai.cpython-314.pyc,sha256=-OnbDtGalUx3NtRU_sAwRL_TvrnKOworTliwS164IUY,8705
29
+ llms/providers/__pycache__/openrouter.cpython-314.pyc,sha256=5zAegKxy40P1uX87T9KhaE5s54UXSq9fjsG0i2n84tc,5078
30
+ llms/ui/App.mjs,sha256=W_rDLlK26t-GbKxSxh5X8727FDaOt7_rPTWnaVw7Cq0,7174
31
+ llms/ui/ai.mjs,sha256=sOzBWe2wpNC7jNI4qZJUp7PsyoT0Wj2awJ2L0nfVvDA,5315
32
+ llms/ui/app.css,sha256=11ENCAUhHgC6wuzHccryCaQseZEOjpdh-c_kLDdIAIo,84747
33
+ llms/ui/ctx.mjs,sha256=p-GEB9rBlU5Vag84dDN0b-4YUJ0jBxqJQPEVGVarj9E,6134
34
+ llms/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
35
+ llms/ui/index.mjs,sha256=J5g0JXuKeMwS65xr6mMivqUeEtojcQV2hsp5QCqcI4U,3820
36
+ llms/ui/markdown.mjs,sha256=WcYnT7xZFOc-TXbghVGSBjQJdLGTXq9vZ1LEIaYdcao,6611
37
+ llms/ui/tailwind.input.css,sha256=5PiDdc5nyPE9Fheg_PgLnz3RR3C0o5x6M3M-KWVFo-4,14403
38
+ llms/ui/typography.css,sha256=6o7pbMIamRVlm2GfzSStpcOG4T5eFCK_WcQ3RIHKAsU,19587
39
+ llms/ui/utils.mjs,sha256=RN7vYsN-rlaVUzIY91yly9seCeA9V9dtXmEtKzcb9Bs,5276
40
+ llms/ui/lib/chart.js,sha256=dx8FdDX0Rv6OZtZjr9FQh5h-twFsKjfnb-FvFlQ--cU,196176
41
+ llms/ui/lib/charts.mjs,sha256=OPFAifcA4MPRv8pw6I7glVh7Xccv8guio0k35zPBDIY,926
42
+ llms/ui/lib/color.js,sha256=DDG7Pr-qzJHTPISZNSqP_qJR8UflKHEc_56n6xrBugQ,8273
43
+ llms/ui/lib/highlight.min.mjs,sha256=sG7wq8bF-IKkfie7S4QSyh5DdHBRf0NqQxMOEH8-MT0,127458
44
+ llms/ui/lib/idb.min.mjs,sha256=CeTXyV4I_pB5vnibvJuyXdMs0iVF2ZL0Z7cdm3w_QaI,3853
45
+ llms/ui/lib/marked.min.mjs,sha256=QRHb_VZugcBJRD2EP6gYlVFEsJw5C2fQ8ImMf_pA2_s,39488
46
+ llms/ui/lib/servicestack-client.mjs,sha256=UVafVbzhJ_0N2lzv7rlzIbzwnWpoqXxGk3N3FSKgOOc,54534
47
+ llms/ui/lib/servicestack-vue.mjs,sha256=unTA8lM0tKy2PwZiJ8UEvrTuGmei8jNZnmmuQ5MKyV4,216753
48
+ llms/ui/lib/vue-router.min.mjs,sha256=fR30GHoXI1u81zyZ26YEU105pZgbbAKSXbpnzFKIxls,30418
49
+ llms/ui/lib/vue.min.mjs,sha256=iXh97m5hotl0eFllb3aoasQTImvp7mQoRJ_0HoxmZkw,163811
50
+ llms/ui/lib/vue.mjs,sha256=dS8LKOG01t9CvZ04i0tbFXHqFXOO_Ha4NmM3BytjQAs,537071
51
+ llms/ui/modules/analytics.mjs,sha256=ZV5ybl4jnzRtHMV04V3OIX-HgiHd2aby3IHzEemkucs,73909
52
+ llms/ui/modules/layout.mjs,sha256=WTy85_8rSyBQooo2f5baw98RzNhpecw4pDpfhEgKv4s,12105
53
+ llms/ui/modules/model-selector.mjs,sha256=dekgCdaqoWX_9fnpI8mhd6lU25JV7vxBoVQVVuF2p-I,67474
54
+ llms/ui/modules/chat/ChatBody.mjs,sha256=GzHimS-haJPF7oqcqViTlOLWgardbetfNGMarTXBWxI,43368
55
+ llms/ui/modules/chat/SettingsDialog.mjs,sha256=N_Xnd9eXkXuw8cVaKR77k16TgOitB9btInW8Qp-WK7U,19929
56
+ llms/ui/modules/chat/index.mjs,sha256=wQWTo54kGRcA68wCjSY-VOPheQTGlxmAOn-LGR76Qnk,33765
57
+ llms/ui/modules/threads/Recents.mjs,sha256=xiN5K9SYc0oJug60m6g_lPVw-75lNkp1ATuVUYgIgX8,8826
58
+ llms/ui/modules/threads/index.mjs,sha256=0ZwVb6W2BT3mwK5bwKgtlJEGJ9IAnQr7qk3XNG1LnFU,11397
59
+ llms/ui/modules/threads/threadStore.mjs,sha256=cGm6g4dn5q0u_K6Bu5xuoAmBuURquubK7R4ahSKNC_E,16886
60
+ llms_py-3.0.0b3.dist-info/licenses/LICENSE,sha256=bus9cuAOWeYqBk2OuhSABVV1P4z7hgrEFISpyda_H5w,1532
61
+ llms_py-3.0.0b3.dist-info/METADATA,sha256=1iKo8N7Owhqbsr3nemrVRlcmZl07jLlkQtT-Q2vTzFw,2193
62
+ llms_py-3.0.0b3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
+ llms_py-3.0.0b3.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
64
+ llms_py-3.0.0b3.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
65
+ llms_py-3.0.0b3.dist-info/RECORD,,
llms/ui/Avatar.mjs DELETED
@@ -1,86 +0,0 @@
1
- import { computed, inject, ref, onMounted, onUnmounted } from "vue"
2
-
3
- export default {
4
- template: `
5
- <div v-if="$ai.auth?.profileUrl" class="relative" ref="avatarContainer">
6
- <img
7
- @click.stop="toggleMenu"
8
- :src="$ai.auth.profileUrl"
9
- :title="authTitle"
10
- class="size-8 rounded-full cursor-pointer hover:ring-2 hover:ring-gray-300"
11
- />
12
- <div
13
- v-if="showMenu"
14
- @click.stop
15
- 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"
16
- >
17
- <div class="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 border-b border-gray-200 dark:border-gray-700">
18
- <div class="font-medium whitespace-nowrap overflow-hidden text-ellipsis">{{ $ai.auth.displayName || $ai.auth.userName }}</div>
19
- <div class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap overflow-hidden text-ellipsis">{{ $ai.auth.email }}</div>
20
- </div>
21
- <button type="button"
22
- @click="handleLogout"
23
- 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"
24
- >
25
- <svg class="w-4 h-4 mr-2 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
26
- <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>
27
- </svg>
28
- Sign Out
29
- </button>
30
- </div>
31
- </div>
32
- `,
33
- setup() {
34
- const ctx = inject('ctx')
35
- const ai = ctx.ai
36
- const showMenu = ref(false)
37
- const avatarContainer = ref(null)
38
-
39
- const authTitle = computed(() => {
40
- if (!ai.auth) return ''
41
- const { userId, userName, displayName, bearerToken, roles } = ai.auth
42
- const name = userName || displayName
43
- const prefix = roles && roles.includes('Admin') ? 'Admin' : 'Name'
44
- const sb = [
45
- name ? `${prefix}: ${name}` : '',
46
- `API Key: ${bearerToken}`,
47
- `${userId}`,
48
- ]
49
- return sb.filter(x => x).join('\n')
50
- })
51
-
52
- function toggleMenu() {
53
- showMenu.value = !showMenu.value
54
- }
55
-
56
- async function handleLogout() {
57
- showMenu.value = false
58
- await ai.signOut()
59
- // Reload the page to show sign-in screen
60
- window.location.reload()
61
- }
62
-
63
- // Close menu when clicking outside
64
- const handleClickOutside = (event) => {
65
- if (showMenu.value && avatarContainer.value && !avatarContainer.value.contains(event.target)) {
66
- showMenu.value = false
67
- }
68
- }
69
-
70
- onMounted(() => {
71
- document.addEventListener('click', handleClickOutside)
72
- })
73
-
74
- onUnmounted(() => {
75
- document.removeEventListener('click', handleClickOutside)
76
- })
77
-
78
- return {
79
- authTitle,
80
- handleLogout,
81
- showMenu,
82
- toggleMenu,
83
- avatarContainer,
84
- }
85
- }
86
- }
llms/ui/Brand.mjs DELETED
@@ -1,52 +0,0 @@
1
- export default {
2
- template:`
3
- <div class="flex-shrink-0 pl-2 pr-4 py-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 min-h-16 select-none">
4
- <div class="flex items-center justify-between">
5
- <div class="flex items-center space-x-2">
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
- <button type="button"
23
- @click="$emit('home')"
24
- 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"
25
- title="Go back to initial state"
26
- >
27
- History
28
- </button>
29
- </div>
30
-
31
- <div class="flex items-center space-x-2">
32
- <button type="button"
33
- @click="$emit('analytics')"
34
- class="text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
35
- title="Analytics"
36
- >
37
- <svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M5 22a1 1 0 0 1-1-1v-8a1 1 0 0 1 2 0v8a1 1 0 0 1-1 1m5 0a1 1 0 0 1-1-1V3a1 1 0 0 1 2 0v18a1 1 0 0 1-1 1m5 0a1 1 0 0 1-1-1V9a1 1 0 0 1 2 0v12a1 1 0 0 1-1 1m5 0a1 1 0 0 1-1-1v-4a1 1 0 0 1 2 0v4a1 1 0 0 1-1 1"/></svg>
38
- </button>
39
-
40
- <button type="button"
41
- @click="$emit('new')"
42
- class="text-gray-900 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 focus:outline-none transition-colors"
43
- title="New Chat"
44
- >
45
- <svg class="size-6" 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>
46
- </button>
47
- </div>
48
- </div>
49
- </div>
50
- `,
51
- emits:['home','new','analytics','toggle-sidebar'],
52
- }