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