Skip to content

Commit de2cb43

Browse files
authored
Merge pull request #744 from thecodacus/system-prompt-variations
feat(experimental): Add Configurable System Prompts Feature
2 parents 412f290 + d473b2b commit de2cb43

File tree

13 files changed

+340
-22
lines changed

13 files changed

+340
-22
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ https://thinktank.ottomator.ai
4343
- ✅ Mobile friendly (@qwikode)
4444
- ✅ Better prompt enhancing (@SujalXplores)
4545
- ✅ Attach images to prompts (@atrokhym)
46+
- ✅ Added Git Clone button (@thecodacus)
47+
- ✅ Git Import from url (@thecodacus)
48+
- ✅ PromptLibrary to have different variations of prompts for different use cases (@thecodacus)
4649
- ✅ Detect package.json and commands to auto install & run preview for folder and git import (@wonderwhy-er)
4750
- ✅ Selection tool to target changes visually (@emcconnell)
4851
-**HIGH PRIORITY** - Prevent bolt from rewriting files as often (file locking and diffs)

app/commit.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "commit": "eb1d5417e77e699e0489f09814e87fb5afed9dd5" , "version": "" }
1+
{ "commit": "bb941802094c6186e805f99a6c165431ae86d216" }

app/components/chat/BaseChat.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
7777
input = '',
7878
enhancingPrompt,
7979
handleInputChange,
80+
81+
// promptEnhanced,
8082
enhancePrompt,
8183
sendMessage,
8284
handleStop,

app/components/chat/Chat.client.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export const ChatImpl = memo(
9393
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); // Move here
9494
const [imageDataList, setImageDataList] = useState<string[]>([]); // Move here
9595
const files = useStore(workbenchStore.files);
96-
const { activeProviders } = useSettings();
96+
const { activeProviders, promptId } = useSettings();
9797

9898
const [model, setModel] = useState(() => {
9999
const savedModel = Cookies.get('selectedModel');
@@ -115,6 +115,7 @@ export const ChatImpl = memo(
115115
body: {
116116
apiKeys,
117117
files,
118+
promptId,
118119
},
119120
sendExtraMessageFields: true,
120121
onError: (error) => {

app/components/settings/debug/DebugTab.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ const checkProviderStatus = async (url: string | null, providerName: string): Pr
303303
};
304304

305305
export default function DebugTab() {
306-
const { providers, latestBranch } = useSettings();
306+
const { providers, isLatestBranch } = useSettings();
307307
const [activeProviders, setActiveProviders] = useState<ProviderStatus[]>([]);
308308
const [updateMessage, setUpdateMessage] = useState<string>('');
309309
const [systemInfo] = useState<SystemInfo>(getSystemInfo());
@@ -363,7 +363,7 @@ export default function DebugTab() {
363363
setIsCheckingUpdate(true);
364364
setUpdateMessage('Checking for updates...');
365365

366-
const branchToCheck = latestBranch ? 'main' : 'stable';
366+
const branchToCheck = isLatestBranch ? 'main' : 'stable';
367367
console.log(`[Debug] Checking for updates against ${branchToCheck} branch`);
368368

369369
const localCommitResponse = await fetch(GITHUB_URLS.commitJson(branchToCheck));
@@ -391,7 +391,7 @@ export default function DebugTab() {
391391
} finally {
392392
setIsCheckingUpdate(false);
393393
}
394-
}, [isCheckingUpdate, latestBranch]);
394+
}, [isCheckingUpdate, isLatestBranch]);
395395

396396
const handleCopyToClipboard = useCallback(() => {
397397
const debugInfo = {
@@ -408,15 +408,15 @@ export default function DebugTab() {
408408
})),
409409
Version: {
410410
hash: versionHash.slice(0, 7),
411-
branch: latestBranch ? 'main' : 'stable',
411+
branch: isLatestBranch ? 'main' : 'stable',
412412
},
413413
Timestamp: new Date().toISOString(),
414414
};
415415

416416
navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => {
417417
toast.success('Debug information copied to clipboard!');
418418
});
419-
}, [activeProviders, systemInfo, latestBranch]);
419+
}, [activeProviders, systemInfo, isLatestBranch]);
420420

