llms-py 2.0.15__py3-none-any.whl → 2.0.17__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.
llms/ui/threadStore.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { ref, computed } from 'vue'
1
+ import { ref, computed, unref } from 'vue'
2
2
  import { openDB } from 'idb'
3
- import { nextId } from './utils.mjs'
3
+ import { nextId, toModelInfo } from './utils.mjs'
4
4
 
5
5
  // Thread store for managing chat threads with IndexedDB
6
6
  const threads = ref([])
@@ -13,18 +13,35 @@ let db = null
13
13
  async function initDB() {
14
14
  if (db) return db
15
15
 
16
- db = await openDB('LlmsThreads', 1, {
17
- upgrade(db) {
18
- // Create threads store
19
- const threadStore = db.createObjectStore('threads', {
20
- keyPath: 'id',
21
- autoIncrement: false
22
- })
23
-
24
- // Create indexes for efficient querying
25
- threadStore.createIndex('createdAt', 'createdAt')
26
- threadStore.createIndex('updatedAt', 'updatedAt')
27
- threadStore.createIndex('title', 'title')
16
+ db = await openDB('LlmsThreads', 3, {
17
+ upgrade(db, _oldVersion, _newVersion, transaction) {
18
+ if (!db.objectStoreNames.contains('threads')) {
19
+ // Create threads store
20
+ const threadStore = db.createObjectStore('threads', {
21
+ keyPath: 'id',
22
+ autoIncrement: false
23
+ })
24
+
25
+ // Create indexes for efficient querying
26
+ threadStore.createIndex('createdAt', 'createdAt')
27
+ threadStore.createIndex('updatedAt', 'updatedAt')
28
+ threadStore.createIndex('title', 'title')
29
+ }
30
+ if (!db.objectStoreNames.contains('requests')) {
31
+ // Create requests store
32
+ const requestStore = db.createObjectStore('requests', {
33
+ keyPath: 'id',
34
+ autoIncrement: false
35
+ })
36
+ requestStore.createIndex('threadId', 'threadId')
37
+ requestStore.createIndex('model', 'model')
38
+ requestStore.createIndex('provider', 'provider')
39
+ requestStore.createIndex('inputTokens', 'inputTokens')
40
+ requestStore.createIndex('outputTokens', 'outputTokens')
41
+ requestStore.createIndex('cost', 'cost')
42
+ requestStore.createIndex('duration', 'duration')
43
+ requestStore.createIndex('created', 'created')
44
+ }
28
45
  }
29
46
  })
30
47
 
@@ -36,14 +53,64 @@ function generateThreadId() {
36
53
  return Date.now().toString()
37
54
  }
38
55
 
56
+ async function logRequest(threadId, model, request, response) {
57
+ await initDB()
58
+ const metadata = response.metadata || {}
59
+ const usage = response.usage || {}
60
+ const [inputPrice, outputPrice] = metadata.pricing ? metadata.pricing.split('/') : [0, 0]
61
+ const lastUserContent = request.messages?.slice().reverse().find(m => m.role === 'user')?.content
62
+ const content = Array.isArray(lastUserContent)
63
+ ? lastUserContent.filter(c => c?.text).map(c => c.text).join(' ')
64
+ : lastUserContent
65
+ const title = content.slice(0, 100) + (content.length > 100 ? '...' : '')
66
+ const inputTokens = usage?.prompt_tokens ?? 0
67
+ const outputTokens = usage?.completion_tokens ?? 0
68
+ const inputCachedTokens = usage?.prompt_token_details?.cached_tokens ?? 0
69
+ const finishReason = response.choices[0]?.finish_reason || 'unknown'
70
+
71
+ const subtractDays = (date, days) => {
72
+ const result = new Date(date * 1000)
73
+ result.setDate(result.getDate() - days)
74
+ return parseInt(result.valueOf() / 1000)
75
+ }
76
+
77
+ const log = {
78
+ id: nextId(),
79
+ threadId: threadId,
80
+ model: model.id,
81
+ provider: model.provider,
82
+ providerModel: response.model || model.provider_model,
83
+ title,
84
+ inputTokens,
85
+ outputTokens,
86
+ inputCachedTokens,
87
+ totalTokens: usage.total_tokens ?? (inputTokens + outputTokens),
88
+ inputPrice,
89
+ outputPrice,
90
+ cost: (parseFloat(inputPrice) * inputTokens) + (parseFloat(outputPrice) * outputTokens),
91
+ duration: metadata.duration ?? 0,
92
+ created: response.created ?? Math.floor(Date.now() / 1000),
93
+ finishReason,
94
+ providerRef: response.provider,
95
+ ref: response.id || undefined,
96
+ usage: usage,
97
+ }
98
+ console.debug('logRequest', log)
99
+ const tx = db.transaction(['requests'], 'readwrite')
100
+ await tx.objectStore('requests').add(log)
101
+ await tx.complete
102
+ return log
103
+ }
104
+
39
105
  // Create a new thread
40
- async function createThread(title = 'New Chat', model = '', systemPrompt = '') {
106
+ async function createThread(title = 'New Chat', model = null, systemPrompt = '') {
41
107
  await initDB()
42
-
108
+
43
109
  const thread = {
44
110
  id: generateThreadId(),
45
111
  title: title,
46
- model: model,
112
+ model: model?.id ?? '',
113
+ info: toModelInfo(model),
47
114
  systemPrompt: systemPrompt,
48
115
  messages: [],
49
116
  createdAt: new Date().toISOString(),
@@ -92,8 +159,38 @@ async function updateThread(threadId, updates) {
92
159
  return updatedThread
93
160
  }
94
161
 
162
+ async function calculateThreadStats(threadId) {
163
+ await initDB()
164
+
165
+ const tx = db.transaction(['requests'], 'readonly')
166
+ const store = tx.objectStore('requests')
167
+ const index = store.index('threadId')
168
+
169
+ const requests = await index.getAll(threadId)
170
+
171
+ let inputTokens = 0
172
+ let outputTokens = 0
173
+ let cost = 0.0
174
+ let duration = 0
175
+
176
+ requests.forEach(req => {
177
+ inputTokens += req.inputTokens || 0
178
+ outputTokens += req.outputTokens || 0
179
+ cost += req.cost || 0.0
180
+ duration += req.duration || 0
181
+ })
182
+
183
+ return {
184
+ inputTokens,
185
+ outputTokens,
186
+ cost,
187
+ duration,
188
+ requests: requests.length
189
+ }
190
+ }
191
+
95
192
  // Add message to thread
96
- async function addMessageToThread(threadId, message) {
193
+ async function addMessageToThread(threadId, message, usage) {
97
194
  const thread = await getThread(threadId)
98
195
  if (!thread) throw new Error('Thread not found')
99
196
 
@@ -103,6 +200,24 @@ async function addMessageToThread(threadId, message) {
103
200
  ...message
104
201
  }
105
202
 
203
+ // Add input and output token usage to previous 'input' message
204
+ if (usage?.prompt_tokens != null) {
205
+ const lastMessage = thread.messages[thread.messages.length - 1]
206
+ if (lastMessage && lastMessage.role === 'user') {
207
+ lastMessage.usage = {
208
+ tokens: parseInt(usage.prompt_tokens),
209
+ price: usage.input || '0',
210
+ }
211
+ }
212
+ }
213
+ if (usage?.completion_tokens != null) {
214
+ newMessage.usage = {
215
+ tokens: parseInt(usage.completion_tokens),
216
+ price: usage.output || '0',
217
+ duration: usage.duration || undefined,
218
+ }
219
+ }
220
+
106
221
  const updatedMessages = [...thread.messages, newMessage]
107
222
 
108
223
  // Auto-generate title from first user message if still "New Chat"
@@ -110,10 +225,13 @@ async function addMessageToThread(threadId, message) {
110
225
  if (title === 'New Chat' && message.role === 'user' && updatedMessages.length <= 2) {
111
226
  title = message.content.slice(0, 200) + (message.content.length > 200 ? '...' : '')
112
227
  }
228
+
229
+ const stats = await calculateThreadStats(threadId)
113
230
 
114
231
  await updateThread(threadId, {
115
232
  messages: updatedMessages,
116
- title: title
233
+ title: title,
234
+ stats,
117
235
  })
118
236
 
119
237
  return newMessage
@@ -126,6 +244,37 @@ async function deleteMessageFromThread(threadId, messageId) {
126
244
  await updateThread(threadId, { messages: updatedMessages })
127
245
  }
128
246
 
247
+ async function updateMessageInThread(threadId, messageId, updates) {
248
+ const thread = await getThread(threadId)
249
+ if (!thread) throw new Error('Thread not found')
250
+
251
+ const messageIndex = thread.messages.findIndex(m => m.id === messageId)
252
+ if (messageIndex === -1) throw new Error('Message not found')
253
+
254
+ const updatedMessages = [...thread.messages]
255
+ updatedMessages[messageIndex] = {
256
+ ...updatedMessages[messageIndex],
257
+ ...updates
258
+ }
259
+
260
+ await updateThread(threadId, { messages: updatedMessages })
261
+ }
262
+
263
+ async function redoMessageFromThread(threadId, messageId) {
264
+ const thread = await getThread(threadId)
265
+ if (!thread) throw new Error('Thread not found')
266
+
267
+ // Find the index of the message to redo
268
+ const messageIndex = thread.messages.findIndex(m => m.id === messageId)
269
+ if (messageIndex === -1) throw new Error('Message not found')
270
+
271
+ // Keep only messages up to and including the target message
272
+ const updatedMessages = thread.messages.slice(0, messageIndex + 1)
273
+
274
+ // Update the thread with the new messages
275
+ await updateThread(threadId, { messages: updatedMessages })
276
+ }
277
+
129
278
  // Get all threads
130
279
  async function loadThreads() {
131
280
  await initDB()
@@ -247,6 +396,102 @@ function getGroupedThreads(total) {
247
396
  // Group threads by time periods
248
397
  const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
249
398
 
399
+ // Query requests with pagination and filtering
400
+ async function getRequests(filters = {}, limit = 20, offset = 0) {
401
+ try {
402
+ await initDB()
403
+
404
+ const {
405
+ model = null,
406
+ provider = null,
407
+ threadId = null,
408
+ sortBy = 'created',
409
+ sortOrder = 'desc',
410
+ startDate = null,
411
+ endDate = null
412
+ } = filters
413
+
414
+ const tx = db.transaction(['requests'], 'readonly')
415
+ const store = tx.objectStore('requests')
416
+
417
+ // Get all requests and filter in memory (IndexedDB limitations)
418
+ const allRequests = await store.getAll()
419
+
420
+ // Apply filters
421
+ let results = allRequests.filter(req => {
422
+ if (model && req.model !== model) return false
423
+ if (provider && req.provider !== provider) return false
424
+ if (threadId && req.threadId !== threadId) return false
425
+ if (startDate && req.created < startDate) return false
426
+ if (endDate && req.created > endDate) return false
427
+ return true
428
+ })
429
+
430
+ // Sort
431
+ results.sort((a, b) => {
432
+ let aVal = a[sortBy]
433
+ let bVal = b[sortBy]
434
+
435
+ if (sortOrder === 'desc') {
436
+ return bVal - aVal
437
+ } else {
438
+ return aVal - bVal
439
+ }
440
+ })
441
+
442
+ // Paginate
443
+ const total = results.length
444
+ const paginatedResults = results.slice(offset, offset + limit)
445
+
446
+ return {
447
+ requests: paginatedResults,
448
+ total,
449
+ hasMore: offset + limit < total
450
+ }
451
+ } catch (error) {
452
+ console.error('Error in getRequests:', error)
453
+ return {
454
+ requests: [],
455
+ total: 0,
456
+ hasMore: false
457
+ }
458
+ }
459
+ }
460
+
461
+ // Get unique values for filter options
462
+ async function getFilterOptions() {
463
+ try {
464
+ await initDB()
465
+
466
+ const tx = db.transaction(['requests'], 'readonly')
467
+ const store = tx.objectStore('requests')
468
+ const allRequests = await store.getAll()
469
+
470
+ const models = [...new Set(allRequests.map(r => r.model).filter(m => m))].sort()
471
+ const providers = [...new Set(allRequests.map(r => r.provider).filter(p => p))].sort()
472
+
473
+ return {
474
+ models,
475
+ providers
476
+ }
477
+ } catch (error) {
478
+ console.error('Error in getFilterOptions:', error)
479
+ return {
480
+ models: [],
481
+ providers: []
482
+ }
483
+ }
484
+ }
485
+
486
+ // Delete a request by ID
487
+ async function deleteRequest(requestId) {
488
+ await initDB()
489
+
490
+ const tx = db.transaction(['requests'], 'readwrite')
491
+ await tx.objectStore('requests').delete(requestId)
492
+ await tx.complete
493
+ }
494
+
250
495
  // Export the store
251
496
  export function useThreadStore() {
252
497
  return {
@@ -258,10 +503,13 @@ export function useThreadStore() {
258
503
 
259
504
  // Actions
260
505
  initDB,
506
+ logRequest,
261
507
  createThread,
262
508
  updateThread,
263
509
  addMessageToThread,
264
510
  deleteMessageFromThread,
511
+ updateMessageInThread,
512
+ redoMessageFromThread,
265
513
  loadThreads,
266
514
  getThread,
267
515
  deleteThread,
@@ -269,5 +517,8 @@ export function useThreadStore() {
269
517
  setCurrentThreadFromRoute,
270
518
  clearCurrentThread,
271
519
  getGroupedThreads,
520
+ getRequests,
521
+ getFilterOptions,
522
+ deleteRequest,
272
523
  }
273
524
  }
llms/ui/utils.mjs CHANGED
@@ -54,6 +54,42 @@ export function fileToDataUri(file) {
54
54
  })
55
55
  }
56
56
 
57
+ export function toModelInfo(model) {
58
+ if (!model) return undefined
59
+ return Object.assign({}, model, { pricing: Object.assign({}, model.pricing) || undefined })
60
+ }
61
+
62
+ const numFmt = new Intl.NumberFormat(undefined,{style:'currency',currency:'USD', maximumFractionDigits:6})
63
+ export function tokenCost(price) {
64
+ if (!price) return ''
65
+ var ret = numFmt.format(parseFloat(price))
66
+ return ret.endsWith('.00') ? ret.slice(0, -3) : ret
67
+ }
68
+ export function formatCost(cost) {
69
+ if (!cost) return ''
70
+ return numFmt.format(parseFloat(cost))
71
+ }
72
+ export function statsTitle(stats) {
73
+ let title = []
74
+ // Each stat on its own line
75
+ if (stats.cost) {
76
+ title.push(`Total Cost: ${formatCost(stats.cost)}`)
77
+ }
78
+ if (stats.inputTokens) {
79
+ title.push(`Input Tokens: ${stats.inputTokens}`)
80
+ }
81
+ if (stats.outputTokens) {
82
+ title.push(`Output Tokens: ${stats.outputTokens}`)
83
+ }
84
+ if (stats.requests) {
85
+ title.push(`Requests: ${stats.requests}`)
86
+ }
87
+ if (stats.duration) {
88
+ title.push(`Duration: ${stats.duration}ms`)
89
+ }
90
+ return title.join('\n')
91
+ }
92
+
57
93
  const svg = {
58
94
  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>`,
59
95
  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>`,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 2.0.15
3
+ Version: 2.0.17
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
@@ -42,7 +42,7 @@ Dynamic: requires-python
42
42
 
43
43
  Lightweight CLI and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers.
44
44
 
45
- Configure additional providers and models in [llms.json](llms.json)
45
+ Configure additional providers and models in [llms.json](llms/llms.json)
46
46
  - Mix and match local models with models from different API providers
47
47
  - Requests automatically routed to available providers that supports the requested model (in defined order)
48
48
  - Define free/cheapest/local providers first to save on costs
@@ -73,28 +73,10 @@ Read the [Introductory Blog Post](https://servicestack.net/posts/llms-py-ui).
73
73
 
74
74
  ## Installation
75
75
 
76
- ### Option 1: Install from PyPI
77
-
78
76
  ```bash
79
77
  pip install llms-py
80
78
  ```
81
79
 
82
- ### Option 2: Download directly
83
-
84
- 1. Download [llms.py](llms.py)
85
-
86
- ```bash
87
- curl -O https://raw.githubusercontent.com/ServiceStack/llms/main/llms.py
88
- chmod +x llms.py
89
- mv llms.py ~/.local/bin/llms
90
- ```
91
-
92
- 2. Install single dependency:
93
-
94
- ```bash
95
- pip install aiohttp
96
- ```
97
-
98
80
  ## Quick Start
99
81
 
100
82
  ### 1. Set API Keys
@@ -102,12 +84,12 @@ pip install aiohttp
102
84
  Set environment variables for the providers you want to use:
103
85
 
104
86
  ```bash
105
- export OPENROUTER_FREE_API_KEY="..."
87
+ export OPENROUTER_API_KEY="..."
106
88
  ```
107
89
 
108
90
  | Provider | Variable | Description | Example |
109
91
  |-----------------|---------------------------|---------------------|---------|
110
- | openrouter_free | `OPENROUTER_FREE_API_KEY` | OpenRouter FREE models API key | `sk-or-...` |
92
+ | openrouter_free | `OPENROUTER_API_KEY` | OpenRouter FREE models API key | `sk-or-...` |
111
93
  | groq | `GROQ_API_KEY` | Groq API key | `gsk_...` |
112
94
  | google_free | `GOOGLE_FREE_API_KEY` | Google FREE API key | `AIza...` |
113
95
  | codestral | `CODESTRAL_API_KEY` | Codestral API key | `...` |
@@ -151,7 +133,7 @@ llms "What is the capital of France?"
151
133
 
152
134
  ## Configuration
153
135
 
154
- The configuration file [llms.json](llms.json) is saved to `~/.llms/llms.json` and defines available providers, models, and default settings. Key sections:
136
+ The configuration file [llms.json](llms/llms.json) is saved to `~/.llms/llms.json` and defines available providers, models, and default settings. Key sections:
155
137
 
156
138
  ### Defaults
157
139
  - `headers`: Common HTTP headers for all requests
@@ -193,7 +175,7 @@ llms "Explain quantum computing" --raw
193
175
 
194
176
  ### Using a Chat Template
195
177
 
196
- By default llms uses the `defaults/text` chat completion request defined in [llms.json](llms.json).
178
+ By default llms uses the `defaults/text` chat completion request defined in [llms.json](llms/llms.json).
197
179
 
198
180
  You can instead use a custom chat completion request with `--chat`, e.g:
199
181
 
@@ -485,19 +467,10 @@ llms --default grok-4
485
467
 
486
468
  ### Update
487
469
 
488
- 1. Installed from PyPI
489
-
490
470
  ```bash
491
471
  pip install llms-py --upgrade
492
472
  ```
493
473
 
494
- 2. Using Direct Download
495
-
496
- ```bash
497
- # Update to latest version (Downloads latest llms.py)
498
- llms --update
499
- ```
500
-
501
474
  ### Advanced Options
502
475
 
503
476
  ```bash
@@ -596,7 +569,7 @@ llms --update
596
569
  ```
597
570
 
598
571
  This command:
599
- - Downloads the latest `llms.py` from `https://raw.githubusercontent.com/ServiceStack/llms/refs/heads/main/llms.py`
572
+ - Downloads the latest `llms.py` from `github.com/ServiceStack/llms/blob/main/llms/main.py`
600
573
  - Overwrites your current `llms.py` file with the latest version
601
574
  - Preserves your existing configuration file (`llms.json`)
602
575
  - Requires an internet connection to download the update
@@ -633,7 +606,7 @@ or directly in your `llms.json`.
633
606
 
634
607
  | Provider | Variable | Description | Example |
635
608
  |-----------------|---------------------------|---------------------|---------|
636
- | openrouter_free | `OPENROUTER_FREE_API_KEY` | OpenRouter FREE models API key | `sk-or-...` |
609
+ | openrouter_free | `OPENROUTER_API_KEY` | OpenRouter FREE models API key | `sk-or-...` |
637
610
  | groq | `GROQ_API_KEY` | Groq API key | `gsk_...` |
638
611
  | google_free | `GOOGLE_FREE_API_KEY` | Google FREE API key | `AIza...` |
639
612
  | codestral | `CODESTRAL_API_KEY` | Codestral API key | `...` |
@@ -0,0 +1,56 @@
1
+ llms/__init__.py,sha256=Mk6eHi13yoUxLlzhwfZ6A1IjsfSQt9ShhOdbLXTvffU,53
2
+ llms/__main__.py,sha256=hrBulHIt3lmPm1BCyAEVtB6DQ0Hvc3gnIddhHCmJasg,151
3
+ llms/index.html,sha256=OA9mRmgh-dQrPqb0Z2Jv-cwEZ3YLPRxcWUN7ASjxO8s,2658
4
+ llms/llms.json,sha256=nqFxASGkpITzrfT3r4bRW7v7Ys2GuGIhmz3HZGsppnw,40925
5
+ llms/main.py,sha256=8d78tejZl7EA-JYtzA7CNy7-xBAGBkgEmP3DMgesTMA,71376
6
+ llms/ui.json,sha256=iBOmpNeD5-o8AgUa51ymS-KemovJ7bm9J1fnL0nf8jk,134025
7
+ llms/__pycache__/__init__.cpython-312.pyc,sha256=e6TG6e0PTRxM_LRg25eL4FvjQ0wBbfeahICORONYgDk,173
8
+ llms/__pycache__/__init__.cpython-313.pyc,sha256=DvmykIYAjV4Xjv5NeC70BA0JIX8qtwEWP2dfzmOBrps,173
9
+ llms/__pycache__/__init__.cpython-314.pyc,sha256=6G4fhFLhDJOx_Jk5SXH-4GnLvCtwxcQDo5KofT1ZFKY,175
10
+ llms/__pycache__/__main__.cpython-312.pyc,sha256=fcu9LVl5nfdSzg9HqoKyDGoDvENeIXKkjygN7FENEGE,321
11
+ llms/__pycache__/__main__.cpython-314.pyc,sha256=IFxtGVpJq_3whKuM5Ln7YMweKFNbHVp8M7VSrLK5h2A,324
12
+ llms/__pycache__/llms.cpython-312.pyc,sha256=S5dFI79JdUe2dQW4ogdB-CCNhudQeFaFGcfKxgJGBms,72080
13
+ llms/__pycache__/main.cpython-312.pyc,sha256=pqZ-h6Itl6gNQpNwpp_npEYGl-cNxAYDmogr721XoeU,72085
14
+ llms/__pycache__/main.cpython-313.pyc,sha256=6NQ__SJ2rC9ItFLKLHL5ewb5RqxLzZabwgczA9wZd-w,74814
15
+ llms/__pycache__/main.cpython-314.pyc,sha256=WtfggYctsz-pwCdo4gGPO3UcFZAf6VQEK_Gy-fveeEg,89937
16
+ llms/ui/Analytics.mjs,sha256=mAS5AUQjpnEIMyzGzOGE6fZxwxoVyq5QCitYQSSCEpQ,69151
17
+ llms/ui/App.mjs,sha256=hXtUjaL3GrcIHieEK3BzIG72OVzrorBBS4RkE1DOGc4,439
18
+ llms/ui/Avatar.mjs,sha256=3rHpxe_LuCDiNP895F3FOjWx4j377JA9rD1FLluvtgA,851
19
+ llms/ui/Brand.mjs,sha256=0NN2JBLUC0OWERuLz9myrimlcA7v7D5B_EMd0sQQVDo,1905
20
+ llms/ui/ChatPrompt.mjs,sha256=85O_kLVKWbbUDOUlvkuAineam_jrd6lzrj4O00p1XOg,21172
21
+ llms/ui/Main.mjs,sha256=BatB0UZVhWrv0dYSTy5EGeVDAF_T_H2XEVPlWRExRvE,38124
22
+ llms/ui/ModelSelector.mjs,sha256=ASLTUaqig3cDMiGup01rpubC2RrrZvPd8IFrYcK8GyQ,2565
23
+ llms/ui/ProviderIcon.mjs,sha256=HTjlgtXEpekn8iNN_S0uswbbvL0iGb20N15-_lXdojk,9054
24
+ llms/ui/ProviderStatus.mjs,sha256=qF_rPdhyt9GffKdPCJdU0yanrDJ3cw1HLPygFP_KjEs,5744
25
+ llms/ui/Recents.mjs,sha256=hmj7V-RXVw-DqMXjUr3OhFHTYQTkvkEhuNEDTGBf3Qw,8448
26
+ llms/ui/SettingsDialog.mjs,sha256=Jm21s5CcZT97ZEhPc7c1WgnLOLdfSiC1SDroYsh7zM4,18095
27
+ llms/ui/Sidebar.mjs,sha256=XPdbJo87H1vgTG0R9Ssqo_8XNhn9umkGwbubLpIOWqY,10519
28
+ llms/ui/SignIn.mjs,sha256=df3b-7L3ZIneDGbJWUk93K9RGo40gVeuR5StzT1ZH9g,2324
29
+ llms/ui/SystemPromptEditor.mjs,sha256=2CyIUvkIubqYPyIp5zC6_I8CMxvYINuYNjDxvMz4VRU,1265
30
+ llms/ui/SystemPromptSelector.mjs,sha256=AuEtRwUf_RkGgene3nVA9bw8AeMb-b5_6ZLJCTWA8KQ,3051
31
+ llms/ui/Welcome.mjs,sha256=QFAxN7sjWlhMvOIJCmHjNFCQcvpM_T-b4ze1ld9Hj7I,912
32
+ llms/ui/ai.mjs,sha256=t-dabzCgosc75Lbwp35KbJxBqGVncWVlm7aCmSFCYgM,2346
33
+ llms/ui/app.css,sha256=e81FHQ-K7TlS7Cr2x_CCHqrvmVvg9I-m0InLQHRT_Dg,98992
34
+ llms/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
35
+ llms/ui/markdown.mjs,sha256=O5UspOeD8-E23rxOLWcS4eyy2YejMbPwszCYteVtuoU,6221
36
+ llms/ui/tailwind.input.css,sha256=yo_3A50uyiVSUHUWeqAMorXMhCWpZoE5lTO6OJIFlYg,11974
37
+ llms/ui/threadStore.mjs,sha256=JKimOl-9c4p9qQ9L93tZZktmKwuzpiudXiWb4N9Ca3U,15557
38
+ llms/ui/typography.css,sha256=Z5Fe2IQWnh7bu1CMXniYt0SkaN2fXOFlOuniXUW8oGM,19325
39
+ llms/ui/utils.mjs,sha256=cYrP17JwpQk7lLqTWNgVTOD_ZZAovbWnx2QSvKzeB24,5333
40
+ llms/ui/lib/chart.js,sha256=dx8FdDX0Rv6OZtZjr9FQh5h-twFsKjfnb-FvFlQ--cU,196176
41
+ llms/ui/lib/charts.mjs,sha256=MNym9qE_2eoH6M7_8Gj9i6e6-Y3b7zw9UQWCUHRF6x0,1088
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=r_-khYokisXJAIPDLh8Wq6YtcLAY6HNjtJlCZJjLy74,215181
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_py-2.0.17.dist-info/licenses/LICENSE,sha256=rRryrddGfVftpde-rmAZpW0R8IJihqJ8t8wpfDXoKiQ,1549
52
+ llms_py-2.0.17.dist-info/METADATA,sha256=deZTzjc27mr_v6Nfs9zqz_ZYeWsJ7p0dOHZPXRlpmJ8,27902
53
+ llms_py-2.0.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
54
+ llms_py-2.0.17.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
55
+ llms_py-2.0.17.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
56
+ llms_py-2.0.17.dist-info/RECORD,,
@@ -1,46 +0,0 @@
1
- llms/__init__.py,sha256=Mk6eHi13yoUxLlzhwfZ6A1IjsfSQt9ShhOdbLXTvffU,53
2
- llms/__main__.py,sha256=hrBulHIt3lmPm1BCyAEVtB6DQ0Hvc3gnIddhHCmJasg,151
3
- llms/index.html,sha256=RmvPW7H49tHR3DdQvvSrcR_OeCt1InYd5dirBxfZCpo,2417
4
- llms/llms.json,sha256=aQFm_lXsPLTWCndE2DNhTY04P576U7NFN9hx5-QwL3E,19582
5
- llms/main.py,sha256=Bhmj_RE8lalMiO7U0QOuIhLIxIHLaKNFw87hIDMFOFU,62546
6
- llms/ui.json,sha256=iBOmpNeD5-o8AgUa51ymS-KemovJ7bm9J1fnL0nf8jk,134025
7
- llms/__pycache__/__init__.cpython-312.pyc,sha256=e6TG6e0PTRxM_LRg25eL4FvjQ0wBbfeahICORONYgDk,173
8
- llms/__pycache__/__main__.cpython-312.pyc,sha256=fcu9LVl5nfdSzg9HqoKyDGoDvENeIXKkjygN7FENEGE,321
9
- llms/__pycache__/llms.cpython-312.pyc,sha256=S5dFI79JdUe2dQW4ogdB-CCNhudQeFaFGcfKxgJGBms,72080
10
- llms/__pycache__/main.cpython-312.pyc,sha256=Idq1-5l1oPv0fCjS4atoQ7CVS5TSj1KXCFrbHZsYXnk,72080
11
- llms/ui/App.mjs,sha256=hXtUjaL3GrcIHieEK3BzIG72OVzrorBBS4RkE1DOGc4,439
12
- llms/ui/Avatar.mjs,sha256=3rHpxe_LuCDiNP895F3FOjWx4j377JA9rD1FLluvtgA,851
13
- llms/ui/Brand.mjs,sha256=ZkJ1Yd9ZgdTbs7f3ezM0UtsHMcm6v4-L-m3avnSXAXU,1184
14
- llms/ui/ChatPrompt.mjs,sha256=IbfCx3W0SlX8x9lvaFjB1rqJqNkbeUvfqFHP9iKzZ9s,19776
15
- llms/ui/Main.mjs,sha256=kLNWyXWUgHAaX3C3kVkAHOLvEI3H4bd5k9yMkUIRQPU,27348
16
- llms/ui/ModelSelector.mjs,sha256=qiI-7DBwif5ipNZtzgaZQ2o_wHc23dBRMY6zVkMOCak,947
17
- llms/ui/ProviderStatus.mjs,sha256=qF_rPdhyt9GffKdPCJdU0yanrDJ3cw1HLPygFP_KjEs,5744
18
- llms/ui/Recents.mjs,sha256=hmj7V-RXVw-DqMXjUr3OhFHTYQTkvkEhuNEDTGBf3Qw,8448
19
- llms/ui/SettingsDialog.mjs,sha256=Jm21s5CcZT97ZEhPc7c1WgnLOLdfSiC1SDroYsh7zM4,18095
20
- llms/ui/Sidebar.mjs,sha256=ERm2Q7pftKTHLCjf-Q76joy1dsvtNWLoYH-wO2mS1LI,9781
21
- llms/ui/SignIn.mjs,sha256=df3b-7L3ZIneDGbJWUk93K9RGo40gVeuR5StzT1ZH9g,2324
22
- llms/ui/SystemPromptEditor.mjs,sha256=2CyIUvkIubqYPyIp5zC6_I8CMxvYINuYNjDxvMz4VRU,1265
23
- llms/ui/SystemPromptSelector.mjs,sha256=AuEtRwUf_RkGgene3nVA9bw8AeMb-b5_6ZLJCTWA8KQ,3051
24
- llms/ui/Welcome.mjs,sha256=QFAxN7sjWlhMvOIJCmHjNFCQcvpM_T-b4ze1ld9Hj7I,912
25
- llms/ui/ai.mjs,sha256=isXZR2GUy-7W627mLllpuQVaWQHRtPA0LVWDJO-zMxA,2346
26
- llms/ui/app.css,sha256=1Z4coREGsjMw7lYabejdaG0ZMwPpP5lmjRHRDYygq-g,94964
27
- llms/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
28
- llms/ui/markdown.mjs,sha256=O5UspOeD8-E23rxOLWcS4eyy2YejMbPwszCYteVtuoU,6221
29
- llms/ui/tailwind.input.css,sha256=P6QxCwbTBAaG2079ddgtFuuXDLIztEkJDFwj0wmOvkk,11978
30
- llms/ui/threadStore.mjs,sha256=nM53p1E78Jc7tPOoyS3J3SW9F1njcZ-Fw5FlR9l-3yY,7215
31
- llms/ui/typography.css,sha256=Z5Fe2IQWnh7bu1CMXniYt0SkaN2fXOFlOuniXUW8oGM,19325
32
- llms/ui/utils.mjs,sha256=UKyaBsuJYEc-MWoL9b1M5-t3x6h5_HV9HsNBoDVtBRw,4226
33
- llms/ui/lib/highlight.min.mjs,sha256=sG7wq8bF-IKkfie7S4QSyh5DdHBRf0NqQxMOEH8-MT0,127458
34
- llms/ui/lib/idb.min.mjs,sha256=CeTXyV4I_pB5vnibvJuyXdMs0iVF2ZL0Z7cdm3w_QaI,3853
35
- llms/ui/lib/marked.min.mjs,sha256=QRHb_VZugcBJRD2EP6gYlVFEsJw5C2fQ8ImMf_pA2_s,39488
36
- llms/ui/lib/servicestack-client.mjs,sha256=UVafVbzhJ_0N2lzv7rlzIbzwnWpoqXxGk3N3FSKgOOc,54534
37
- llms/ui/lib/servicestack-vue.mjs,sha256=r_-khYokisXJAIPDLh8Wq6YtcLAY6HNjtJlCZJjLy74,215181
38
- llms/ui/lib/vue-router.min.mjs,sha256=fR30GHoXI1u81zyZ26YEU105pZgbbAKSXbpnzFKIxls,30418
39
- llms/ui/lib/vue.min.mjs,sha256=iXh97m5hotl0eFllb3aoasQTImvp7mQoRJ_0HoxmZkw,163811
40
- llms/ui/lib/vue.mjs,sha256=dS8LKOG01t9CvZ04i0tbFXHqFXOO_Ha4NmM3BytjQAs,537071
41
- llms_py-2.0.15.dist-info/licenses/LICENSE,sha256=rRryrddGfVftpde-rmAZpW0R8IJihqJ8t8wpfDXoKiQ,1549
42
- llms_py-2.0.15.dist-info/METADATA,sha256=kEJJM0Bs_c7BPRaTNR-em3T1yRkP59X8wTC-0uNUefA,28351
43
- llms_py-2.0.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- llms_py-2.0.15.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
45
- llms_py-2.0.15.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
46
- llms_py-2.0.15.dist-info/RECORD,,