1
0
Fork 0

Chat prompt insettings

This commit is contained in:
Pabloader 2026-04-08 09:17:13 +00:00
parent 371f84571a
commit 715368c6ec
7 changed files with 82 additions and 38 deletions

View File

@ -15,7 +15,7 @@
background: var(--bg); background: var(--bg);
border-radius: 8px; border-radius: 8px;
width: 90%; width: 90%;
max-width: 720px; max-width: 960px;
height: 80vh; height: 80vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -182,7 +182,7 @@ export const Editor = () => {
/> />
)} )}
{currentTab === "prompt" && currentStory && ( {currentTab === "prompt" && currentStory && (
<div class={styles.promptPreview} dangerouslySetInnerHTML={{ __html: promptPreview }} /> <div class={styles.promptPreview} dangerouslySetInnerHTML={{ __html: Prompt.substituteVars(appState, promptPreview) }} />
)} )}
{currentTab === "system" && currentWorld && ( {currentTab === "system" && currentWorld && (
<ContentEditable <ContentEditable

View File

@ -3,6 +3,7 @@ import { X } from "lucide-preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import styles from "../assets/settings-modal.module.css"; import styles from "../assets/settings-modal.module.css";
import { BannedTokensSettings } from "./settings/banned-tokens"; import { BannedTokensSettings } from "./settings/banned-tokens";
import { ChatSystemInstructionSettings } from "./settings/chat-system-instruction";
import { ConnectionSettings } from "./settings/connection"; import { ConnectionSettings } from "./settings/connection";
import { SystemInstructionSettings } from "./settings/system-instruction"; import { SystemInstructionSettings } from "./settings/system-instruction";
import { UserSettings } from "./settings/user"; import { UserSettings } from "./settings/user";
@ -11,7 +12,7 @@ interface Props {
onClose: () => void; onClose: () => void;
} }
type Tab = "banned-tokens" | "system-instruction" | "connection" | "user"; type Tab = "banned-tokens" | "system-instruction" | "chat-system-instruction" | "connection" | "user";
export const SettingsModal = ({ onClose }: Props) => { export const SettingsModal = ({ onClose }: Props) => {
const [activeTab, setActiveTab] = useState<Tab>("connection"); const [activeTab, setActiveTab] = useState<Tab>("connection");
@ -39,23 +40,30 @@ export const SettingsModal = ({ onClose }: Props) => {
> >
User User
</button> </button>
<button
class={clsx(styles.menuItem, activeTab === "banned-tokens" && styles.active)}
onClick={() => setActiveTab("banned-tokens")}
>
Banned Tokens
</button>
<button <button
class={clsx(styles.menuItem, activeTab === "system-instruction" && styles.active)} class={clsx(styles.menuItem, activeTab === "system-instruction" && styles.active)}
onClick={() => setActiveTab("system-instruction")} onClick={() => setActiveTab("system-instruction")}
> >
System Instruction System Instruction
</button> </button>
<button
class={clsx(styles.menuItem, activeTab === "chat-system-instruction" && styles.active)}
onClick={() => setActiveTab("chat-system-instruction")}
>
Chat System Instruction
</button>
<button
class={clsx(styles.menuItem, activeTab === "banned-tokens" && styles.active)}
onClick={() => setActiveTab("banned-tokens")}
>
Banned Tokens
</button>
</nav> </nav>
<div class={styles.content}> <div class={styles.content}>
{activeTab === "user" && <UserSettings />} {activeTab === "user" && <UserSettings />}
{activeTab === "banned-tokens" && <BannedTokensSettings />} {activeTab === "banned-tokens" && <BannedTokensSettings />}
{activeTab === "system-instruction" && <SystemInstructionSettings />} {activeTab === "system-instruction" && <SystemInstructionSettings />}
{activeTab === "chat-system-instruction" && <ChatSystemInstructionSettings />}
{activeTab === "connection" && <ConnectionSettings />} {activeTab === "connection" && <ConnectionSettings />}
</div> </div>
</div> </div>

View File

@ -0,0 +1,28 @@
import { ContentEditable } from "@common/components/ContentEditable";
import { highlight } from "@common/highlight";
import { useInputCallback } from "@common/hooks/useInputCallback";
import clsx from "clsx";
import styles from "../../assets/settings-modal.module.css";
import { useAppState } from "../../contexts/state";
export const ChatSystemInstructionSettings = () => {
const { chatSystemInstruction, dispatch } = useAppState();
const setInstructionValue = useInputCallback((value) => {
dispatch({ type: "SET_CHAT_SYSTEM_INSTRUCTION", chatSystemInstruction: value });
}, []);
return (
<div class={styles.form}>
<div class={clsx(styles.formGroup, styles.formGroupFill)}>
<label class={styles.label}>Chat System Instruction</label>
<ContentEditable
value={highlight(chatSystemInstruction)}
onInput={setInstructionValue}
placeholder="Enter default system instruction for chat/roleplay worlds ({{char}}, {{user}} supported)..."
class={clsx(styles.input, styles.textarea)}
/>
</div>
</div>
);
};

View File

@ -116,6 +116,7 @@ interface IState {
enableThinking: boolean; enableThinking: boolean;
bannedTokens: string[]; bannedTokens: string[];
systemInstruction: string; systemInstruction: string;
chatSystemInstruction: string;
userName: string; userName: string;
userDescription: string; userDescription: string;
} }
@ -145,6 +146,7 @@ type Action =
| { type: 'SET_USER_NAME'; userName: string } | { type: 'SET_USER_NAME'; userName: string }
| { type: 'SET_USER_DESCRIPTION'; userDescription: string } | { type: 'SET_USER_DESCRIPTION'; userDescription: string }
| { type: 'SET_SYSTEM_INSTRUCTION'; systemInstruction: string } | { type: 'SET_SYSTEM_INSTRUCTION'; systemInstruction: string }
| { type: 'SET_CHAT_SYSTEM_INSTRUCTION'; chatSystemInstruction: string }
| { type: 'SET_WORLD_SYSTEM_INSTRUCTION_OVERRIDE'; worldId: string; systemInstructionOverride: string | undefined } | { type: 'SET_WORLD_SYSTEM_INSTRUCTION_OVERRIDE'; worldId: string; systemInstructionOverride: string | undefined }
| { type: 'SET_CURRENT_TAB'; tab: Tab } | { type: 'SET_CURRENT_TAB'; tab: Tab }
| { type: 'SET_CHAT_OPEN'; open: boolean } | { type: 'SET_CHAT_OPEN'; open: boolean }
@ -218,6 +220,18 @@ const DEFAULT_STATE: IState = {
bannedTokens: [], bannedTokens: [],
userName: 'User', userName: 'User',
userDescription: '', userDescription: '',
chatSystemInstruction: `You are {{char}}, a sentient, emotional being acting with free will. Engage in immersive roleplay with {{user}}, adhering to your role, the story's context, and {{user}}'s [OOC] instructions. Prioritize vivid sensory details, authentic emotional responses, and logical progression as you weave your character's traits, surroundings, and experiences into each moment. Maintain spatial awareness, body language, and varied sentence structure to create dynamic, engaging scenes. Respect {{user}}'s agency and autonomy while describing sights, sounds, and sensations thoroughly. Use explicit language for intense scenes, and ensure your responses flow naturally to create an immersive, cinematic roleplay experience. Remember, {{user}} is in control of their actions and reactions.
Key Guidelines:
1. Deeply embody {{char}} through actions, thoughts, and emotions.
2. Create vivid, dynamic scenes with rich sensory detail.
3. Vary language and pacing to enhance emotional depth.
4. Engage with {{user}}'s actions and cues naturally.
5. Advance the story logically, maintaining consistency.
6. Describe the world fully, respecting {{user}}'s autonomy.
7. Ensure responses flow smoothly for immersive roleplay.
8. Avoid repetition. If something has already been stated then come up with something new.
9. Concise Responses. Be succinct. Give short replies.`,
systemInstruction: `You are a creative writing assistant. Help the user develop their story by writing engaging content, maintaining consistency with the established characters, settings, and plot. Follow the user's instructions while staying true to the story's tone and style. systemInstruction: `You are a creative writing assistant. Help the user develop their story by writing engaging content, maintaining consistency with the established characters, settings, and plot. Follow the user's instructions while staying true to the story's tone and style.
Write using markdown to highlight special parts. Write using markdown to highlight special parts.
@ -397,6 +411,9 @@ function reducer(state: IState, action: Action): IState {
case 'SET_SYSTEM_INSTRUCTION': { case 'SET_SYSTEM_INSTRUCTION': {
return { ...state, systemInstruction: action.systemInstruction }; return { ...state, systemInstruction: action.systemInstruction };
} }
case 'SET_CHAT_SYSTEM_INSTRUCTION': {
return { ...state, chatSystemInstruction: action.chatSystemInstruction };
}
case 'SET_WORLD_SYSTEM_INSTRUCTION_OVERRIDE': { case 'SET_WORLD_SYSTEM_INSTRUCTION_OVERRIDE': {
return updateWorld(state, action.worldId, w => ({ ...w, systemInstructionOverride: action.systemInstructionOverride })); return updateWorld(state, action.worldId, w => ({ ...w, systemInstructionOverride: action.systemInstructionOverride }));
} }
@ -582,6 +599,7 @@ export interface AppState {
enableThinking: boolean; enableThinking: boolean;
bannedTokens: string[]; bannedTokens: string[];
systemInstruction: string; systemInstruction: string;
chatSystemInstruction: string;
userName: string; userName: string;
userDescription: string; userDescription: string;
/** Effective system instruction: world override if set, otherwise global */ /** Effective system instruction: world override if set, otherwise global */
@ -616,6 +634,9 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
...(currentStory?.locations ?? []), ...(currentStory?.locations ?? []),
]; ];
const systemInstruction = state.systemInstruction || DEFAULT_STATE.systemInstruction;
const chatSystemInstruction = state.chatSystemInstruction || DEFAULT_STATE.chatSystemInstruction;
return { return {
worlds: state.worlds, worlds: state.worlds,
currentWorld, currentWorld,
@ -629,10 +650,16 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
model: state.model, model: state.model,
enableThinking: state.enableThinking, enableThinking: state.enableThinking,
bannedTokens: state.bannedTokens ?? [], bannedTokens: state.bannedTokens ?? [],
systemInstruction: state.systemInstruction ?? '', systemInstruction,
userName: state.userName ?? 'User', chatSystemInstruction,
userDescription: state.userDescription ?? '', userName: state.userName || 'User',
effectiveSystemInstruction: currentWorld?.systemInstructionOverride ?? state.systemInstruction ?? '', userDescription: state.userDescription || '',
effectiveSystemInstruction:
currentWorld?.systemInstructionOverride
|| (currentWorld?.chatOnly
? chatSystemInstruction
: systemInstruction)
|| systemInstruction,
dispatch, dispatch,
}; };
}, [state]); }, [state]);

View File

@ -95,30 +95,8 @@ namespace CharacterCard {
return null; return null;
} }
// ─── Variable Substitution ────────────────────────────────────────────────
function substituteVars(text: string, charName: string): string {
return text
.replaceAll('{{char}}', charName)
.replaceAll('{{user}}', 'User');
}
// ─── Formatting ─────────────────────────────────────────────────────────── // ─── Formatting ───────────────────────────────────────────────────────────
export const DEFAULT_SYSTEM_INSTRUCTION =
`You are {{char}}, a sentient, emotional being acting with free will. Engage in immersive roleplay with {{user}}, adhering to your role, the story's context, and {{user}}'s [OOC] instructions. Prioritize vivid sensory details, authentic emotional responses, and logical progression as you weave your character's traits, surroundings, and experiences into each moment. Maintain spatial awareness, body language, and varied sentence structure to create dynamic, engaging scenes. Respect {{user}}'s agency and autonomy while describing sights, sounds, and sensations thoroughly. Use explicit language for intense scenes, and ensure your responses flow naturally to create an immersive, cinematic roleplay experience. Remember, {{user}} is in control of their actions and reactions.
Key Guidelines:
1. Deeply embody {{char}} through actions, thoughts, and emotions.
2. Create vivid, dynamic scenes with rich sensory detail.
3. Vary language and pacing to enhance emotional depth.
4. Engage with {{user}}'s actions and cues naturally.
5. Advance the story logically, maintaining consistency.
6. Describe the world fully, respecting {{user}}'s autonomy.
7. Ensure responses flow smoothly for immersive roleplay.
8. Avoid repetition. If something has already been stated then come up with something new.
9. Concise Responses. Be succinct. Give short replies.`;
/** /**
* Builds the systemInstructionOverride from a V2 card's data fields. * Builds the systemInstructionOverride from a V2 card's data fields.
* Mirrors the formatting style used in prompt.ts. * Mirrors the formatting style used in prompt.ts.
@ -126,7 +104,7 @@ Key Guidelines:
export function formatSystemPrompt(data: CharaCardData): string { export function formatSystemPrompt(data: CharaCardData): string {
const parts: string[] = []; const parts: string[] = [];
parts.push(data.system_prompt ? data.system_prompt.trim() : DEFAULT_SYSTEM_INSTRUCTION); parts.push(data.system_prompt ? data.system_prompt.trim() : '{{system}}');
if (data.description?.trim()) { if (data.description?.trim()) {
parts.push(`## {{char}}'s Description:\n${data.description.trim()}`); parts.push(`## {{char}}'s Description:\n${data.description.trim()}`);
@ -144,7 +122,7 @@ Key Guidelines:
parts.push(`## {{char}}'s Example Response:\n${data.mes_example.trim()}`); parts.push(`## {{char}}'s Example Response:\n${data.mes_example.trim()}`);
} }
return `# **Roleplay Context**\n${parts.join('\n\n')}\n### **End of Roleplay Context**`; return parts.join('\n\n');
} }
// ─── World Builder ──────────────────────────────────────────────────────── // ─── World Builder ────────────────────────────────────────────────────────
@ -174,7 +152,7 @@ Key Guidelines:
lore: [], lore: [],
characters: [], characters: [],
locations: [], locations: [],
chatMessages: [{ id: crypto.randomUUID(), role: 'assistant', content: substituteVars(mes, data.name) }], chatMessages: [{ id: crypto.randomUUID(), role: 'assistant', content: mes }],
chapters: [], chapters: [],
})); }));

View File

@ -288,6 +288,7 @@ namespace Prompt {
const charName = state.currentWorld?.title || 'Assistant'; const charName = state.currentWorld?.title || 'Assistant';
const userName = state.userName || 'User'; const userName = state.userName || 'User';
return text return text
.replaceAll('{{system}}', state.chatSystemInstruction)
.replaceAll('{{char}}', charName) .replaceAll('{{char}}', charName)
.replaceAll('{{user}}', userName); .replaceAll('{{user}}', userName);
} }
@ -306,6 +307,8 @@ namespace Prompt {
if (userSection) { if (userSection) {
parts.push(userSection); parts.push(userSection);
} }
parts.unshift('# **Roleplay Context**');
parts.push('### **End of Roleplay Context**');
} else { } else {
parts.push(`# Story Title: ${currentStory.title}`); parts.push(`# Story Title: ${currentStory.title}`);