feat: support import and export with prompts (#330)
* feat: support import and export prompts * test: update importExports.test.ts * Delete .gitpod.yml
This commit is contained in:
		
							parent
							
								
									462ca9bb04
								
							
						
					
					
						commit
						d68f77867d
					
				|  | @ -1,4 +1,4 @@ | ||||||
| import { ExportFormatV1, ExportFormatV2 } from '@/types/export'; | import { ExportFormatV1, ExportFormatV2, ExportFormatV4 } from '@/types/export'; | ||||||
| import { OpenAIModels, OpenAIModelID } from '@/types/openai'; | import { OpenAIModels, OpenAIModelID } from '@/types/openai'; | ||||||
| import { DEFAULT_SYSTEM_PROMPT } from '@/utils/app/const'; | import { DEFAULT_SYSTEM_PROMPT } from '@/utils/app/const'; | ||||||
| import { it, describe, expect } from 'vitest'; | import { it, describe, expect } from 'vitest'; | ||||||
|  | @ -8,6 +8,7 @@ import { | ||||||
|   isExportFormatV1, |   isExportFormatV1, | ||||||
|   isExportFormatV2, |   isExportFormatV2, | ||||||
|   isExportFormatV3, |   isExportFormatV3, | ||||||
|  |   isExportFormatV4, | ||||||
|   isLatestExportFormat, |   isLatestExportFormat, | ||||||
| } from '@/utils/app/importExport'; | } from '@/utils/app/importExport'; | ||||||
| 
 | 
 | ||||||
|  | @ -47,6 +48,18 @@ describe('Export Format Functions', () => { | ||||||
|       expect(isExportFormatV3(obj)).toBe(false); |       expect(isExportFormatV3(obj)).toBe(false); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   describe('isExportFormatV4', () => { | ||||||
|  |     it('should return true for v4 format', () => { | ||||||
|  |       const obj = { version: 4, history: [], folders: [], prompts: [] }; | ||||||
|  |       expect(isExportFormatV4(obj)).toBe(true); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return false for non-v4 formats', () => { | ||||||
|  |       const obj = { version: 5, history: [], folders: [], prompts: [] }; | ||||||
|  |       expect(isExportFormatV4(obj)).toBe(false); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| describe('cleanData Functions', () => { | describe('cleanData Functions', () => { | ||||||
|  | @ -71,7 +84,7 @@ describe('cleanData Functions', () => { | ||||||
|       const obj = cleanData(data); |       const obj = cleanData(data); | ||||||
|       expect(isLatestExportFormat(obj)).toBe(true); |       expect(isLatestExportFormat(obj)).toBe(true); | ||||||
|       expect(obj).toEqual({ |       expect(obj).toEqual({ | ||||||
|         version: 3, |         version: 4, | ||||||
|         history: [ |         history: [ | ||||||
|           { |           { | ||||||
|             id: 1, |             id: 1, | ||||||
|  | @ -92,6 +105,7 @@ describe('cleanData Functions', () => { | ||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
|         folders: [], |         folders: [], | ||||||
|  |         prompts:[] | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | @ -125,7 +139,7 @@ describe('cleanData Functions', () => { | ||||||
|       const obj = cleanData(data); |       const obj = cleanData(data); | ||||||
|       expect(isLatestExportFormat(obj)).toBe(true); |       expect(isLatestExportFormat(obj)).toBe(true); | ||||||
|       expect(obj).toEqual({ |       expect(obj).toEqual({ | ||||||
|         version: 3, |         version: 4, | ||||||
|         history: [ |         history: [ | ||||||
|           { |           { | ||||||
|             id: '1', |             id: '1', | ||||||
|  | @ -152,7 +166,96 @@ describe('cleanData Functions', () => { | ||||||
|             type: 'chat', |             type: 'chat', | ||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
|  |         prompts: [], | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   describe('cleaning v4 data', () => { | ||||||
|  |     it('should return the latest format', () => { | ||||||
|  |       const data = { | ||||||
|  |         version: 4, | ||||||
|  |         history: [ | ||||||
|  |           { | ||||||
|  |             id: '1', | ||||||
|  |             name: 'conversation 1', | ||||||
|  |             messages: [ | ||||||
|  |               { | ||||||
|  |                 role: 'user', | ||||||
|  |                 content: "what's up ?", | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 role: 'assistant', | ||||||
|  |                 content: 'Hi', | ||||||
|  |               }, | ||||||
|  |             ], | ||||||
|  |             model: OpenAIModels[OpenAIModelID.GPT_3_5], | ||||||
|  |             prompt: DEFAULT_SYSTEM_PROMPT, | ||||||
|  |             folderId: null, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         folders: [ | ||||||
|  |           { | ||||||
|  |             id: '1', | ||||||
|  |             name: 'folder 1', | ||||||
|  |             type: 'chat', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         prompts: [ | ||||||
|  |           { | ||||||
|  |             id: '1', | ||||||
|  |             name: 'prompt 1', | ||||||
|  |             description: '', | ||||||
|  |             content: '', | ||||||
|  |             model: OpenAIModels[OpenAIModelID.GPT_3_5], | ||||||
|  |             folderId: null, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       } as ExportFormatV4; | ||||||
|  |        | ||||||
|  |       const obj = cleanData(data); | ||||||
|  |       expect(isLatestExportFormat(obj)).toBe(true); | ||||||
|  |       expect(obj).toEqual({ | ||||||
|  |         version: 4, | ||||||
|  |         history: [ | ||||||
|  |           { | ||||||
|  |             id: '1', | ||||||
|  |             name: 'conversation 1', | ||||||
|  |             messages: [ | ||||||
|  |               { | ||||||
|  |                 role: 'user', | ||||||
|  |                 content: "what's up ?", | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 role: 'assistant', | ||||||
|  |                 content: 'Hi', | ||||||
|  |               }, | ||||||
|  |             ], | ||||||
|  |             model: OpenAIModels[OpenAIModelID.GPT_3_5], | ||||||
|  |             prompt: DEFAULT_SYSTEM_PROMPT, | ||||||
|  |             folderId: null, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         folders: [ | ||||||
|  |           { | ||||||
|  |             id: '1', | ||||||
|  |             name: 'folder 1', | ||||||
|  |             type: 'chat', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         prompts: [ | ||||||
|  |           { | ||||||
|  |             id: '1', | ||||||
|  |             name: 'prompt 1', | ||||||
|  |             description: '', | ||||||
|  |             content: '', | ||||||
|  |             model: OpenAIModels[OpenAIModelID.GPT_3_5], | ||||||
|  |             folderId: null, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  | 
 | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |    | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -296,11 +296,12 @@ const Home: React.FC<HomeProps> = ({ | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const handleImportConversations = (data: SupportedExportFormats) => { |   const handleImportConversations = (data: SupportedExportFormats) => { | ||||||
|     const { history, folders }: LatestExportFormat = importData(data); |     const { history, folders, prompts }: LatestExportFormat = importData(data); | ||||||
| 
 | 
 | ||||||
|     setConversations(history); |     setConversations(history); | ||||||
|     setSelectedConversation(history[history.length - 1]); |     setSelectedConversation(history[history.length - 1]); | ||||||
|     setFolders(folders); |     setFolders(folders); | ||||||
|  |     setPrompts(prompts); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const handleSelectConversation = (conversation: Conversation) => { |   const handleSelectConversation = (conversation: Conversation) => { | ||||||
|  |  | ||||||
|  | @ -1,12 +1,14 @@ | ||||||
| import { Conversation, Message } from './chat'; | import { Conversation, Message } from './chat'; | ||||||
| import { Folder } from './folder'; | import { Folder } from './folder'; | ||||||
| import { OpenAIModel } from './openai'; | import { OpenAIModel } from './openai'; | ||||||
|  | import { Prompt } from './prompt'; | ||||||
| 
 | 
 | ||||||
| export type SupportedExportFormats = | export type SupportedExportFormats = | ||||||
|   | ExportFormatV1 |   | ExportFormatV1 | ||||||
|   | ExportFormatV2 |   | ExportFormatV2 | ||||||
|   | ExportFormatV3; |   | ExportFormatV3 | ||||||
| export type LatestExportFormat = ExportFormatV3; |   | ExportFormatV4; | ||||||
|  | export type LatestExportFormat = ExportFormatV4; | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| interface ConversationV1 { | interface ConversationV1 { | ||||||
|  | @ -34,3 +36,10 @@ export interface ExportFormatV3 { | ||||||
|   history: Conversation[]; |   history: Conversation[]; | ||||||
|   folders: Folder[]; |   folders: Folder[]; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export interface ExportFormatV4 { | ||||||
|  |   version: 4; | ||||||
|  |   history: Conversation[]; | ||||||
|  |   folders: Folder[]; | ||||||
|  |   prompts: Prompt[] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { | ||||||
|   ExportFormatV1, |   ExportFormatV1, | ||||||
|   ExportFormatV2, |   ExportFormatV2, | ||||||
|   ExportFormatV3, |   ExportFormatV3, | ||||||
|  |   ExportFormatV4, | ||||||
|   LatestExportFormat, |   LatestExportFormat, | ||||||
|   SupportedExportFormats, |   SupportedExportFormats, | ||||||
| } from '@/types/export'; | } from '@/types/export'; | ||||||
|  | @ -19,33 +20,44 @@ export function isExportFormatV3(obj: any): obj is ExportFormatV3 { | ||||||
|   return obj.version === 3; |   return obj.version === 3; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const isLatestExportFormat = isExportFormatV3; | export function isExportFormatV4(obj: any): obj is ExportFormatV4 { | ||||||
|  |   return obj.version === 4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const isLatestExportFormat = isExportFormatV4; | ||||||
| 
 | 
 | ||||||
| export function cleanData(data: SupportedExportFormats): LatestExportFormat { | export function cleanData(data: SupportedExportFormats): LatestExportFormat { | ||||||
|   if (isExportFormatV1(data)) { |   if (isExportFormatV1(data)) { | ||||||
|     return { |     return { | ||||||
|       version: 3, |       version: 4, | ||||||
|       history: cleanConversationHistory(data), |       history: cleanConversationHistory(data), | ||||||
|       folders: [], |       folders: [], | ||||||
|  |       prompts: [], | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (isExportFormatV2(data)) { |   if (isExportFormatV2(data)) { | ||||||
|     return { |     return { | ||||||
|       version: 3, |       version: 4, | ||||||
|       history: cleanConversationHistory(data.history || []), |       history: cleanConversationHistory(data.history || []), | ||||||
|       folders: (data.folders || []).map((chatFolder) => ({ |       folders: (data.folders || []).map((chatFolder) => ({ | ||||||
|         id: chatFolder.id.toString(), |         id: chatFolder.id.toString(), | ||||||
|         name: chatFolder.name, |         name: chatFolder.name, | ||||||
|         type: 'chat', |         type: 'chat', | ||||||
|       })), |       })), | ||||||
|  |       prompts: [], | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (isExportFormatV3(data)) {     |   if (isExportFormatV3(data)) {     | ||||||
|  |     return {...data, version: 4, prompts: []}; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if(isExportFormatV4(data)){ | ||||||
|     return data; |     return data; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|   throw new Error('Unsupported data format'); |   throw new Error('Unsupported data format'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -59,6 +71,7 @@ function currentDate() { | ||||||
| export const exportData = () => { | export const exportData = () => { | ||||||
|   let history = localStorage.getItem('conversationHistory'); |   let history = localStorage.getItem('conversationHistory'); | ||||||
|   let folders = localStorage.getItem('folders'); |   let folders = localStorage.getItem('folders'); | ||||||
|  |   let prompts = localStorage.getItem('prompts'); | ||||||
| 
 | 
 | ||||||
|   if (history) { |   if (history) { | ||||||
|     history = JSON.parse(history); |     history = JSON.parse(history); | ||||||
|  | @ -68,10 +81,15 @@ export const exportData = () => { | ||||||
|     folders = JSON.parse(folders); |     folders = JSON.parse(folders); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   if(prompts){ | ||||||
|  |     prompts = JSON.parse(prompts); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   const data = { |   const data = { | ||||||
|     version: 3, |     version: 4, | ||||||
|     history: history || [], |     history: history || [], | ||||||
|     folders: folders || [], |     folders: folders || [], | ||||||
|  |     prompts: prompts || [], | ||||||
|   } as LatestExportFormat; |   } as LatestExportFormat; | ||||||
| 
 | 
 | ||||||
|   const blob = new Blob([JSON.stringify(data, null, 2)], { |   const blob = new Blob([JSON.stringify(data, null, 2)], { | ||||||
|  | @ -92,15 +110,17 @@ export const importData = ( | ||||||
|   data: SupportedExportFormats, |   data: SupportedExportFormats, | ||||||
| ): LatestExportFormat => { | ): LatestExportFormat => { | ||||||
|   const cleanedData = cleanData(data); |   const cleanedData = cleanData(data); | ||||||
|  |   const { history,folders, prompts } = cleanedData; | ||||||
| 
 | 
 | ||||||
|   const conversations = cleanedData.history; |   const conversations = history; | ||||||
|   localStorage.setItem('conversationHistory', JSON.stringify(conversations)); |   localStorage.setItem('conversationHistory', JSON.stringify(conversations)); | ||||||
|   localStorage.setItem( |   localStorage.setItem( | ||||||
|     'selectedConversation', |     'selectedConversation', | ||||||
|     JSON.stringify(conversations[conversations.length - 1]), |     JSON.stringify(conversations[conversations.length - 1]), | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   localStorage.setItem('folders', JSON.stringify(cleanedData.folders)); |   localStorage.setItem('folders', JSON.stringify(folders)); | ||||||
|  |   localStorage.setItem('prompts', JSON.stringify(prompts)); | ||||||
| 
 | 
 | ||||||
|   return cleanedData; |   return cleanedData; | ||||||
| }; | }; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue