Skip to content

Commit 30be9f8

Browse files
committed
handle exporting requests with ALT + importing of requests
1 parent 469bacd commit 30be9f8

2 files changed

Lines changed: 139 additions & 51 deletions

File tree

llms/ui/Main.mjs

Lines changed: 129 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export default {
6161
<!-- Export/Import buttons -->
6262
<div class="mt-2 flex space-x-3 justify-center">
6363
<button type="button"
64-
@click="exportThreads"
64+
@click="(e) => e.altKey ? exportRequests() : exportThreads()"
6565
:disabled="isExporting"
6666
:title="'Export ' + threads?.threads?.value?.length + ' conversations'"
6767
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 {
411411
const exportData = {
412412
exportedAt: new Date().toISOString(),
413413
version: '1.0',
414-
source: 'ServiceStack.AI.Chat',
414+
source: 'llmspy',
415415
threadCount: allThreads.length,
416416
threads: allThreads
417417
}
@@ -423,7 +423,7 @@ export default {
423423

424424
const link = document.createElement('a')
425425
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`
427427
document.body.appendChild(link)
428428
link.click()
429429
document.body.removeChild(link)
@@ -437,6 +437,44 @@ export default {
437437
}
438438
}
439439

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+
440478
function triggerImport() {
441479
if (isImporting.value) return
442480
fileInput.value?.click()
@@ -447,71 +485,110 @@ export default {
447485
if (!file) return
448486

449487
isImporting.value = true
488+
var importType = 'threads'
450489
try {
451490
const text = await file.text()
452491
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'
458497

459498
// Import threads one by one
460499
let importedCount = 0
461500
let updatedCount = 0
462501

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')
467507
}
468508

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)
501548
}
502-
} catch (error) {
503-
console.error('Failed to import thread:', threadData.id, error)
504549
}
550+
551+
// Reload threads to reflect changes
552+
await threads.loadThreads()
553+
554+
alert(`Import completed!\nNew threads: ${importedCount}\nUpdated threads: ${updatedCount}`)
505555
}
556+
if (importData.requests) {
557+
if (!Array.isArray(importData.requests)) {
558+
throw new Error('Invalid import file: missing or invalid requests array')
559+
}
506560

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+
}
509566

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+
}
511588

512589
} 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)
515592
} finally {
516593
isImporting.value = false
517594
// Clear the file input
@@ -725,6 +802,7 @@ export default {
725802
cancelEdit,
726803
configUpdated,
727804
exportThreads,
805+
exportRequests,
728806
isExporting,
729807
triggerImport,
730808
handleFileImport,

llms/ui/threadStore.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@ function getGroupedThreads(total) {
396396
// Group threads by time periods
397397
const groupedThreads = computed(() => getGroupedThreads(threads.value.length))
398398

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+
399408
// Query requests with pagination and filtering
400409
async function getRequests(filters = {}, limit = 20, offset = 0) {
401410
try {
@@ -518,6 +527,7 @@ export function useThreadStore() {
518527
clearCurrentThread,
519528
getGroupedThreads,
520529
getRequests,
530+
getAllRequests,
521531
getFilterOptions,
522532
deleteRequest,
523533
}

0 commit comments

Comments
 (0)