421421
return (
422422
<div className="p-4 space-y-6">
@@ -523,7 +523,7 @@ export default function DebugTab() {
523523
<p className="text-sm font-medium text-bolt-elements-textPrimary font-mono">
524524
{versionHash.slice(0, 7)}
525525
<span className="ml-2 text-xs text-bolt-elements-textSecondary">
526-
(v{versionTag || '0.0.1'}) - {latestBranch ? 'nightly' : 'stable'}
526+
(v{versionTag || '0.0.1'}) - {isLatestBranch ? 'nightly' : 'stable'}
527527
</span>
528528
</p>
529529
</div>

app/components/settings/features/FeaturesTab.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
import React from 'react';
22
import { Switch } from '~/components/ui/Switch';
3+
import { PromptLibrary } from '~/lib/common/prompt-library';
34
import { useSettings } from '~/lib/hooks/useSettings';
45

56
export default function FeaturesTab() {
6-
const { debug, enableDebugMode, isLocalModel, enableLocalModels, enableEventLogs, latestBranch, enableLatestBranch } =
7-
useSettings();
7+
const {
8+
debug,
9+
enableDebugMode,
10+
isLocalModel,
11+
enableLocalModels,
12+
enableEventLogs,
13+
isLatestBranch,
14+
enableLatestBranch,
15+
promptId,
16+
setPromptId,
17+
} = useSettings();
818

919
const handleToggle = (enabled: boolean) => {
1020
enableDebugMode(enabled);
@@ -27,7 +37,7 @@ export default function FeaturesTab() {
2737
Check for updates against the main branch instead of stable
2838
</p>
2939
</div>
30-
<Switch className="ml-auto" checked={latestBranch} onCheckedChange={enableLatestBranch} />
40+
<Switch className="ml-auto" checked={isLatestBranch} onCheckedChange={enableLatestBranch} />
3141
</div>
3242
</div>
3343
</div>
@@ -37,10 +47,28 @@ export default function FeaturesTab() {
3747
<p className="text-sm text-bolt-elements-textSecondary mb-4">
3848
Disclaimer: Experimental features may be unstable and are subject to change.
3949
</p>
50+
4051
<div className="flex items-center justify-between mb-2">
4152
<span className="text-bolt-elements-textPrimary">Experimental Providers</span>
4253
<Switch className="ml-auto" checked={isLocalModel} onCheckedChange={enableLocalModels} />
4354
</div>
55+
<div className="flex items-start justify-between pt-4 mb-2 gap-2">
56+
<div className="flex-1 max-w-[200px]">
57+
<span className="text-bolt-elements-textPrimary">Prompt Library</span>
58+
<p className="text-sm text-bolt-elements-textSecondary mb-4">
59+
Choose a prompt from the library to use as the system prompt.
60+
</p>
61+
</div>
62+
<select
63+
value={promptId}
64+
onChange={(e) => setPromptId(e.target.value)}
65+
className="flex-1 p-2 ml-auto rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus transition-all text-sm min-w-[100px]"
66+
>
67+
{PromptLibrary.getList().map((x) => (
68+
<option value={x.id}>{x.label}</option>
69+
))}
70+
</select>
71+
</div>
4472
</div>
4573
</div>
4674
);

app/lib/.server/llm/stream-text.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
import { convertToCoreMessages, streamText as _streamText } from 'ai';
22
import { getModel } from '~/lib/.server/llm/model';
33
import { MAX_TOKENS } from './constants';
4-
import { getSystemPrompt } from './prompts';
5-
import { DEFAULT_MODEL, DEFAULT_PROVIDER, getModelList, MODEL_REGEX, PROVIDER_REGEX } from '~/utils/constants';
4+
import { getSystemPrompt } from '~/lib/common/prompts/prompts';
5+
import {
6+
DEFAULT_MODEL,
7+
DEFAULT_PROVIDER,
8+
getModelList,
9+
MODEL_REGEX,
10+
MODIFICATIONS_TAG_NAME,
11+
PROVIDER_REGEX,
12+
WORK_DIR,
13+
} from '~/utils/constants';
614
import ignore from 'ignore';
715
import type { IProviderSetting } from '~/types/model';
16+
import { PromptLibrary } from '~/lib/common/prompt-library';
17+
import { allowedHTMLElements } from '~/utils/markdown';
818

919
interface ToolResult<Name extends string, Args, Result> {
1020
toolCallId: string;
@@ -139,8 +149,9 @@ export async function streamText(props: {
139149
apiKeys?: Record<string, string>;
140150
files?: FileMap;
141151
providerSettings?: Record<string, IProviderSetting>;
152+
promptId?: string;
142153
}) {
143-
const { messages, env, options, apiKeys, files, providerSettings } = props;
154+
const { messages, env, options, apiKeys, files, providerSettings, promptId } = props;
144155
let currentModel = DEFAULT_MODEL;
145156
let currentProvider = DEFAULT_PROVIDER.name;
146157
const MODEL_LIST = await getModelList(apiKeys || {}, providerSettings);
@@ -170,11 +181,17 @@ export async function streamText(props: {
170181

171182
const dynamicMaxTokens = modelDetails && modelDetails.maxTokenAllowed ? modelDetails.maxTokenAllowed : MAX_TOKENS;
172183

173-
let systemPrompt = getSystemPrompt();
184+
let systemPrompt =
185+
PromptLibrary.getPropmtFromLibrary(promptId || 'default', {
186+
cwd: WORK_DIR,
187+
allowedHtmlElements: allowedHTMLElements,
188+
modificationTagName: MODIFICATIONS_TAG_NAME,
189+
}) ?? getSystemPrompt();
174190
let codeContext = '';
175191

176192
if (files) {
177193
codeContext = createFilesContext(files);
194+
codeContext = '';
178195
systemPrompt = `${systemPrompt}\n\n ${codeContext}`;
179196
}
180197

app/lib/common/prompt-library.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { getSystemPrompt } from './prompts/prompts';
2+
import optimized from './prompts/optimized';
3+
4+
export interface PromptOptions {
5+
cwd: string;
6+
allowedHtmlElements: string[];
7+
modificationTagName: string;
8+
}
9+
10+
export class PromptLibrary {
11+
static library: Record<
12+
string,
13+
{
14+
label: string;
15+
description: string;
16+
get: (options: PromptOptions) => string;
17+
}
18+
> = {
19+
default: {
20+
label: 'Default Prompt',
21+
description: 'This is the battle tested default system Prompt',
22+
get: (options) => getSystemPrompt(options.cwd),
23+
},
24+
optimized: {
25+
label: 'Optimized Prompt (experimental)',
26+
description: 'an Experimental version of the prompt for lower token usage',
27+
get: (options) => optimized(options),
28+
},
29+
};
30+
static getList() {
31+
return Object.entries(this.library).map(([key, value]) => {
32+
const { label, description } = value;
33+
return {
34+
id: key,
35+
label,
36+
description,
37+
};
38+
});
39+
}
40+
static getPropmtFromLibrary(promptId: string, options: PromptOptions) {
41+
const prompt = this.library[promptId];
42+
43+
if (!prompt) {
44+
throw 'Prompt Now Found';
45+
}
46+
47+
return this.library[promptId]?.get(options);
48+
}
49+
}

0 commit comments

Comments
 (0)