How to Implement Import and Export Functionality for Prompts and Configurations in Prompt-Optimizer
You can implement import and export functionality in prompt-optimizer by leveraging the existing DataManager class in the core layer, which aggregates data from all services (templates, models, preferences, history) and exposes exportData() and importData() methods that serialize to JSON, while the UI layer uses DataImportExportManager to handle file downloads and uploads.
The prompt-optimizer repository by linshenkx provides a robust architecture for managing AI prompt workflows. When users need to migrate their workspace or backup their configurations, implementing import and export functionality becomes essential. This guide demonstrates how to utilize the existing core services and UI managers to enable seamless data portability for prompts, model configurations, and user preferences.
Understanding the Core Architecture for Data Export and Import
The DataManager Orchestration Layer
At the center of the export/import system is DataManager located in packages/core/src/services/data/manager.ts. This class acts as a central coordinator that collects data from all domain-specific services and bundles them into a single JSON structure.
The exportData() method aggregates state from five core services:
// packages/core/src/services/data/manager.ts
export class DataManager {
async exportData(): Promise<string> {
const data: Record<string, any> = {};
data['history'] = await this.historyManager.exportData();
data['models'] = await this.modelManager.exportData();
data['templates'] = await this.templateManager.exportData();
data['settings'] = await this.preferenceService.exportData();
data['contexts'] = await this.contextRepo.exportData();
return JSON.stringify({ version: 1, data }, null, 2);
}
}
The resulting JSON includes a version identifier for future migration compatibility and pretty-printed formatting for human readability.
Individual Service Export Methods
Each domain service in the core layer implements standardized exportData() and importData() methods that handle type-specific serialization:
| Service | File Path | Export Return Type | Import Parameter | Contents |
|---|---|---|---|---|
| Model | packages/core/src/services/model/manager.ts |
Promise<TextModelConfig[]> |
TextModelConfig[] |
User-defined text model configurations |
| Template | packages/core/src/services/template/manager.ts |
Promise<Template[]> |
Template[] |
Prompt templates (system and user prompts) |
| Preference | packages/core/src/services/preference/service.ts |
Promise<Record<string, string>> |
Record<string, string> |
UI preferences and global settings |
| History | packages/core/src/services/history/manager.ts |
Promise<PromptRecord[]> |
PromptRecord[] |
Full prompt execution history |
| Context | packages/core/src/services/context/repo.ts |
Promise<ContextBundle> |
ContextBundle |
Variable contexts and saved workspaces |
These methods ensure type safety and validation during the import process, rejecting malformed data before it reaches the application state.
Implementing the UI Layer with DataImportExportManager
Creating the Export Workflow
The UI layer abstracts core functionality through DataImportExportManager in packages/ui/src/services/DataImportExportManager.ts. This class provides thin wrappers that components can call directly:
// packages/ui/src/services/DataImportExportManager.ts
export class DataImportExportManager {
constructor(private readonly dataManager: DataManager) {}
/** Returns a JSON string that can be saved as a file */
async exportAll(): Promise<string> {
return this.dataManager.exportData();
}
/** Takes a JSON string (from a file) and restores the state */
async importAll(json: string): Promise<void> {
await this.dataManager.importData(json);
}
}
To implement the download functionality in a Vue component, construct a Blob and trigger a browser download:
<!-- packages/web/src/components/toolbar/ExportImportBar.vue -->
<template>
<NSpace justify="end">
<NButton @click="exportAll">{{ t('toolbar.exportAll') }}</NButton>
<NUpload
:show-file-list="false"
:before-upload="handleImport"
accept="application/json"
>
<NButton>{{ t('toolbar.importAll') }}</NButton>
</NUpload>
</NSpace>
</template>
<script setup lang="ts">
import { DataImportExportManager } from '@/services/DataImportExportManager';
import { useToast } from '@/composables/ui/useToast';
import { dataManager } from '@/services/coreInstance';
const manager = new DataImportExportManager(dataManager);
const toast = useToast();
async function exportAll() {
const json = await manager.exportAll();
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `prompt-optimizer-${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
a.click();
URL.revokeObjectURL(url);
toast.success('Export completed');
}
function handleImport(file: File) {
const reader = new FileReader();
reader.onload = async () => {
try {
await manager.importAll(reader.result as string);
toast.success('Import completed');
} catch (e) {
toast.error('Import failed - invalid file format');
}
};
reader.readAsText(file);
return false;
}
</script>
Handling File Imports
The import process reverses the export flow. The DataManager.importData() method parses the JSON and distributes sections to their respective services:
// packages/core/src/services/data/manager.ts
async importData(raw: string): Promise<void> {
const { version, data } = JSON.parse(raw);
// Version compatibility check omitted for brevity
await this.historyManager.importData(data['history']);
await this.modelManager.importData(data['models']);
await this.templateManager.importData(data['templates']);
await this.preferenceService.importData(data['settings']);
await this.contextRepo.importData(data['contexts']);
}
Each service validates its incoming data before applying it to the application state, ensuring type safety and preventing corruption from malformed imports.
Desktop Integration with Electron IPC
For the desktop version of prompt-optimizer, the core services run through Electron IPC proxies. The DataImportExportManager works identically, but the underlying DataManager calls are forwarded to the main process:
// electron/main.ts (simplified)
import { ipcMain } from 'electron';
import { DataManager } from '@prompt-optimizer/core';
const dataManager = new DataManager(/* injected services */);
ipcMain.handle('export-all', async () => {
return dataManager.exportData();
});
ipcMain.handle('import-all', async (_e, json: string) => {
await dataManager.importData(json);
});
The renderer process accesses these through service proxies:
// packages/core/src/services/model/electron-proxy.ts
export class ModelElectronProxy implements ModelService {
async exportData(): Promise<TextModelConfig[]> {
return (this.electronAPI as any).model.exportData();
}
async importData(data: TextModelConfig[]): Promise<void> {
return (this.electronAPI as any).model.importData(data);
}
}
This architecture ensures that the same DataImportExportManager works across web and desktop environments without modification.
Summary
Implementing import and export functionality for prompts and configurations in prompt-optimizer leverages the existing layered architecture:
- Core Layer:
DataManagerinpackages/core/src/services/data/manager.tsorchestrates export/import across all domain services (templates, models, preferences, history, contexts) - Service Layer: Each manager implements
exportData()andimportData()with type-safe serialization - UI Layer:
DataImportExportManagerinpackages/ui/src/services/DataImportExportManager.tsprovides thin wrappers for components - Desktop Layer: Electron IPC proxies forward calls to the main process for file system access
Frequently Asked Questions
How does prompt-optimizer handle version compatibility during import?
The DataManager.exportData() method embeds a version identifier (currently version: 1) in the JSON root object. During import, the importData() method can check this version field to handle migrations or reject incompatible formats. Each core service also performs runtime validation on its incoming data structure before applying changes.
Can I export only specific data types instead of everything?
While the DataManager provides a comprehensive exportData() method that bundles all services, you can access individual service managers directly through the same API. For example, calling templateManager.exportData() returns only prompt templates, and modelManager.exportData() returns only model configurations. The DataImportExportManager can be extended with selective export methods that call these specific services.
What file format does prompt-optimizer use for exports?
Prompt-optimizer uses JSON as the primary export format. The exported file contains a structured object with version metadata and nested data sections for history, models, templates, settings, and contexts. For variable management specifically, the UI also supports CSV and TXT formats through the VariableManagerModal component, but the global export/import functionality relies on JSON.
How do I handle import errors in the UI?
The DataImportExportManager.importAll() method propagates validation errors from the core services. In your UI component, wrap the import call in a try-catch block to catch parsing errors or validation failures. The VariableManagerModal.vue component demonstrates this pattern by showing toast notifications for success states and error dialogs for failures, which you can replicate in your global import handler.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →