fix(data): include image model configs in backups
Include image model configuration data in app-wide export/import flows and initialize the image model manager before wiring backup services.
This commit is contained in:
1 parent
2d8720247f
commit
0c9ddd5063
5 files changed
+91
-8
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { IHistoryManager } from '../history/types';
|
||||
import { IModelManager } from '../model/types';
|
||||
import type { IImageModelManager } from '../image/types';
|
||||
import { ITemplateManager } from '../template/types';
|
||||
import { IPreferenceService } from '../preference/types';
|
||||
import { ContextRepo } from '../context/types';
|
||||
@@ -41,6 +42,7 @@ export interface IDataManager {
|
||||
|
||||
export class DataManager implements IDataManager {
|
||||
private modelManager: IModelManager;
|
||||
private imageModelManager?: IImageModelManager;
|
||||
private templateManager: ITemplateManager;
|
||||
private historyManager: IHistoryManager;
|
||||
private preferenceService: IPreferenceService;
|
||||
@@ -51,9 +53,11 @@ export class DataManager implements IDataManager {
|
||||
templateManager: ITemplateManager,
|
||||
historyManager: IHistoryManager,
|
||||
preferenceService: IPreferenceService,
|
||||
contextRepo: ContextRepo
|
||||
contextRepo: ContextRepo,
|
||||
imageModelManager?: IImageModelManager
|
||||
) {
|
||||
this.modelManager = modelManager;
|
||||
this.imageModelManager = imageModelManager;
|
||||
this.templateManager = templateManager;
|
||||
this.historyManager = historyManager;
|
||||
this.preferenceService = preferenceService;
|
||||
@@ -67,6 +71,9 @@ export class DataManager implements IDataManager {
|
||||
// 使用各服务的exportData接口,使用固定的键名保持兼容性
|
||||
data['history'] = await this.historyManager.exportData();
|
||||
data['models'] = await this.modelManager.exportData();
|
||||
if (this.imageModelManager) {
|
||||
data['imageModels'] = await this.imageModelManager.exportData();
|
||||
}
|
||||
data['userTemplates'] = await this.templateManager.exportData();
|
||||
data['userSettings'] = await this.preferenceService.exportData();
|
||||
data['contexts'] = await this.contextRepo.exportData();
|
||||
@@ -110,7 +117,7 @@ export class DataManager implements IDataManager {
|
||||
dataToImport = exportData.data;
|
||||
}
|
||||
// Old format: direct data object { history: [...], models: [...], ... }
|
||||
else if (exportData.history || exportData.models || exportData.userTemplates || exportData.userSettings || exportData.contexts) {
|
||||
else if (exportData.history || exportData.models || exportData.imageModels || exportData.userTemplates || exportData.userSettings || exportData.contexts) {
|
||||
dataToImport = exportData;
|
||||
}
|
||||
else {
|
||||
@@ -123,6 +130,7 @@ export class DataManager implements IDataManager {
|
||||
const serviceMap = [
|
||||
{ service: this.historyManager, dataKey: 'history' },
|
||||
{ service: this.modelManager, dataKey: 'models' },
|
||||
...(this.imageModelManager ? [{ service: this.imageModelManager, dataKey: 'imageModels' }] : []),
|
||||
{ service: this.templateManager, dataKey: 'userTemplates' },
|
||||
{ service: this.preferenceService, dataKey: 'userSettings' },
|
||||
{ service: this.contextRepo, dataKey: 'contexts' }
|
||||
@@ -161,7 +169,8 @@ export function createDataManager(
|
||||
templateManager: ITemplateManager,
|
||||
historyManager: IHistoryManager,
|
||||
preferenceService: IPreferenceService,
|
||||
contextRepo: ContextRepo
|
||||
contextRepo: ContextRepo,
|
||||
imageModelManager?: IImageModelManager
|
||||
): DataManager {
|
||||
return new DataManager(modelManager, templateManager, historyManager, preferenceService, contextRepo);
|
||||
return new DataManager(modelManager, templateManager, historyManager, preferenceService, contextRepo, imageModelManager);
|
||||
}
|
||||
@@ -8,6 +8,9 @@ import { TemplateLanguageService } from '../../../src/services/template/language
|
||||
import { MemoryStorageProvider } from '../../../src/services/storage/memoryStorageProvider';
|
||||
import { TextModelConfig } from '../../../src/services/model/types';
|
||||
import { TextAdapterRegistry } from '../../../src/services/llm/adapters/registry';
|
||||
import { ImageAdapterRegistry } from '../../../src/services/image/adapters/registry';
|
||||
import { ImageModelManager } from '../../../src/services/image-model/manager';
|
||||
import type { ImageModelConfig } from '../../../src/services/image/types';
|
||||
import { Template } from '../../../src/services/template/types';
|
||||
import { PromptRecord } from '../../../src/services/history/types';
|
||||
import { ContextRepo } from '../../../src/services/context/types';
|
||||
@@ -19,9 +22,11 @@ describe('DataManager Import/Export Integration', () => {
|
||||
let templateManager: TemplateManager;
|
||||
let historyManager: HistoryManager;
|
||||
let preferenceService: PreferenceService;
|
||||
let imageModelManager: ImageModelManager;
|
||||
let mockContextRepo: ContextRepo;
|
||||
let storageProvider: MemoryStorageProvider;
|
||||
let registry: TextAdapterRegistry;
|
||||
let imageRegistry: ImageAdapterRegistry;
|
||||
|
||||
beforeEach(async () => {
|
||||
storageProvider = new MemoryStorageProvider();
|
||||
@@ -29,9 +34,12 @@ describe('DataManager Import/Export Integration', () => {
|
||||
|
||||
// 创建真实的服务实例
|
||||
registry = new TextAdapterRegistry();
|
||||
imageRegistry = new ImageAdapterRegistry();
|
||||
preferenceService = new PreferenceService(storageProvider);
|
||||
modelManager = new ModelManager(storageProvider, registry);
|
||||
await modelManager.ensureInitialized();
|
||||
imageModelManager = new ImageModelManager(storageProvider, imageRegistry);
|
||||
await imageModelManager.ensureInitialized();
|
||||
|
||||
const languageService = new TemplateLanguageService(storageProvider, preferenceService);
|
||||
await languageService.initialize();
|
||||
@@ -59,7 +67,14 @@ describe('DataManager Import/Export Integration', () => {
|
||||
validateData: vi.fn().mockReturnValue(true),
|
||||
} as ContextRepo;
|
||||
|
||||
dataManager = new DataManager(modelManager, templateManager, historyManager, preferenceService, mockContextRepo);
|
||||
dataManager = new DataManager(
|
||||
modelManager,
|
||||
templateManager,
|
||||
historyManager,
|
||||
preferenceService,
|
||||
mockContextRepo,
|
||||
imageModelManager,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -85,6 +100,24 @@ describe('DataManager Import/Export Integration', () => {
|
||||
paramOverrides: {}
|
||||
};
|
||||
await modelManager.addModel('test-model-key', testModel);
|
||||
const imageAdapter = imageRegistry.getAdapter('openai');
|
||||
const imageProvider = imageAdapter.getProvider();
|
||||
const imageModel = imageAdapter.buildDefaultModel('gpt-image-1');
|
||||
const testImageModel: ImageModelConfig = {
|
||||
id: 'test-image-model-key',
|
||||
name: 'Test Image Model',
|
||||
providerId: imageProvider.id,
|
||||
modelId: imageModel.id,
|
||||
enabled: true,
|
||||
connectionConfig: {
|
||||
apiKey: 'test-image-key',
|
||||
baseURL: 'https://images.test.com/v1',
|
||||
},
|
||||
paramOverrides: {},
|
||||
provider: imageProvider,
|
||||
model: imageModel,
|
||||
};
|
||||
await imageModelManager.addConfig(testImageModel);
|
||||
|
||||
// 添加模板
|
||||
const testTemplate: Template = {
|
||||
@@ -129,6 +162,7 @@ describe('DataManager Import/Export Integration', () => {
|
||||
|
||||
// 验证导出的数据结构
|
||||
expect(exportedData.data).toHaveProperty('models');
|
||||
expect(exportedData.data).toHaveProperty('imageModels');
|
||||
expect(exportedData.data).toHaveProperty('userTemplates');
|
||||
expect(exportedData.data).toHaveProperty('history');
|
||||
expect(exportedData.data).toHaveProperty('userSettings');
|
||||
@@ -137,6 +171,9 @@ describe('DataManager Import/Export Integration', () => {
|
||||
const exportedModel = exportedData.data.models.find((m: any) => m.id === 'test-model-key');
|
||||
expect(exportedModel).toBeDefined();
|
||||
expect(exportedModel.name).toBe('Test Model');
|
||||
const exportedImageModel = exportedData.data.imageModels.find((m: any) => m.id === 'test-image-model-key');
|
||||
expect(exportedImageModel).toBeDefined();
|
||||
expect(exportedImageModel.name).toBe('Test Image Model');
|
||||
|
||||
const exportedTemplate = exportedData.data.userTemplates.find((t: any) => t.id === 'test-template');
|
||||
expect(exportedTemplate).toBeDefined();
|
||||
@@ -151,6 +188,7 @@ describe('DataManager Import/Export Integration', () => {
|
||||
// 3. 清空数据
|
||||
await historyManager.clearHistory();
|
||||
await preferenceService.clear();
|
||||
await imageModelManager.deleteConfig('test-image-model-key');
|
||||
// 注意:模型和模板的清空需要通过删除操作
|
||||
|
||||
// 4. 导入数据
|
||||
@@ -163,6 +201,10 @@ describe('DataManager Import/Export Integration', () => {
|
||||
expect(importedModel).toBeDefined();
|
||||
expect(importedModel?.name).toBe('Test Model');
|
||||
expect(importedModel?.enabled).toBe(true);
|
||||
const importedImageModel = await imageModelManager.getConfig('test-image-model-key');
|
||||
expect(importedImageModel).toBeDefined();
|
||||
expect(importedImageModel?.name).toBe('Test Image Model');
|
||||
expect(importedImageModel?.connectionConfig?.apiKey).toBe('test-image-key');
|
||||
|
||||
// 验证模板
|
||||
const importedTemplates = await templateManager.listTemplates();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import { DataManager } from '../../../src/services/data/manager';
|
||||
import { IHistoryManager } from '../../../src/services/history/types';
|
||||
import { IModelManager } from '../../../src/services/model/types';
|
||||
import type { IImageModelManager } from '../../../src/services/image/types';
|
||||
import { ITemplateManager, Template } from '../../../src/services/template/types';
|
||||
import { IPreferenceService } from '../../../src/services/preference/types';
|
||||
import { ContextRepo } from '../../../src/services/context/types';
|
||||
@@ -11,6 +12,7 @@ import { DATA_ERROR_CODES } from '../../../src/constants/error-codes';
|
||||
describe('DataManager', () => {
|
||||
let dataManager: DataManager;
|
||||
let mockModelManager: IModelManager;
|
||||
let mockImageModelManager: IImageModelManager;
|
||||
let mockTemplateManager: ITemplateManager;
|
||||
let mockHistoryManager: IHistoryManager;
|
||||
let mockPreferenceService: IPreferenceService;
|
||||
@@ -73,6 +75,21 @@ describe('DataManager', () => {
|
||||
validateData: vi.fn().mockReturnValue(true),
|
||||
};
|
||||
|
||||
mockImageModelManager = {
|
||||
ensureInitialized: vi.fn().mockResolvedValue(undefined),
|
||||
isInitialized: vi.fn().mockResolvedValue(true),
|
||||
addConfig: vi.fn().mockResolvedValue(undefined),
|
||||
updateConfig: vi.fn().mockResolvedValue(undefined),
|
||||
deleteConfig: vi.fn().mockResolvedValue(undefined),
|
||||
getConfig: vi.fn().mockResolvedValue(null),
|
||||
getAllConfigs: vi.fn().mockResolvedValue([]),
|
||||
getEnabledConfigs: vi.fn().mockResolvedValue([]),
|
||||
exportData: vi.fn().mockResolvedValue([]),
|
||||
importData: vi.fn().mockResolvedValue(undefined),
|
||||
getDataType: vi.fn().mockResolvedValue('image-model-configs'),
|
||||
validateData: vi.fn().mockReturnValue(true),
|
||||
};
|
||||
|
||||
mockHistoryManager = {
|
||||
getRecords: vi.fn().mockResolvedValue([]),
|
||||
addRecord: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -117,7 +134,8 @@ describe('DataManager', () => {
|
||||
mockTemplateManager,
|
||||
mockHistoryManager,
|
||||
mockPreferenceService,
|
||||
mockContextRepo
|
||||
mockContextRepo,
|
||||
mockImageModelManager
|
||||
);
|
||||
});
|
||||
|
||||
@@ -128,6 +146,7 @@ describe('DataManager', () => {
|
||||
describe('exportAllData', () => {
|
||||
it('should fetch data from all managers and return a JSON string', async () => {
|
||||
const models = [{ id: 'model1', name: 'Test Model' }];
|
||||
const imageModels = [{ id: 'image-model1', name: 'Test Image Model' }];
|
||||
const templates: Template[] = [{ id: 'tpl1', name: 'Test Template', content: 'c', isBuiltin: false, metadata: { templateType: 'optimize', version: '1.0', lastModified: 0 } }];
|
||||
const history = [{
|
||||
id: 'hist1',
|
||||
@@ -142,6 +161,7 @@ describe('DataManager', () => {
|
||||
}];
|
||||
|
||||
(mockModelManager.exportData as vi.Mock).mockResolvedValue(models);
|
||||
(mockImageModelManager.exportData as vi.Mock).mockResolvedValue(imageModels);
|
||||
(mockTemplateManager.exportData as vi.Mock).mockResolvedValue(templates.filter(t => !t.isBuiltin));
|
||||
(mockHistoryManager.exportData as vi.Mock).mockResolvedValue(history as any);
|
||||
(mockPreferenceService.exportData as vi.Mock).mockResolvedValue({});
|
||||
@@ -151,6 +171,7 @@ describe('DataManager', () => {
|
||||
|
||||
expect(data.version).toBe(1);
|
||||
expect(data.data.models).toEqual(models);
|
||||
expect(data.data.imageModels).toEqual(imageModels);
|
||||
expect(data.data.userTemplates).toEqual(templates.filter(t => !t.isBuiltin));
|
||||
expect(data.data.history).toEqual(history);
|
||||
});
|
||||
@@ -161,6 +182,7 @@ describe('DataManager', () => {
|
||||
version: 1,
|
||||
data: {
|
||||
models: [{ key: 'imp-model1', id: 'imp-model1', name: 'Imported Model' }],
|
||||
imageModels: [{ id: 'imp-image-model1', name: 'Imported Image Model' }],
|
||||
userTemplates: [{ id: 'imp-tpl1', name: 'Imported Template', content: 'test content', isBuiltin: false, metadata: { templateType: 'optimize', version: '1.0', lastModified: 0 } }],
|
||||
history: [{
|
||||
id: 'imp-hist1',
|
||||
@@ -181,6 +203,7 @@ describe('DataManager', () => {
|
||||
await dataManager.importAllData(JSON.stringify(importData));
|
||||
|
||||
expect(mockModelManager.importData).toHaveBeenCalledWith(importData.data.models);
|
||||
expect(mockImageModelManager.importData).toHaveBeenCalledWith(importData.data.imageModels);
|
||||
expect(mockTemplateManager.importData).toHaveBeenCalledWith(importData.data.userTemplates);
|
||||
expect(mockHistoryManager.importData).toHaveBeenCalledWith(importData.data.history);
|
||||
expect(mockPreferenceService.importData).toHaveBeenCalledWith(importData.data.userSettings);
|
||||
@@ -210,12 +233,14 @@ describe('DataManager', () => {
|
||||
templateId: 'old-template'
|
||||
}],
|
||||
models: [{ key: 'old-model1', name: 'Old Format Model' }],
|
||||
imageModels: [{ id: 'old-image-model1', name: 'Old Image Model' }],
|
||||
userTemplates: [{ id: 'old-tpl1', name: 'Old Format Template', content: 'old content', isBuiltin: false, metadata: { templateType: 'optimize', version: '1.0', lastModified: 0 } }],
|
||||
userSettings: { 'app:settings:ui:theme-id': 'light' },
|
||||
};
|
||||
|
||||
await expect(dataManager.importAllData(JSON.stringify(oldFormatData))).resolves.not.toThrow();
|
||||
expect(mockModelManager.importData).toHaveBeenCalled();
|
||||
expect(mockImageModelManager.importData).toHaveBeenCalled();
|
||||
expect(mockTemplateManager.importData).toHaveBeenCalled();
|
||||
expect(mockHistoryManager.importData).toHaveBeenCalled();
|
||||
expect(mockPreferenceService.importData).toHaveBeenCalled();
|
||||
|
||||
@@ -636,7 +636,7 @@ async function initializeServices() {
|
||||
contextRepo = createContextRepo(storageProvider);
|
||||
|
||||
console.log('[DESKTOP] Creating Data manager...');
|
||||
dataManager = createDataManager(modelManager, templateManager, historyManager, preferenceService, contextRepo);
|
||||
dataManager = createDataManager(modelManager, templateManager, historyManager, preferenceService, contextRepo, imageModelManager);
|
||||
|
||||
console.log('[DESKTOP] Creating Favorite manager...');
|
||||
favoriteManager = new FavoriteManager(storageProvider);
|
||||
|
||||
@@ -419,7 +419,14 @@ export function useAppInitializer(): {
|
||||
const contextRepo = createContextRepo(storageProvider);
|
||||
|
||||
// 创建 DataManager(需要contextRepo)
|
||||
dataManager = createDataManager(modelManagerInstance, templateManagerInstance, historyManagerInstance, preferenceService, contextRepo);
|
||||
dataManager = createDataManager(
|
||||
modelManagerInstance,
|
||||
templateManagerInstance,
|
||||
historyManagerInstance,
|
||||
preferenceService,
|
||||
contextRepo,
|
||||
imageModelManagerInstance,
|
||||
);
|
||||
|
||||
// 创建收藏管理器
|
||||
favoriteManager = new FavoriteManager(storageProvider);
|
||||
|
||||
Reference in new issue
Block a user