Add story cloning, fix storage caching issue
This commit is contained in:
parent
a660ab8161
commit
3f41cea5eb
|
|
@ -55,18 +55,14 @@ export const Header = () => {
|
||||||
assistantOpen.setFalse();
|
assistantOpen.setFalse();
|
||||||
}, [addSwipe, messages]);
|
}, [addSwipe, messages]);
|
||||||
|
|
||||||
const handleSetBannedWords = useCallback((e: Event) => {
|
const handleSetBannedWords = useInputCallback((text) => {
|
||||||
if (e.target instanceof HTMLTextAreaElement) {
|
const words = text.split('\n');
|
||||||
const words = e.target.value.split('\n');
|
setBannedWords(words);
|
||||||
setBannedWords(words);
|
|
||||||
}
|
|
||||||
}, [setBannedWords]);
|
}, [setBannedWords]);
|
||||||
|
|
||||||
const handleBlurBannedWords = useCallback((e: Event) => {
|
const handleBlurBannedWords = useInputCallback((text) => {
|
||||||
if (e.target instanceof HTMLTextAreaElement) {
|
const words = text.toLowerCase().split('\n').sort();
|
||||||
const words = e.target.value.toLowerCase().split('\n').sort();
|
setBannedWords(words);
|
||||||
setBannedWords(words);
|
|
||||||
}
|
|
||||||
}, [setBannedWords]);
|
}, [setBannedWords]);
|
||||||
|
|
||||||
const handleSetSummaryEnabled = useCallback((e: Event) => {
|
const handleSetSummaryEnabled = useCallback((e: Event) => {
|
||||||
|
|
@ -76,21 +72,21 @@ export const Header = () => {
|
||||||
}, [setSummaryEnabled]);
|
}, [setSummaryEnabled]);
|
||||||
|
|
||||||
const handleChangeStory = useInputCallback((story) => {
|
const handleChangeStory = useInputCallback((story) => {
|
||||||
if (story === '@new') {
|
setCurrentStory(story);
|
||||||
const id = prompt('Story id');
|
|
||||||
if (id) {
|
|
||||||
createStory(id);
|
|
||||||
setCurrentStory(id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setCurrentStory(story);
|
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDeleteStory = useCallback(() => {
|
const handleDeleteStory = useCallback(() => {
|
||||||
if (confirm(`Delete story "${currentStory}"?`)) {
|
if (confirm(`Delete story "${currentStory}"?`)) {
|
||||||
deleteStory(currentStory);
|
deleteStory(currentStory);
|
||||||
}
|
}
|
||||||
|
}, [currentStory]);
|
||||||
|
|
||||||
|
const handleDuplicateStory = useCallback(() => {
|
||||||
|
const id = prompt('Story id');
|
||||||
|
if (id) {
|
||||||
|
createStory(id, currentStory);
|
||||||
|
setCurrentStory(id);
|
||||||
|
}
|
||||||
}, [currentStory]);
|
}, [currentStory]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -137,8 +133,10 @@ export const Header = () => {
|
||||||
{Object.keys(stories).map((story) => (
|
{Object.keys(stories).map((story) => (
|
||||||
<option key={story} value={story}>{story}</option>
|
<option key={story} value={story}>{story}</option>
|
||||||
))}
|
))}
|
||||||
<option value='@new'>New Story...</option>
|
|
||||||
</select>
|
</select>
|
||||||
|
<button class='icon' onClick={handleDuplicateStory}>
|
||||||
|
➕
|
||||||
|
</button>
|
||||||
{currentStory !== DEFAULT_STORY
|
{currentStory !== DEFAULT_STORY
|
||||||
? <button class='icon' onClick={handleDeleteStory}>
|
? <button class='icon' onClick={handleDeleteStory}>
|
||||||
🗑️
|
🗑️
|
||||||
|
|
|
||||||
|
|
@ -101,9 +101,9 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
|
||||||
let content = swipe?.content ?? '';
|
let content = swipe?.content ?? '';
|
||||||
if (role === 'user' && usersRemaining > keepUsers) {
|
if (role === 'user' && usersRemaining > keepUsers) {
|
||||||
usersRemaining--;
|
usersRemaining--;
|
||||||
} else if (role === 'assistant' && templateMessages.at(-1).role === 'assistant') {
|
} else if (role === 'assistant' && templateMessages.at(-1)!.role === 'assistant') {
|
||||||
wasStory = true;
|
wasStory = true;
|
||||||
templateMessages.at(-1).content += '\n\n' + content;
|
templateMessages.at(-1)!.content += '\n\n' + content;
|
||||||
} else if (role === 'user' && !message.technical) {
|
} else if (role === 'user' && !message.technical) {
|
||||||
templateMessages.push({
|
templateMessages.push({
|
||||||
role: message.role,
|
role: message.role,
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ interface StoryActionSetLore {
|
||||||
interface StoryActionWithId {
|
interface StoryActionWithId {
|
||||||
action: 'create' | 'delete';
|
action: 'create' | 'delete';
|
||||||
id: string;
|
id: string;
|
||||||
|
fromId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type StoryAction = StoryActionSetCurrent | StoryActionSetLore | StoryActionSetMessages | StoryActionWithId;
|
type StoryAction = StoryActionSetCurrent | StoryActionSetLore | StoryActionSetMessages | StoryActionWithId;
|
||||||
|
|
||||||
interface IContext {
|
interface IContext {
|
||||||
currentConnection: number;
|
connection: IConnection;
|
||||||
availableConnections: IConnection[];
|
|
||||||
input: string;
|
input: string;
|
||||||
systemPrompt: string;
|
systemPrompt: string;
|
||||||
userPrompt: string;
|
userPrompt: string;
|
||||||
|
|
@ -57,15 +57,12 @@ interface IContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IComputableContext {
|
interface IComputableContext {
|
||||||
connection: IConnection;
|
|
||||||
lore: string;
|
lore: string;
|
||||||
messages: IMessage[];
|
messages: IMessage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IActions {
|
interface IActions {
|
||||||
setConnection: (connection: IConnection) => void;
|
setConnection: (connection: IConnection) => void;
|
||||||
setAvailableConnections: (connections: IConnection[]) => void;
|
|
||||||
setCurrentConnection: (connection: number) => void;
|
|
||||||
setInput: (url: string | Event) => void;
|
setInput: (url: string | Event) => void;
|
||||||
setInstruct: (template: string | Event) => void;
|
setInstruct: (template: string | Event) => void;
|
||||||
setLore: (lore: string | Event) => void;
|
setLore: (lore: string | Event) => void;
|
||||||
|
|
@ -90,7 +87,7 @@ interface IActions {
|
||||||
continueMessage: (continueLast?: boolean) => void;
|
continueMessage: (continueLast?: boolean) => void;
|
||||||
|
|
||||||
setCurrentStory: (id: string) => void;
|
setCurrentStory: (id: string) => void;
|
||||||
createStory: (id: string) => void;
|
createStory: (id: string, fromId?: string) => void;
|
||||||
deleteStory: (id: string) => void;
|
deleteStory: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,12 +116,11 @@ export const INSTRUCTS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_CONTEXT: IContext = {
|
const DEFAULT_CONTEXT: IContext = {
|
||||||
currentConnection: 0,
|
connection: {
|
||||||
availableConnections: [{
|
|
||||||
type: 'kobold',
|
type: 'kobold',
|
||||||
url: 'http://localhost:5001',
|
url: 'http://localhost:5001',
|
||||||
instruct: INSTRUCT_MISTRAL,
|
instruct: INSTRUCT_MISTRAL,
|
||||||
}],
|
},
|
||||||
input: '',
|
input: '',
|
||||||
systemPrompt: 'You are a creative writer. Write a story based on the world description below. Story should be adult and mature; and could include swearing, violence and unfairness. Portray characters realistically and stay in the lore.',
|
systemPrompt: 'You are a creative writer. Write a story based on the world description below. Story should be adult and mature; and could include swearing, violence and unfairness. Portray characters realistically and stay in the lore.',
|
||||||
stories: {},
|
stories: {},
|
||||||
|
|
@ -155,7 +151,6 @@ const EMPTY_STORY: IStory = {
|
||||||
|
|
||||||
const saveContext = throttle(async (context: IContext & IComputableContext) => {
|
const saveContext = throttle(async (context: IContext & IComputableContext) => {
|
||||||
const contextToSave: Partial<IContext & IComputableContext> = { ...context };
|
const contextToSave: Partial<IContext & IComputableContext> = { ...context };
|
||||||
delete contextToSave.connection;
|
|
||||||
delete contextToSave.triggerNext;
|
delete contextToSave.triggerNext;
|
||||||
delete contextToSave.continueLast;
|
delete contextToSave.continueLast;
|
||||||
delete contextToSave.lore;
|
delete contextToSave.lore;
|
||||||
|
|
@ -176,7 +171,10 @@ const storyReducer = (state: StoriesState, action: StoryAction): StoriesState =>
|
||||||
if ('id' in action) {
|
if ('id' in action) {
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case 'create':
|
case 'create':
|
||||||
stories[action.id] = EMPTY_STORY;
|
stories[action.id] = {
|
||||||
|
...EMPTY_STORY,
|
||||||
|
lore: (action.fromId && stories[action.fromId]?.lore) ?? EMPTY_STORY.lore,
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
if (action.id !== DEFAULT_STORY) {
|
if (action.id !== DEFAULT_STORY) {
|
||||||
|
|
@ -204,8 +202,7 @@ const storyReducer = (state: StoriesState, action: StoryAction): StoriesState =>
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StateContextProvider = ({ children }: { children?: any }) => {
|
export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
const [currentConnection, setCurrentConnection] = useState<number>(loadedContext.currentConnection);
|
const [connection, setConnection] = useState<IConnection>(loadedContext.connection);
|
||||||
const [availableConnections, setAvailableConnections] = useState<IConnection[]>(loadedContext.availableConnections);
|
|
||||||
const [input, setInput] = useInputState(loadedContext.input);
|
const [input, setInput] = useInputState(loadedContext.input);
|
||||||
const [systemPrompt, setSystemPrompt] = useInputState(loadedContext.systemPrompt);
|
const [systemPrompt, setSystemPrompt] = useInputState(loadedContext.systemPrompt);
|
||||||
const [userPrompt, setUserPrompt] = useInputState(loadedContext.userPrompt);
|
const [userPrompt, setUserPrompt] = useInputState(loadedContext.userPrompt);
|
||||||
|
|
@ -221,22 +218,10 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
messages: loadedContext.stories[loadedContext.currentStory]?.messages ?? [],
|
messages: loadedContext.stories[loadedContext.currentStory]?.messages ?? [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const connection = availableConnections[currentConnection] ?? DEFAULT_CONTEXT.availableConnections[0];
|
|
||||||
|
|
||||||
const [triggerNext, setTriggerNext] = useState(false);
|
const [triggerNext, setTriggerNext] = useState(false);
|
||||||
const [continueLast, setContinueLast] = useState(false);
|
const [continueLast, setContinueLast] = useState(false);
|
||||||
const [instruct, setInstruct] = useInputState(connection.instruct);
|
const [instruct, setInstruct] = useInputState(connection.instruct);
|
||||||
|
|
||||||
const setConnection = useCallback((c: IConnection) => {
|
|
||||||
setAvailableConnections(availableConnections.map((ac, ai) => {
|
|
||||||
if (ai === currentConnection) {
|
|
||||||
return c;
|
|
||||||
} else {
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}, [availableConnections, currentConnection]);
|
|
||||||
|
|
||||||
useEffect(() => setConnection({ ...connection, instruct }), [instruct]);
|
useEffect(() => setConnection({ ...connection, instruct }), [instruct]);
|
||||||
|
|
||||||
const setLore = useInputCallback((lore) => {
|
const setLore = useInputCallback((lore) => {
|
||||||
|
|
@ -253,7 +238,6 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
|
|
||||||
const actions: IActions = useMemo(() => ({
|
const actions: IActions = useMemo(() => ({
|
||||||
setConnection,
|
setConnection,
|
||||||
setCurrentConnection,
|
|
||||||
setInput,
|
setInput,
|
||||||
setInstruct,
|
setInstruct,
|
||||||
setSystemPrompt,
|
setSystemPrompt,
|
||||||
|
|
@ -268,7 +252,6 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
setCurrentStory,
|
setCurrentStory,
|
||||||
|
|
||||||
setBannedWords: (words) => setBannedWords(words.slice()),
|
setBannedWords: (words) => setBannedWords(words.slice()),
|
||||||
setAvailableConnections: (connections) => setAvailableConnections(connections.slice()),
|
|
||||||
|
|
||||||
setMessages: (newMessages) => setMessages(newMessages.slice()),
|
setMessages: (newMessages) => setMessages(newMessages.slice()),
|
||||||
addMessage: (content, role, triggerNext = false) => {
|
addMessage: (content, role, triggerNext = false) => {
|
||||||
|
|
@ -297,7 +280,7 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
const swipes = message.swipes.slice();
|
const swipes = message.swipes.slice();
|
||||||
const latestSwipe = swipes.at(-1);
|
const latestSwipe = swipes.at(-1);
|
||||||
if (currentSwipe >= swipes.length) {
|
if (currentSwipe >= swipes.length) {
|
||||||
if (latestSwipe.content.length > 0) {
|
if (latestSwipe && latestSwipe.content.length > 0) {
|
||||||
currentSwipe = swipes.length;
|
currentSwipe = swipes.length;
|
||||||
swipes.push({ content: '', cost: 0 });
|
swipes.push({ content: '', cost: 0 });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -340,8 +323,8 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
setTriggerNext(true);
|
setTriggerNext(true);
|
||||||
setContinueLast(c);
|
setContinueLast(c);
|
||||||
},
|
},
|
||||||
createStory: (id: string) => {
|
createStory: (id: string, fromId?: string) => {
|
||||||
storyDispatch({ id, action: 'create' });
|
storyDispatch({ id, action: 'create', fromId });
|
||||||
},
|
},
|
||||||
deleteStory: (id: string) => {
|
deleteStory: (id: string) => {
|
||||||
storyDispatch({ id, action: 'delete' });
|
storyDispatch({ id, action: 'delete' });
|
||||||
|
|
@ -350,8 +333,6 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
|
|
||||||
const rawContext: IContext & IComputableContext = {
|
const rawContext: IContext & IComputableContext = {
|
||||||
connection,
|
connection,
|
||||||
currentConnection,
|
|
||||||
availableConnections,
|
|
||||||
input,
|
input,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
userPrompt,
|
userPrompt,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export const loadObject = async <T>(key: string, defaultObject: T): Promise<T> =
|
||||||
|
|
||||||
let remoteObject: Partial<T> = {};
|
let remoteObject: Partial<T> = {};
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://demo.pabloader.ru/storage/${key}`);
|
const response = await fetch(`https://demo.pabloader.ru/storage/${key}?_=${Math.random()}`);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const compressedData = await response.blob();
|
const compressedData = await response.blob();
|
||||||
const decompressedData = await decompressBlob(compressedData);
|
const decompressedData = await decompressBlob(compressedData);
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,6 @@
|
||||||
"@common/*": ["./src/common/*"]
|
"@common/*": ["./src/common/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["./**/*.ts", "./**/*.tsx", "./**/*.js", "./**/*.jsx", "./node_modules/assemblyscript/std/portable/index.d.ts"],
|
"include": ["./**/*.ts", "./**/*.tsx", "./**/*.js", "./**/*.jsx"],
|
||||||
"exclude": ["./dist/**/*"]
|
"exclude": ["./dist/**/*"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue