Sampling settings
This commit is contained in:
parent
0413c6a10a
commit
ecacc126f4
|
|
@ -1,6 +1,8 @@
|
||||||
/* ─── Form Fields ─────────────────────────────────────────── */
|
/* ─── Form Fields ─────────────────────────────────────────── */
|
||||||
|
|
||||||
.input, input, textarea {
|
.input,
|
||||||
|
input,
|
||||||
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
|
|
@ -16,6 +18,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="number"] {
|
||||||
|
appearance: textfield;
|
||||||
|
|
||||||
|
&::-webkit-inner-spin-button {
|
||||||
|
opacity: 0;
|
||||||
|
display: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.textarea {
|
.textarea {
|
||||||
composes: input;
|
composes: input;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
|
|
@ -35,7 +47,8 @@
|
||||||
|
|
||||||
/* ─── Buttons ─────────────────────────────────────────────── */
|
/* ─── Buttons ─────────────────────────────────────────────── */
|
||||||
|
|
||||||
.button, button {
|
.button,
|
||||||
|
button {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
@ -261,4 +274,4 @@
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: var(--border);
|
background: var(--border);
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +136,12 @@
|
||||||
composes: empty from '@common/assets/ui.module.css';
|
composes: empty from '@common/assets/ui.module.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.labelHint {
|
||||||
|
font-weight: normal;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.menuItem {
|
.menuItem {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ const WORLD_TABS: { id: Tab; label: string; icon: LucideIcon; right?: boolean }[
|
||||||
{ id: "lore", label: "Lore", icon: BookMarked },
|
{ id: "lore", label: "Lore", icon: BookMarked },
|
||||||
{ id: "characters", label: "Characters", icon: Users },
|
{ id: "characters", label: "Characters", icon: Users },
|
||||||
{ id: "locations", label: "Locations", icon: MapPin },
|
{ id: "locations", label: "Locations", icon: MapPin },
|
||||||
{ id: "system", label: "System", icon: BrainCircuit },
|
{ id: "system", label: "System", icon: BrainCircuit, right: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
const CHAT_TAB = { id: "chat", label: "Chat", icon: MessageSquare } as const;
|
const CHAT_TAB = { id: "chat", label: "Chat", icon: MessageSquare } as const;
|
||||||
|
|
@ -45,6 +45,7 @@ const CHAT_STORY_TABS: { id: Tab; label: string; icon: LucideIcon; right?: boole
|
||||||
{ id: "menu", label: "Menu", icon: List },
|
{ id: "menu", label: "Menu", icon: List },
|
||||||
CHAT_TAB,
|
CHAT_TAB,
|
||||||
{ id: "scratchpad", label: "Scratchpad", icon: FileText, right: true },
|
{ id: "scratchpad", label: "Scratchpad", icon: FileText, right: true },
|
||||||
|
{ id: "system", label: "System", icon: BrainCircuit },
|
||||||
{ id: "prompt", label: "Prompt", icon: Code },
|
{ id: "prompt", label: "Prompt", icon: Code },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -66,7 +67,8 @@ export const Editor = () => {
|
||||||
const editorRef = useRef<HTMLDivElement>(null);
|
const editorRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const promptPreview = useMemo(() => {
|
const promptPreview = useMemo(() => {
|
||||||
const text = Prompt.formatSystemPrompt(appState);
|
let text = Prompt.formatSystemPrompt(appState);
|
||||||
|
text = Prompt.substituteVars(appState, text);
|
||||||
return highlight(text, false);
|
return highlight(text, false);
|
||||||
}, [appState]);
|
}, [appState]);
|
||||||
|
|
||||||
|
|
@ -147,7 +149,7 @@ export const Editor = () => {
|
||||||
<ScratchpadEditor visible={currentTab === "scratchpad"} />
|
<ScratchpadEditor visible={currentTab === "scratchpad"} />
|
||||||
<div
|
<div
|
||||||
class={clsx(styles.promptPreview, currentTab !== "prompt" && styles.tabHidden)}
|
class={clsx(styles.promptPreview, currentTab !== "prompt" && styles.tabHidden)}
|
||||||
dangerouslySetInnerHTML={{ __html: Prompt.substituteVars(appState, promptPreview) }}
|
dangerouslySetInnerHTML={{ __html: promptPreview }}
|
||||||
/>
|
/>
|
||||||
{(isChatOnly || isMobile) && <ChatPanel visible={currentTab === "chat"} />}
|
{(isChatOnly || isMobile) && <ChatPanel visible={currentTab === "chat"} />}
|
||||||
</>)}
|
</>)}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { BannedTokensSettings } from "./settings/banned-tokens";
|
||||||
import { ChatSystemInstructionSettings } from "./settings/chat-system-instruction";
|
import { ChatSystemInstructionSettings } from "./settings/chat-system-instruction";
|
||||||
import { ContinuePromptSettings } from "./settings/continue-prompt";
|
import { ContinuePromptSettings } from "./settings/continue-prompt";
|
||||||
import { ConnectionSettings } from "./settings/connection";
|
import { ConnectionSettings } from "./settings/connection";
|
||||||
|
import { SamplingSettings } from "./settings/sampling";
|
||||||
import { SystemInstructionSettings } from "./settings/system-instruction";
|
import { SystemInstructionSettings } from "./settings/system-instruction";
|
||||||
import { UserSettings } from "./settings/user";
|
import { UserSettings } from "./settings/user";
|
||||||
|
|
||||||
|
|
@ -14,10 +15,11 @@ interface Props {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tab = "banned-tokens" | "system-instruction" | "chat-system-instruction" | "continue-prompt" | "connection" | "user";
|
type Tab = "banned-tokens" | "system-instruction" | "chat-system-instruction" | "continue-prompt" | "connection" | "user" | "sampling";
|
||||||
|
|
||||||
const TABS: { id: Tab; label: string }[] = [
|
const TABS: { id: Tab; label: string }[] = [
|
||||||
{ id: "connection", label: "Connection" },
|
{ id: "connection", label: "Connection" },
|
||||||
|
{ id: "sampling", label: "Sampling" },
|
||||||
{ id: "user", label: "User" },
|
{ id: "user", label: "User" },
|
||||||
{ id: "system-instruction", label: "System Instruction" },
|
{ id: "system-instruction", label: "System Instruction" },
|
||||||
{ id: "continue-prompt", label: "Continue Prompt" },
|
{ id: "continue-prompt", label: "Continue Prompt" },
|
||||||
|
|
@ -54,6 +56,7 @@ export const SettingsModal = ({ open, onClose }: Props) => {
|
||||||
{activeTab === "chat-system-instruction" && <ChatSystemInstructionSettings />}
|
{activeTab === "chat-system-instruction" && <ChatSystemInstructionSettings />}
|
||||||
{activeTab === "continue-prompt" && <ContinuePromptSettings />}
|
{activeTab === "continue-prompt" && <ContinuePromptSettings />}
|
||||||
{activeTab === "connection" && <ConnectionSettings />}
|
{activeTab === "connection" && <ConnectionSettings />}
|
||||||
|
{activeTab === "sampling" && <SamplingSettings />}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { useInputState } from "@common/hooks/useInputState";
|
||||||
|
import { useState } from "preact/hooks";
|
||||||
|
import styles from "../../assets/settings-modal.module.css";
|
||||||
|
import { DEFAULT_GENERATION_SETTINGS, type GenerationSettings, useAppState } from "../../contexts/state";
|
||||||
|
|
||||||
|
type NumericKey = keyof GenerationSettings;
|
||||||
|
|
||||||
|
interface ParamConfig {
|
||||||
|
key: NumericKey;
|
||||||
|
label: string;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
step: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PARAMS: ParamConfig[] = [
|
||||||
|
{ key: "temperature", label: "Temperature", min: 0, max: 4, step: 0.01 },
|
||||||
|
{ key: "top_p", label: "Top P", min: 0, max: 1, step: 0.01 },
|
||||||
|
{ key: "top_k", label: "Top K", min: 0, max: 200, step: 1 },
|
||||||
|
{ key: "min_p", label: "Min P", min: 0, max: 1, step: 0.01 },
|
||||||
|
{ key: "repetition_penalty", label: "Repetition Penalty", min: 1, max: 3, step: 0.01 },
|
||||||
|
{ key: "frequency_penalty", label: "Frequency Penalty", min: -2, max: 2, step: 0.01 },
|
||||||
|
];
|
||||||
|
|
||||||
|
function ParamRow({ config, initialValue, onChange }: {
|
||||||
|
config: ParamConfig;
|
||||||
|
initialValue: number;
|
||||||
|
onChange: (key: NumericKey, value: number) => void;
|
||||||
|
}) {
|
||||||
|
const [draft, setDraft] = useInputState(String(initialValue));
|
||||||
|
|
||||||
|
const commit = () => {
|
||||||
|
const parsed = parseFloat(draft);
|
||||||
|
if (!isNaN(parsed)) {
|
||||||
|
onChange(config.key, parsed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={styles.formGroup}>
|
||||||
|
<label class={styles.label}>
|
||||||
|
{config.label}
|
||||||
|
<span class={styles.labelHint}>
|
||||||
|
(default: {DEFAULT_GENERATION_SETTINGS[config.key]})
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={draft}
|
||||||
|
min={config.min}
|
||||||
|
max={config.max}
|
||||||
|
step={config.step}
|
||||||
|
class={styles.input}
|
||||||
|
onInput={setDraft}
|
||||||
|
onBlur={commit}
|
||||||
|
onKeyDown={(e) => e.key === "Enter" && commit()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SamplingSettings = () => {
|
||||||
|
const { generationSettings, dispatch } = useAppState();
|
||||||
|
const [resetKey, setResetKey] = useState(0);
|
||||||
|
|
||||||
|
const handleChange = (key: NumericKey, value: number) => {
|
||||||
|
dispatch({ type: "SET_GENERATION_SETTINGS", settings: { [key]: value } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
dispatch({ type: "SET_GENERATION_SETTINGS", settings: DEFAULT_GENERATION_SETTINGS });
|
||||||
|
setResetKey(k => k + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={styles.form}>
|
||||||
|
{PARAMS.map(config => (
|
||||||
|
<ParamRow
|
||||||
|
key={`${config.key}-${resetKey}`}
|
||||||
|
config={config}
|
||||||
|
initialValue={generationSettings[config.key]}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<div>
|
||||||
|
<button class={styles.button} onClick={handleReset}>
|
||||||
|
Reset to defaults
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -63,6 +63,24 @@ export interface LoreEntry {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GenerationSettings {
|
||||||
|
temperature: number;
|
||||||
|
top_p: number;
|
||||||
|
top_k: number;
|
||||||
|
min_p: number;
|
||||||
|
repetition_penalty: number;
|
||||||
|
frequency_penalty: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_GENERATION_SETTINGS: GenerationSettings = {
|
||||||
|
temperature: 0.8,
|
||||||
|
top_p: 0.92,
|
||||||
|
top_k: 100,
|
||||||
|
min_p: 0.1,
|
||||||
|
repetition_penalty: 1.08,
|
||||||
|
frequency_penalty: 0,
|
||||||
|
};
|
||||||
|
|
||||||
export interface Story {
|
export interface Story {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -74,6 +92,7 @@ export interface Story {
|
||||||
chatMessages: ChatMessage[];
|
chatMessages: ChatMessage[];
|
||||||
chapters: Chapters.Chapter[];
|
chapters: Chapters.Chapter[];
|
||||||
lastEditedText?: string;
|
lastEditedText?: string;
|
||||||
|
titleWasChanged?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface World {
|
export interface World {
|
||||||
|
|
@ -120,6 +139,7 @@ interface IState {
|
||||||
continuePrompt: string;
|
continuePrompt: string;
|
||||||
userName: string;
|
userName: string;
|
||||||
userDescription: string;
|
userDescription: string;
|
||||||
|
generationSettings: GenerationSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Actions ─────────────────────────────────────────────────────────────────
|
// ─── Actions ─────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -144,6 +164,7 @@ type Action =
|
||||||
| { type: 'DELETE_LORE_ENTRY'; worldId: string; storyId: string | null; entryId: string }
|
| { type: 'DELETE_LORE_ENTRY'; worldId: string; storyId: string | null; entryId: string }
|
||||||
| { type: 'REORDER_LORE_ENTRIES'; worldId: string; storyId: string | null; entryIds: string[] }
|
| { type: 'REORDER_LORE_ENTRIES'; worldId: string; storyId: string | null; entryIds: string[] }
|
||||||
// Settings
|
// Settings
|
||||||
|
| { type: 'SET_GENERATION_SETTINGS'; settings: Partial<GenerationSettings> }
|
||||||
| { 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 }
|
||||||
|
|
@ -221,6 +242,7 @@ const DEFAULT_STATE: IState = {
|
||||||
bannedTokens: [],
|
bannedTokens: [],
|
||||||
userName: 'User',
|
userName: 'User',
|
||||||
userDescription: '',
|
userDescription: '',
|
||||||
|
generationSettings: DEFAULT_GENERATION_SETTINGS,
|
||||||
continuePrompt: "Continue the story naturally.\nUse `edit_text` tool in append mode to add new text to the story.\nWait for the approval after adding.\nNote: added text could be cropped due to limit, do not make any attempts to add it back.",
|
continuePrompt: "Continue the story naturally.\nUse `edit_text` tool in append mode to add new text to the story.\nWait for the approval after adding.\nNote: added text could be cropped due to limit, do not make any attempts to add it back.",
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
@ -320,7 +342,7 @@ function reducer(state: IState, action: Action): IState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'RENAME_STORY': {
|
case 'RENAME_STORY': {
|
||||||
return updateStory(state, action.worldId, action.id, s => ({ ...s, title: action.title }));
|
return updateStory(state, action.worldId, action.id, s => ({ ...s, title: action.title, titleWasChanged: true }));
|
||||||
}
|
}
|
||||||
case 'EDIT_STORY': {
|
case 'EDIT_STORY': {
|
||||||
return updateStory(state, action.worldId, action.id, s => ({
|
return updateStory(state, action.worldId, action.id, s => ({
|
||||||
|
|
@ -404,6 +426,9 @@ function reducer(state: IState, action: Action): IState {
|
||||||
return { lore: reordered };
|
return { lore: reordered };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
case 'SET_GENERATION_SETTINGS': {
|
||||||
|
return { ...state, generationSettings: { ...state.generationSettings, ...action.settings } };
|
||||||
|
}
|
||||||
case 'SET_USER_NAME': {
|
case 'SET_USER_NAME': {
|
||||||
return { ...state, userName: action.userName };
|
return { ...state, userName: action.userName };
|
||||||
}
|
}
|
||||||
|
|
@ -604,6 +629,7 @@ export interface AppState {
|
||||||
continuePrompt: string;
|
continuePrompt: string;
|
||||||
userName: string;
|
userName: string;
|
||||||
userDescription: string;
|
userDescription: string;
|
||||||
|
generationSettings: GenerationSettings;
|
||||||
/** Effective system instruction: world override if set, otherwise global */
|
/** Effective system instruction: world override if set, otherwise global */
|
||||||
effectiveSystemInstruction: string;
|
effectiveSystemInstruction: string;
|
||||||
dispatch: (action: Action) => void;
|
dispatch: (action: Action) => void;
|
||||||
|
|
@ -657,6 +683,7 @@ export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||||
continuePrompt: state.continuePrompt || DEFAULT_STATE.continuePrompt,
|
continuePrompt: state.continuePrompt || DEFAULT_STATE.continuePrompt,
|
||||||
userName: state.userName || 'User',
|
userName: state.userName || 'User',
|
||||||
userDescription: state.userDescription || '',
|
userDescription: state.userDescription || '',
|
||||||
|
generationSettings: state.generationSettings ?? DEFAULT_GENERATION_SETTINGS,
|
||||||
effectiveSystemInstruction:
|
effectiveSystemInstruction:
|
||||||
currentWorld?.systemInstructionOverride
|
currentWorld?.systemInstructionOverride
|
||||||
|| (currentWorld?.chatOnly
|
|| (currentWorld?.chatOnly
|
||||||
|
|
|
||||||
|
|
@ -407,6 +407,7 @@ namespace Prompt {
|
||||||
messages: applyVars(formattedMessages),
|
messages: applyVars(formattedMessages),
|
||||||
max_tokens: model.top_provider.max_completion_tokens || 2048,
|
max_tokens: model.top_provider.max_completion_tokens || 2048,
|
||||||
banned_tokens: state.bannedTokens,
|
banned_tokens: state.bannedTokens,
|
||||||
|
...state.generationSettings,
|
||||||
...(continueLast && { remove_last_eos: true }),
|
...(continueLast && { remove_last_eos: true }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -434,6 +435,7 @@ namespace Prompt {
|
||||||
banned_tokens: state.bannedTokens,
|
banned_tokens: state.bannedTokens,
|
||||||
reasoning: { effort: enableThinking ? 'high' : 'none' },
|
reasoning: { effort: enableThinking ? 'high' : 'none' },
|
||||||
max_tokens: model.top_provider.max_completion_tokens || 2048,
|
max_tokens: model.top_provider.max_completion_tokens || 2048,
|
||||||
|
...state.generationSettings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue