1
0
Fork 0

AI Story: add bg, add summarize toggle

This commit is contained in:
Pabloader 2024-11-11 20:07:22 +00:00
parent 763085726b
commit ece1621e73
8 changed files with 121 additions and 57 deletions

View File

@ -18,7 +18,7 @@
border-radius: var(--border-radius, 0); border-radius: var(--border-radius, 0);
&::backdrop { &::backdrop {
background-color: var(--shadeColor, rgba(0, 0, 0, 0.2)); backdrop-filter: blur(5px);
} }
>.content { >.content {

BIN
src/games/ai/assets/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -1,6 +1,6 @@
:root { :root {
--backgroundColorDark: #111111; --backgroundColorDark: rgba(0, 0, 0, 0.3);
--backgroundColor: #333333; --backgroundColor: rgba(51, 51, 51, 0.9);
--color: #DCDCD2; --color: #DCDCD2;
--italicColor: #AFAFAF; --italicColor: #AFAFAF;
--quoteColor: #D4E5FF; --quoteColor: #D4E5FF;
@ -67,19 +67,27 @@ button {
body { body {
color: var(--color); color: var(--color);
background-color: var(--backgroundColor);
width: 100dvw; width: 100dvw;
height: 100dvh; height: 100dvh;
display: flex;
flex-direction: row;
justify-content: center;
font-size: 16px; font-size: 16px;
line-height: 1.5; line-height: 1.5;
.root {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: center;
}
.app { .app {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: var(--backgroundColor);
width: 100%; width: 100%;
max-width: 1200px; max-width: 1200px;

View File

@ -2,12 +2,16 @@ import { Header } from "./header/header";
import { Chat } from "./chat"; import { Chat } from "./chat";
import { Input } from "./input"; import { Input } from "./input";
import bgImage from '../assets/bg.jpg';
export const App = () => { export const App = () => {
return ( return (
<div class='app'> <div class='root' style={{ backgroundImage: `url('${bgImage.src}')` }}>
<Header /> <div class='app'>
<Chat /> <Header />
<Input /> <Chat />
<Input />
</div>
</div> </div>
); );
}; };

View File

@ -13,8 +13,8 @@ import { Ace } from "../ace";
export const Header = () => { export const Header = () => {
const { modelName, modelTemplate, contextLength, promptTokens, blockConnection } = useContext(LLMContext); const { modelName, modelTemplate, contextLength, promptTokens, blockConnection } = useContext(LLMContext);
const { const {
messages, connectionUrl, systemPrompt, lore, userPrompt, bannedWords, instruct, summarizePrompt, messages, connectionUrl, systemPrompt, lore, userPrompt, bannedWords, instruct, summarizePrompt, summaryEnabled,
setConnectionUrl, setSystemPrompt, setLore, setUserPrompt, addSwipe, setBannedWords, setInstruct, setSummarizePrompt, setConnectionUrl, setSystemPrompt, setLore, setUserPrompt, addSwipe, setBannedWords, setInstruct, setSummarizePrompt, setSummaryEnabled,
} = useContext(StateContext); } = useContext(StateContext);
const loreOpen = useBool(); const loreOpen = useBool();
@ -52,6 +52,12 @@ export const Header = () => {
} }
}, [setBannedWords]); }, [setBannedWords]);
const handleSetSummaryEnabled = useCallback((e: Event) => {
if (e.target instanceof HTMLInputElement) {
setSummaryEnabled(e.target.checked);
}
}, [setSummaryEnabled]);
return ( return (
<div class={styles.header}> <div class={styles.header}>
<div class={styles.inputs}> <div class={styles.inputs}>
@ -128,6 +134,10 @@ export const Header = () => {
<hr /> <hr />
<h4 class={styles.modalTitle}>Summary template</h4> <h4 class={styles.modalTitle}>Summary template</h4>
<Ace value={summarizePrompt} onInput={setSummarizePrompt} /> <Ace value={summarizePrompt} onInput={setSummarizePrompt} />
<label>
<input type='checkbox' checked={summaryEnabled} onChange={handleSetSummaryEnabled}/>
&nbsp;Enable summarization
</label>
<hr /> <hr />
<h4 class={styles.modalTitle}>Instruct template</h4> <h4 class={styles.modalTitle}>Instruct template</h4>
<Ace value={instruct} onInput={setInstruct} /> <Ace value={instruct} onInput={setInstruct} />

View File

@ -32,7 +32,7 @@ export const MiniChat = ({ history = [], buttons = {}, open, onClose }: IProps)
}, []); }, []);
useEffect(() => { useEffect(() => {
DOMTools.scrollDown(ref.current, false); setTimeout(() => DOMTools.scrollDown(ref.current, false), 100);
}, [generating, open]); }, [generating, open]);
useEffect(() => { useEffect(() => {

View File

@ -38,11 +38,18 @@ const DEFAULT_GENERATION_SETTINGS = {
top_k: 100, top_k: 100,
top_p: 0.92, top_p: 0.92,
banned_tokens: [], banned_tokens: [],
max_length: 512, max_length: 300,
trim_stop: true, trim_stop: true,
stop_sequence: ['[INST]', '[/INST]', '</s>', '<|'] stop_sequence: ['[INST]', '[/INST]', '</s>', '<|'],
dry_allowed_length: 5,
dry_multiplier: 0.8,
dry_base: 1,
dry_sequence_breakers: ["\n", ":", "\"", "*"],
dry_penalty_last_n: 0
} }
const MESSAGES_TO_KEEP = 10;
type IGenerationSettings = Partial<typeof DEFAULT_GENERATION_SETTINGS>; type IGenerationSettings = Partial<typeof DEFAULT_GENERATION_SETTINGS>;
interface IActions { interface IActions {
@ -81,14 +88,18 @@ export const normalizeModel = (model: string) => {
export const LLMContext = createContext<ILLMContext>({} as ILLMContext); export const LLMContext = createContext<ILLMContext>({} as ILLMContext);
const processing = {
tokenizing: false,
summarizing: false,
}
export const LLMContextProvider = ({ children }: { children?: any }) => { export const LLMContextProvider = ({ children }: { children?: any }) => {
const { const {
connectionUrl, messages, triggerNext, lore, userPrompt, systemPrompt, bannedWords, instruct, summarizePrompt, connectionUrl, messages, triggerNext, lore, userPrompt, systemPrompt, bannedWords, instruct, summarizePrompt, summaryEnabled,
setTriggerNext, addMessage, editMessage, editSummary, setInstruct, setTriggerNext, addMessage, editMessage, editSummary, setInstruct,
} = useContext(StateContext); } = useContext(StateContext);
const generating = useBool(false); const generating = useBool(false);
const summarizing = useBool(false);
const blockConnection = useBool(false); const blockConnection = useBool(false);
const [promptTokens, setPromptTokens] = useState(0); const [promptTokens, setPromptTokens] = useState(0);
const [contextLength, setContextLength] = useState(0); const [contextLength, setContextLength] = useState(0);
@ -156,10 +167,8 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
const lastUserMessage = userMessages.at(-1); const lastUserMessage = userMessages.at(-1);
const firstUserMessage = userMessages.at(0); const firstUserMessage = userMessages.at(0);
const system = `${systemPrompt}\n\n${lore}`.trim();
const templateMessages: Huggingface.ITemplateMessage[] = [ const templateMessages: Huggingface.ITemplateMessage[] = [
{ role: 'system', content: system }, { role: 'system', content: systemPrompt.trim() },
]; ];
if (keepUsers) { if (keepUsers) {
@ -168,7 +177,8 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
for (const message of messages) { for (const message of messages) {
const { role } = message; const { role } = message;
const content = MessageTools.getSwipe(message)?.content ?? ''; const swipe = MessageTools.getSwipe(message);
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') {
@ -188,7 +198,16 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
} }
} else { } else {
const story = promptMessages.filter(m => m.role === 'assistant') const story = promptMessages.filter(m => m.role === 'assistant')
.map(m => MessageTools.getSwipe(m)?.content.trim()).join('\n\n'); .map((m, i, msgs) => {
const swipe = MessageTools.getSwipe(m);
if (!swipe) return '';
let { content, summary } = swipe;
if (summary && i < msgs.length - MESSAGES_TO_KEEP) {
content = summary;
}
return content;
}).join('\n\n');
if (story.length > 0) { if (story.length > 0) {
const prompt = MessageTools.getSwipe(firstUserMessage)?.content; const prompt = MessageTools.getSwipe(firstUserMessage)?.content;
@ -215,6 +234,8 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
}); });
} }
templateMessages[1].content = `${lore}\n\n${templateMessages[1].content}`;
const prompt = Huggingface.applyChatTemplate(instruct, templateMessages); const prompt = Huggingface.applyChatTemplate(instruct, templateMessages);
return { return {
prompt, prompt,
@ -326,8 +347,6 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
let text: string = ''; let text: string = '';
const { prompt, isRegen } = await actions.compilePrompt(messages); const { prompt, isRegen } = await actions.compilePrompt(messages);
const tokens = await actions.countTokens(prompt);
setPromptTokens(tokens);
if (!isRegen) { if (!isRegen) {
addMessage('', 'assistant'); addMessage('', 'assistant');
@ -337,7 +356,7 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
editSummary(messageId, 'Generating...'); editSummary(messageId, 'Generating...');
for await (const chunk of actions.generate(prompt)) { for await (const chunk of actions.generate(prompt)) {
text += chunk; text += chunk;
setPromptTokens(tokens + Math.round(text.length * 0.25)); setPromptTokens(promptTokens + Math.round(text.length * 0.25));
editMessage(messageId, text.trim()); editMessage(messageId, text.trim());
} }
@ -345,39 +364,37 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
editMessage(messageId, text); editMessage(messageId, text);
editSummary(messageId, ''); editSummary(messageId, '');
setPromptTokens(0); // trigger calculation
MessageTools.playReady(); MessageTools.playReady();
} }
})(), [actions, triggerNext, messages, generating.value]); })(), [triggerNext]);
useEffect(() => void (async () => { useEffect(() => void (async () => {
if (!generating.value && !summarizing.value) { if (summaryEnabled && !generating.value && !processing.summarizing) {
summarizing.setTrue(); try {
for (let id = 0; id < messages.length; id++) { processing.summarizing = true;
const message = messages[id]; for (let id = 0; id < messages.length; id++) {
const swipe = MessageTools.getSwipe(message); const message = messages[id];
if (message.role === 'assistant' && swipe?.content?.includes('\n') && !swipe.summary) { const swipe = MessageTools.getSwipe(message);
const summary = await actions.summarize(swipe.content); if (message.role === 'assistant' && swipe?.content?.includes('\n') && !swipe.summary) {
editSummary(id, summary); const summary = await actions.summarize(swipe.content);
editSummary(id, summary);
}
} }
} catch (e) {
console.error(`Could not summarize`, e)
} finally {
processing.summarizing = false;
} }
summarizing.setFalse();
} }
})(), [messages, generating.value, summarizing.value]); })(), [messages]);
useEffect(() => { useEffect(() => {
if (!blockConnection.value) { if (!blockConnection.value) {
setPromptTokens(0); setPromptTokens(0);
setContextLength(0); setContextLength(0);
setModelName('');
getContextLength().then(setContextLength); getContextLength().then(setContextLength);
}
}, [connectionUrl, instruct, blockConnection.value]);
useEffect(() => {
if (!blockConnection.value) {
setModelName('');
getModelName().then(normalizeModel).then(setModelName); getModelName().then(normalizeModel).then(setModelName);
} }
}, [connectionUrl, blockConnection.value]); }, [connectionUrl, blockConnection.value]);
@ -397,14 +414,24 @@ export const LLMContextProvider = ({ children }: { children?: any }) => {
} }
}, [modelName]); }, [modelName]);
useEffect(() => { const calculateTokens = useCallback(async () => {
if (promptTokens === 0 && !blockConnection.value) { if (!processing.tokenizing && !blockConnection.value && !generating.value) {
actions.compilePrompt(messages) try {
.then(({ prompt }) => actions.countTokens(prompt)) processing.tokenizing = true;
.then(setPromptTokens) const { prompt } = await actions.compilePrompt(messages);
.catch(e => console.error(`Could not count tokens`, e)); const tokens = await actions.countTokens(prompt);
setPromptTokens(tokens);
} catch (e) {
console.error(`Could not count tokens`, e)
} finally {
processing.tokenizing = false;
}
} }
}, [actions, promptTokens, messages, blockConnection.value]); }, [actions, messages, blockConnection.value]);
useEffect(() => {
calculateTokens();
}, [messages, connectionUrl, blockConnection.value, instruct, /* systemPrompt, lore, userPrompt TODO debounce*/]);
useEffect(() => { useEffect(() => {
try { try {

View File

@ -11,6 +11,7 @@ interface IContext {
lore: string; lore: string;
userPrompt: string; userPrompt: string;
summarizePrompt: string; summarizePrompt: string;
summaryEnabled: boolean;
bannedWords: string[]; bannedWords: string[];
messages: IMessage[]; messages: IMessage[];
triggerNext: boolean; triggerNext: boolean;
@ -26,6 +27,7 @@ interface IActions {
setSummarizePrompt: (prompt: string | Event) => void; setSummarizePrompt: (prompt: string | Event) => void;
setBannedWords: (words: string[]) => void; setBannedWords: (words: string[]) => void;
setTriggerNext: (triggerNext: boolean) => void; setTriggerNext: (triggerNext: boolean) => void;
setSummaryEnabled: (summaryEnabled: boolean) => void;
setMessages: (messages: IMessage[]) => void; setMessages: (messages: IMessage[]) => void;
addMessage: (content: string, role: IMessage['role'], triggerNext?: boolean) => void; addMessage: (content: string, role: IMessage['role'], triggerNext?: boolean) => void;
@ -64,11 +66,21 @@ export const loadContext = (): IContext => {
connectionUrl: 'http://localhost:5001', connectionUrl: 'http://localhost:5001',
input: '', input: '',
instruct: Instruct.CHATML, instruct: Instruct.CHATML,
systemPrompt: 'You are creative writer. Write a story based on the world description below.', 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.',
lore: '', lore: '',
userPrompt: `{% if prompt %}{% if isStart %}Start{% else %}Continue{% endif %} this story, taking information into account: {{ prompt | trim }} userPrompt: `{% if isStart -%}
Remember that this story should be infinite and go forever. Avoid cliffhangers and pauses, be creative.{% elif isStart %}Write a novel using information above as a reference. Make sure to follow the lore exactly and avoid cliffhangers.{% else %}Continue the story forward. Avoid cliffhangers and pauses.{% endif %}`, Write a novel using information above as a reference.
summarizePrompt: 'Shrink following text down to one paragraph, keeping all important details, your answer should contain only the shortened text:\n\n{{ message }}', {%- else -%}
Continue the story forward.
{%- endif %}
{% if prompt -%}
This is the description of what I want to happen next: {{ prompt | trim }}
{% endif %}
Remember that this story should be infinite and go forever.
Make sure to follow the world description and rules exactly. Avoid cliffhangers and pauses, be creative.`,
summarizePrompt: 'Shrink following text down, keeping all important details:\n\n{{ message }}\n\nAnswer with shortened text only.',
summaryEnabled: false,
bannedWords: [], bannedWords: [],
messages: [], messages: [],
triggerNext: false, triggerNext: false,
@ -101,6 +113,7 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
const [summarizePrompt, setSummarizePrompt] = useInputState(loadedContext.summarizePrompt); const [summarizePrompt, setSummarizePrompt] = useInputState(loadedContext.summarizePrompt);
const [bannedWords, setBannedWords] = useState<string[]>(loadedContext.bannedWords); const [bannedWords, setBannedWords] = useState<string[]>(loadedContext.bannedWords);
const [messages, setMessages] = useState(loadedContext.messages); const [messages, setMessages] = useState(loadedContext.messages);
const [summaryEnabled, setSummaryEnabled] = useState(loadedContext.summaryEnabled);
const [triggerNext, setTriggerNext] = useState(false); const [triggerNext, setTriggerNext] = useState(false);
@ -113,6 +126,7 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
setSummarizePrompt, setSummarizePrompt,
setLore, setLore,
setTriggerNext, setTriggerNext,
setSummaryEnabled,
setBannedWords: (words) => setBannedWords([...words]), setBannedWords: (words) => setBannedWords([...words]),
setMessages: (newMessages) => setMessages(newMessages.slice()), setMessages: (newMessages) => setMessages(newMessages.slice()),
@ -192,6 +206,7 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
lore, lore,
userPrompt, userPrompt,
summarizePrompt, summarizePrompt,
summaryEnabled,
bannedWords, bannedWords,
messages, messages,
triggerNext, triggerNext,