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:
Superman 2023-04-02 12:59:51 +08:00 committed by GitHub
parent 462ca9bb04
commit d68f77867d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 146 additions and 13 deletions

View File

@ -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,
},
],
});
});
});
}); });

View File

@ -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) => {

View File

@ -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[]
}

View File

@ -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;
}; };