llms-py 2.0.21__tar.gz → 2.0.23__tar.gz
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_py-2.0.21/llms_py.egg-info → llms_py-2.0.23}/PKG-INFO +1 -1
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/main.py +1 -1
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/Main.mjs +122 -52
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/ai.mjs +1 -1
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/threadStore.mjs +39 -0
- {llms_py-2.0.21 → llms_py-2.0.23/llms_py.egg-info}/PKG-INFO +1 -1
- {llms_py-2.0.21 → llms_py-2.0.23}/pyproject.toml +1 -1
- {llms_py-2.0.21 → llms_py-2.0.23}/setup.py +1 -1
- {llms_py-2.0.21 → llms_py-2.0.23}/LICENSE +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/MANIFEST.in +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/README.md +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/__init__.py +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/__main__.py +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/index.html +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/llms.json +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/Analytics.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/App.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/Avatar.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/Brand.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/ChatPrompt.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/ModelSelector.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/ProviderIcon.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/ProviderStatus.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/Recents.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/SettingsDialog.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/Sidebar.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/SignIn.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/SystemPromptEditor.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/SystemPromptSelector.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/Welcome.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/app.css +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/fav.svg +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/chart.js +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/charts.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/color.js +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/highlight.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/idb.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/marked.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/servicestack-vue.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/vue.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/lib/vue.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/markdown.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/tailwind.input.css +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/typography.css +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui/utils.mjs +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms/ui.json +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms_py.egg-info/SOURCES.txt +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms_py.egg-info/dependency_links.txt +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms_py.egg-info/entry_points.txt +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms_py.egg-info/not-zip-safe +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms_py.egg-info/requires.txt +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/llms_py.egg-info/top_level.txt +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/requirements.txt +0 -0
- {llms_py-2.0.21 → llms_py-2.0.23}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llms-py
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.23
|
|
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
|
|
@@ -61,7 +61,7 @@ export default {
|
|
|
61
61
|
<!-- Export/Import buttons -->
|
|
62
62
|
<div class="mt-2 flex space-x-3 justify-center">
|
|
63
63
|
<button type="button"
|
|
64
|
-
@click="exportThreads"
|
|
64
|
+
@click="(e) => e.altKey ? exportRequests() : exportThreads()"
|
|
65
65
|
:disabled="isExporting"
|
|
66
66
|
:title="'Export ' + threads?.threads?.value?.length + ' conversations'"
|
|
67
67
|
class="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
@@ -411,7 +411,7 @@ export default {
|
|
|
411
411
|
const exportData = {
|
|
412
412
|
exportedAt: new Date().toISOString(),
|
|
413
413
|
version: '1.0',
|
|
414
|
-
source: '
|
|
414
|
+
source: 'llmspy',
|
|
415
415
|
threadCount: allThreads.length,
|
|
416
416
|
threads: allThreads
|
|
417
417
|
}
|
|
@@ -423,7 +423,7 @@ export default {
|
|
|
423
423
|
|
|
424
424
|
const link = document.createElement('a')
|
|
425
425
|
link.href = url
|
|
426
|
-
link.download = `
|
|
426
|
+
link.download = `llmsthreads-export-${new Date().toISOString().split('T')[0]}.json`
|
|
427
427
|
document.body.appendChild(link)
|
|
428
428
|
link.click()
|
|
429
429
|
document.body.removeChild(link)
|
|
@@ -437,6 +437,44 @@ export default {
|
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
+
async function exportRequests() {
|
|
441
|
+
if (isExporting.value) return
|
|
442
|
+
|
|
443
|
+
isExporting.value = true
|
|
444
|
+
try {
|
|
445
|
+
// Load all threads from IndexedDB
|
|
446
|
+
const allRequests = await threads.getAllRequests()
|
|
447
|
+
|
|
448
|
+
// Create export data with metadata
|
|
449
|
+
const exportData = {
|
|
450
|
+
exportedAt: new Date().toISOString(),
|
|
451
|
+
version: '1.0',
|
|
452
|
+
source: 'llmspy',
|
|
453
|
+
requestsCount: allRequests.length,
|
|
454
|
+
requests: allRequests
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Create and download JSON file
|
|
458
|
+
const jsonString = JSON.stringify(exportData, null, 2)
|
|
459
|
+
const blob = new Blob([jsonString], { type: 'application/json' })
|
|
460
|
+
const url = URL.createObjectURL(blob)
|
|
461
|
+
|
|
462
|
+
const link = document.createElement('a')
|
|
463
|
+
link.href = url
|
|
464
|
+
link.download = `llmsrequests-export-${new Date().toISOString().split('T')[0]}.json`
|
|
465
|
+
document.body.appendChild(link)
|
|
466
|
+
link.click()
|
|
467
|
+
document.body.removeChild(link)
|
|
468
|
+
URL.revokeObjectURL(url)
|
|
469
|
+
|
|
470
|
+
} catch (error) {
|
|
471
|
+
console.error('Failed to export requests:', error)
|
|
472
|
+
alert('Failed to export requests: ' + error.message)
|
|
473
|
+
} finally {
|
|
474
|
+
isExporting.value = false
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
440
478
|
function triggerImport() {
|
|
441
479
|
if (isImporting.value) return
|
|
442
480
|
fileInput.value?.click()
|
|
@@ -447,71 +485,102 @@ export default {
|
|
|
447
485
|
if (!file) return
|
|
448
486
|
|
|
449
487
|
isImporting.value = true
|
|
488
|
+
var importType = 'threads'
|
|
450
489
|
try {
|
|
451
490
|
const text = await file.text()
|
|
452
491
|
const importData = JSON.parse(text)
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
492
|
+
importType = importData.threads
|
|
493
|
+
? 'threads'
|
|
494
|
+
: importData.requests
|
|
495
|
+
? 'requests'
|
|
496
|
+
: 'unknown'
|
|
458
497
|
|
|
459
498
|
// Import threads one by one
|
|
460
499
|
let importedCount = 0
|
|
461
|
-
let
|
|
500
|
+
let existingCount = 0
|
|
462
501
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
502
|
+
const db = await threads.initDB()
|
|
503
|
+
|
|
504
|
+
if (importData.threads) {
|
|
505
|
+
if (!Array.isArray(importData.threads)) {
|
|
506
|
+
throw new Error('Invalid import file: missing or invalid threads array')
|
|
467
507
|
}
|
|
468
508
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
await tx.complete
|
|
500
|
-
importedCount++
|
|
509
|
+
const threadIds = new Set(await threads.getAllThreadIds())
|
|
510
|
+
|
|
511
|
+
for (const threadData of importData.threads) {
|
|
512
|
+
if (!threadData.id) {
|
|
513
|
+
console.warn('Skipping thread without ID:', threadData)
|
|
514
|
+
continue
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
// Check if thread already exists
|
|
519
|
+
const existingThread = threadIds.has(threadData.id)
|
|
520
|
+
if (existingThread) {
|
|
521
|
+
existingCount++
|
|
522
|
+
} else {
|
|
523
|
+
// Add new thread directly to IndexedDB
|
|
524
|
+
const tx = db.transaction(['threads'], 'readwrite')
|
|
525
|
+
await tx.objectStore('threads').add({
|
|
526
|
+
id: threadData.id,
|
|
527
|
+
title: threadData.title || 'Imported Chat',
|
|
528
|
+
model: threadData.model || '',
|
|
529
|
+
systemPrompt: threadData.systemPrompt || '',
|
|
530
|
+
messages: threadData.messages || [],
|
|
531
|
+
createdAt: threadData.createdAt || new Date().toISOString(),
|
|
532
|
+
updatedAt: threadData.updatedAt || new Date().toISOString()
|
|
533
|
+
})
|
|
534
|
+
await tx.complete
|
|
535
|
+
importedCount++
|
|
536
|
+
}
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.error('Failed to import thread:', threadData.id, error)
|
|
501
539
|
}
|
|
502
|
-
} catch (error) {
|
|
503
|
-
console.error('Failed to import thread:', threadData.id, error)
|
|
504
540
|
}
|
|
541
|
+
|
|
542
|
+
// Reload threads to reflect changes
|
|
543
|
+
await threads.loadThreads()
|
|
544
|
+
|
|
545
|
+
alert(`Import completed!\nNew threads: ${importedCount}\nExisting threads: ${existingCount}`)
|
|
505
546
|
}
|
|
547
|
+
if (importData.requests) {
|
|
548
|
+
if (!Array.isArray(importData.requests)) {
|
|
549
|
+
throw new Error('Invalid import file: missing or invalid requests array')
|
|
550
|
+
}
|
|
506
551
|
|
|
507
|
-
|
|
508
|
-
await threads.loadThreads()
|
|
552
|
+
const requestIds = new Set(await threads.getAllRequestIds())
|
|
509
553
|
|
|
510
|
-
|
|
554
|
+
for (const requestData of importData.requests) {
|
|
555
|
+
if (!requestData.id) {
|
|
556
|
+
console.warn('Skipping request without ID:', requestData)
|
|
557
|
+
continue
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
try {
|
|
561
|
+
// Check if request already exists
|
|
562
|
+
const existingRequest = requestIds.has(requestData.id)
|
|
563
|
+
if (existingRequest) {
|
|
564
|
+
existingCount++
|
|
565
|
+
} else {
|
|
566
|
+
// Add new request directly to IndexedDB
|
|
567
|
+
const db = await threads.initDB()
|
|
568
|
+
const tx = db.transaction(['requests'], 'readwrite')
|
|
569
|
+
await tx.objectStore('requests').add(requestData)
|
|
570
|
+
await tx.complete
|
|
571
|
+
importedCount++
|
|
572
|
+
}
|
|
573
|
+
} catch (error) {
|
|
574
|
+
console.error('Failed to import request:', requestData.id, error)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
alert(`Import completed!\nNew requests: ${importedCount}\nExisting requests: ${existingCount}`)
|
|
579
|
+
}
|
|
511
580
|
|
|
512
581
|
} catch (error) {
|
|
513
|
-
console.error('Failed to import
|
|
514
|
-
alert('Failed to import
|
|
582
|
+
console.error('Failed to import ' + importType + ':', error)
|
|
583
|
+
alert('Failed to import ' + importType + ': ' + error.message)
|
|
515
584
|
} finally {
|
|
516
585
|
isImporting.value = false
|
|
517
586
|
// Clear the file input
|
|
@@ -725,6 +794,7 @@ export default {
|
|
|
725
794
|
cancelEdit,
|
|
726
795
|
configUpdated,
|
|
727
796
|
exportThreads,
|
|
797
|
+
exportRequests,
|
|
728
798
|
isExporting,
|
|
729
799
|
triggerImport,
|
|
730
800
|
handleFileImport,
|
|
@@ -396,6 +396,41 @@ function getGroupedThreads(total) {
|
|
|
396
396
|
// Group threads by time periods
|
|
397
397
|
const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
|
|
398
398
|
|
|
399
|
+
async function getAllRequests() {
|
|
400
|
+
await initDB()
|
|
401
|
+
|
|
402
|
+
const tx = db.transaction(['requests'], 'readonly')
|
|
403
|
+
const store = tx.objectStore('requests')
|
|
404
|
+
const allRequests = await store.getAll()
|
|
405
|
+
return allRequests
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function getRequest(requestId) {
|
|
409
|
+
await initDB()
|
|
410
|
+
|
|
411
|
+
const tx = db.transaction(['requests'], 'readonly')
|
|
412
|
+
const store = tx.objectStore('requests')
|
|
413
|
+
const request = await store.get(requestId)
|
|
414
|
+
return request
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async function getAllRequestIds() {
|
|
418
|
+
await initDB()
|
|
419
|
+
|
|
420
|
+
const tx = db.transaction(['requests'], 'readonly')
|
|
421
|
+
const store = tx.objectStore('requests')
|
|
422
|
+
const ids = await store.getAllKeys()
|
|
423
|
+
return ids
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function getAllThreadIds() {
|
|
427
|
+
await initDB()
|
|
428
|
+
const tx = db.transaction(['threads'], 'readonly')
|
|
429
|
+
const store = tx.objectStore('threads')
|
|
430
|
+
const ids = await store.getAllKeys()
|
|
431
|
+
return ids
|
|
432
|
+
}
|
|
433
|
+
|
|
399
434
|
// Query requests with pagination and filtering
|
|
400
435
|
async function getRequests(filters = {}, limit = 20, offset = 0) {
|
|
401
436
|
try {
|
|
@@ -517,8 +552,12 @@ export function useThreadStore() {
|
|
|
517
552
|
setCurrentThreadFromRoute,
|
|
518
553
|
clearCurrentThread,
|
|
519
554
|
getGroupedThreads,
|
|
555
|
+
getRequest,
|
|
520
556
|
getRequests,
|
|
557
|
+
getAllRequests,
|
|
521
558
|
getFilterOptions,
|
|
522
559
|
deleteRequest,
|
|
560
|
+
getAllRequestIds,
|
|
561
|
+
getAllThreadIds,
|
|
523
562
|
}
|
|
524
563
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llms-py
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.23
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "llms-py"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.23"
|
|
8
8
|
description = "A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -16,7 +16,7 @@ with open(os.path.join(this_directory, "requirements.txt"), encoding="utf-8") as
|
|
|
16
16
|
|
|
17
17
|
setup(
|
|
18
18
|
name="llms-py",
|
|
19
|
-
version="2.0.
|
|
19
|
+
version="2.0.23",
|
|
20
20
|
author="ServiceStack",
|
|
21
21
|
author_email="team@servicestack.net",
|
|
22
22
|
description="A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|