llms-py 2.0.20__tar.gz → 2.0.22__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.
Files changed (56) hide show
  1. {llms_py-2.0.20/llms_py.egg-info → llms_py-2.0.22}/PKG-INFO +1 -1
  2. {llms_py-2.0.20 → llms_py-2.0.22}/llms/main.py +1 -1
  3. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/Main.mjs +129 -51
  4. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/ai.mjs +1 -1
  5. llms_py-2.0.22/llms/ui/lib/chart.js +14 -0
  6. llms_py-2.0.22/llms/ui/lib/charts.mjs +20 -0
  7. llms_py-2.0.22/llms/ui/lib/color.js +14 -0
  8. llms_py-2.0.22/llms/ui/lib/highlight.min.mjs +1243 -0
  9. llms_py-2.0.22/llms/ui/lib/idb.min.mjs +8 -0
  10. llms_py-2.0.22/llms/ui/lib/marked.min.mjs +8 -0
  11. llms_py-2.0.22/llms/ui/lib/servicestack-client.mjs +1 -0
  12. llms_py-2.0.22/llms/ui/lib/servicestack-vue.mjs +37 -0
  13. llms_py-2.0.22/llms/ui/lib/vue-router.min.mjs +6 -0
  14. llms_py-2.0.22/llms/ui/lib/vue.min.mjs +12 -0
  15. llms_py-2.0.22/llms/ui/lib/vue.mjs +18369 -0
  16. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/threadStore.mjs +10 -0
  17. {llms_py-2.0.20 → llms_py-2.0.22/llms_py.egg-info}/PKG-INFO +1 -1
  18. {llms_py-2.0.20 → llms_py-2.0.22}/llms_py.egg-info/SOURCES.txt +11 -0
  19. {llms_py-2.0.20 → llms_py-2.0.22}/pyproject.toml +1 -1
  20. {llms_py-2.0.20 → llms_py-2.0.22}/setup.py +1 -1
  21. {llms_py-2.0.20 → llms_py-2.0.22}/LICENSE +0 -0
  22. {llms_py-2.0.20 → llms_py-2.0.22}/MANIFEST.in +0 -0
  23. {llms_py-2.0.20 → llms_py-2.0.22}/README.md +0 -0
  24. {llms_py-2.0.20 → llms_py-2.0.22}/llms/__init__.py +0 -0
  25. {llms_py-2.0.20 → llms_py-2.0.22}/llms/__main__.py +0 -0
  26. {llms_py-2.0.20 → llms_py-2.0.22}/llms/index.html +0 -0
  27. {llms_py-2.0.20 → llms_py-2.0.22}/llms/llms.json +0 -0
  28. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/Analytics.mjs +0 -0
  29. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/App.mjs +0 -0
  30. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/Avatar.mjs +0 -0
  31. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/Brand.mjs +0 -0
  32. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/ChatPrompt.mjs +0 -0
  33. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/ModelSelector.mjs +0 -0
  34. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/ProviderIcon.mjs +0 -0
  35. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/ProviderStatus.mjs +0 -0
  36. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/Recents.mjs +0 -0
  37. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/SettingsDialog.mjs +0 -0
  38. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/Sidebar.mjs +0 -0
  39. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/SignIn.mjs +0 -0
  40. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/SystemPromptEditor.mjs +0 -0
  41. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/SystemPromptSelector.mjs +0 -0
  42. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/Welcome.mjs +0 -0
  43. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/app.css +0 -0
  44. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/fav.svg +0 -0
  45. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/markdown.mjs +0 -0
  46. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/tailwind.input.css +0 -0
  47. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/typography.css +0 -0
  48. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui/utils.mjs +0 -0
  49. {llms_py-2.0.20 → llms_py-2.0.22}/llms/ui.json +0 -0
  50. {llms_py-2.0.20 → llms_py-2.0.22}/llms_py.egg-info/dependency_links.txt +0 -0
  51. {llms_py-2.0.20 → llms_py-2.0.22}/llms_py.egg-info/entry_points.txt +0 -0
  52. {llms_py-2.0.20 → llms_py-2.0.22}/llms_py.egg-info/not-zip-safe +0 -0
  53. {llms_py-2.0.20 → llms_py-2.0.22}/llms_py.egg-info/requires.txt +0 -0
  54. {llms_py-2.0.20 → llms_py-2.0.22}/llms_py.egg-info/top_level.txt +0 -0
  55. {llms_py-2.0.20 → llms_py-2.0.22}/requirements.txt +0 -0
  56. {llms_py-2.0.20 → llms_py-2.0.22}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 2.0.20
3
+ Version: 2.0.22
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
@@ -22,7 +22,7 @@ from aiohttp import web
22
22
  from pathlib import Path
23
23
  from importlib import resources # Py≥3.9 (pip install importlib_resources for 3.7/3.8)
24
24
 
25
- VERSION = "2.0.20"
25
+ VERSION = "2.0.22"
26
26
  _ROOT = None
27
27
  g_config_path = None
28
28
  g_ui_path = None
@@ -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: 'ServiceStack.AI.Chat',
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 = `aichat-threads-export-${new Date().toISOString().split('T')[0]}.json`
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,110 @@ 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
- // Validate import data structure
455
- if (!importData.threads || !Array.isArray(importData.threads)) {
456
- throw new Error('Invalid import file: missing or invalid threads array')
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
500
  let updatedCount = 0
462
501
 
463
- for (const threadData of importData.threads) {
464
- if (!threadData.id) {
465
- console.warn('Skipping thread without ID:', threadData)
466
- continue
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
- try {
470
- // Check if thread already exists
471
- const existingThread = await threads.getThread(threadData.id)
472
-
473
- if (existingThread) {
474
- // Update existing thread
475
- await threads.updateThread(threadData.id, {
476
- title: threadData.title,
477
- model: threadData.model,
478
- systemPrompt: threadData.systemPrompt,
479
- messages: threadData.messages || [],
480
- createdAt: threadData.createdAt,
481
- // Keep the existing updatedAt or use imported one
482
- updatedAt: threadData.updatedAt || existingThread.updatedAt
483
- })
484
- updatedCount++
485
- } else {
486
- // Add new thread directly to IndexedDB
487
- //await threads.initDB()
488
- const db = await threads.initDB()
489
- const tx = db.transaction(['threads'], 'readwrite')
490
- await tx.objectStore('threads').add({
491
- id: threadData.id,
492
- title: threadData.title || 'Imported Chat',
493
- model: threadData.model || '',
494
- systemPrompt: threadData.systemPrompt || '',
495
- messages: threadData.messages || [],
496
- createdAt: threadData.createdAt || new Date().toISOString(),
497
- updatedAt: threadData.updatedAt || new Date().toISOString()
498
- })
499
- await tx.complete
500
- importedCount++
509
+ for (const threadData of importData.threads) {
510
+ if (!threadData.id) {
511
+ console.warn('Skipping thread without ID:', threadData)
512
+ continue
513
+ }
514
+
515
+ try {
516
+ // Check if thread already exists
517
+ const existingThread = await threads.getThread(threadData.id)
518
+
519
+ if (existingThread) {
520
+ // Update existing thread
521
+ await threads.updateThread(threadData.id, {
522
+ title: threadData.title,
523
+ model: threadData.model,
524
+ systemPrompt: threadData.systemPrompt,
525
+ messages: threadData.messages || [],
526
+ createdAt: threadData.createdAt,
527
+ // Keep the existing updatedAt or use imported one
528
+ updatedAt: threadData.updatedAt || existingThread.updatedAt
529
+ })
530
+ updatedCount++
531
+ } else {
532
+ // Add new thread directly to IndexedDB
533
+ const tx = db.transaction(['threads'], 'readwrite')
534
+ await tx.objectStore('threads').add({
535
+ id: threadData.id,
536
+ title: threadData.title || 'Imported Chat',
537
+ model: threadData.model || '',
538
+ systemPrompt: threadData.systemPrompt || '',
539
+ messages: threadData.messages || [],
540
+ createdAt: threadData.createdAt || new Date().toISOString(),
541
+ updatedAt: threadData.updatedAt || new Date().toISOString()
542
+ })
543
+ await tx.complete
544
+ importedCount++
545
+ }
546
+ } catch (error) {
547
+ console.error('Failed to import thread:', threadData.id, error)
501
548
  }
502
- } catch (error) {
503
- console.error('Failed to import thread:', threadData.id, error)
504
549
  }
550
+
551
+ // Reload threads to reflect changes
552
+ await threads.loadThreads()
553
+
554
+ alert(`Import completed!\nNew threads: ${importedCount}\nUpdated threads: ${updatedCount}`)
505
555
  }
556
+ if (importData.requests) {
557
+ if (!Array.isArray(importData.requests)) {
558
+ throw new Error('Invalid import file: missing or invalid requests array')
559
+ }
506
560
 
507
- // Reload threads to reflect changes
508
- await threads.loadThreads()
561
+ for (const requestData of importData.requests) {
562
+ if (!requestData.id) {
563
+ console.warn('Skipping request without ID:', requestData)
564
+ continue
565
+ }
509
566
 
510
- alert(`Import completed!\nNew threads: ${importedCount}\nUpdated threads: ${updatedCount}`)
567
+ try {
568
+ // Check if request already exists
569
+ const existingRequest = await threads.getRequest(requestData.id)
570
+
571
+ if (existingRequest) {
572
+ updatedCount++
573
+ } else {
574
+ // Add new request directly to IndexedDB
575
+ const db = await threads.initDB()
576
+ const tx = db.transaction(['requests'], 'readwrite')
577
+ await tx.objectStore('requests').add(requestData)
578
+ await tx.complete
579
+ importedCount++
580
+ }
581
+ } catch (error) {
582
+ console.error('Failed to import request:', requestData.id, error)
583
+ }
584
+ }
585
+
586
+ alert(`Import completed!\nNew requests: ${importedCount}\nUpdated requests: ${updatedCount}`)
587
+ }
511
588
 
512
589
  } catch (error) {
513
- console.error('Failed to import threads:', error)
514
- alert('Failed to import threads: ' + error.message)
590
+ console.error('Failed to import ' + importType + ':', error)
591
+ alert('Failed to import ' + importType + ': ' + error.message)
515
592
  } finally {
516
593
  isImporting.value = false
517
594
  // Clear the file input
@@ -725,6 +802,7 @@ export default {
725
802
  cancelEdit,
726
803
  configUpdated,
727
804
  exportThreads,
805
+ exportRequests,
728
806
  isExporting,
729
807
  triggerImport,
730
808
  handleFileImport,
@@ -6,7 +6,7 @@ const headers = { 'Accept': 'application/json' }
6
6
  const prefsKey = 'llms.prefs'
7
7
 
8
8
  export const o = {
9
- version: '2.0.20',
9
+ version: '2.0.22',
10
10
  base,
11
11
  prefsKey,
12
12
  welcome: 'Welcome to llms.py',