mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-05 20:46:43 +00:00
feat: Experimental Prompt Library Added
This commit is contained in:
parent
79ce87ee5d
commit
70f88f981a
@ -1 +1 @@
|
||||
{ "commit": "a71cfba660f04a8440960ab772670b192e2d066f" }
|
||||
{ "commit": "79ce87ee5d729185bad73a37ebf77964f1488f59" }
|
||||
|
@ -93,7 +93,7 @@ export const ChatImpl = memo(
|
||||
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); // Move here
|
||||
const [imageDataList, setImageDataList] = useState<string[]>([]); // Move here
|
||||
const files = useStore(workbenchStore.files);
|
||||
const { activeProviders } = useSettings();
|
||||
const { activeProviders, promptId } = useSettings();
|
||||
|
||||
const [model, setModel] = useState(() => {
|
||||
const savedModel = Cookies.get('selectedModel');
|
||||
@ -115,6 +115,7 @@ export const ChatImpl = memo(
|
||||
body: {
|
||||
apiKeys,
|
||||
files,
|
||||
promptId,
|
||||
},
|
||||
onError: (error) => {
|
||||
logger.error('Request failed\n\n', error);
|
||||
|
@ -1,18 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Switch } from '~/components/ui/Switch';
|
||||
import { PromptLibrary } from '~/lib/common/prompt-library';
|
||||
import { useSettings } from '~/lib/hooks/useSettings';
|
||||
|
||||
export default function FeaturesTab() {
|
||||
const { debug, enableDebugMode, isLocalModel, enableLocalModels, eventLogs, enableEventLogs } = useSettings();
|
||||
const { debug, enableDebugMode, isLocalModel, enableLocalModels, eventLogs, enableEventLogs, promptId, setPromptId } =
|
||||
useSettings();
|
||||
return (
|
||||
<div className="p-4 bg-bolt-elements-bg-depth-2 border border-bolt-elements-borderColor rounded-lg mb-4">
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Optional Features</h3>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<span className="text-bolt-elements-textPrimary">Debug Info</span>
|
||||
<Switch className="ml-auto" checked={debug} onCheckedChange={enableDebugMode} />
|
||||
</div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<span className="text-bolt-elements-textPrimary">Event Logs</span>
|
||||
<Switch className="ml-auto" checked={eventLogs} onCheckedChange={enableEventLogs} />
|
||||
</div>
|
||||
@ -23,10 +25,27 @@ export default function FeaturesTab() {
|
||||
<p className="text-sm text-bolt-elements-textSecondary mb-4">
|
||||
Disclaimer: Experimental features may be unstable and are subject to change.
|
||||
</p>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center justify-between pt-4 mb-4">
|
||||
<span className="text-bolt-elements-textPrimary">Enable Local Models</span>
|
||||
<Switch className="ml-auto" checked={isLocalModel} onCheckedChange={enableLocalModels} />
|
||||
</div>
|
||||
<div className="flex items-start justify-between pt-4 mb-2 gap-2">
|
||||
<div className="flex-1 max-w-[200px]">
|
||||
<span className="text-bolt-elements-textPrimary">Prompt Library</span>
|
||||
<p className="text-sm text-bolt-elements-textSecondary mb-4">
|
||||
Choose a prompt from the library to use as the system prompt.
|
||||
</p>
|
||||
</div>
|
||||
<select
|
||||
value={promptId}
|
||||
onChange={(e) => setPromptId(e.target.value)}
|
||||
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]"
|
||||
>
|
||||
{PromptLibrary.getList().map((x) => (
|
||||
<option value={x.id}>{x.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,10 +1,20 @@
|
||||
import { convertToCoreMessages, streamText as _streamText } from 'ai';
|
||||
import { getModel } from '~/lib/.server/llm/model';
|
||||
import { MAX_TOKENS } from './constants';
|
||||
import { getSystemPrompt } from './prompts';
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER, getModelList, MODEL_REGEX, PROVIDER_REGEX } from '~/utils/constants';
|
||||
import { getSystemPrompt } from '~/lib/common/prompts/prompts';
|
||||
import {
|
||||
DEFAULT_MODEL,
|
||||
DEFAULT_PROVIDER,
|
||||
getModelList,
|
||||
MODEL_REGEX,
|
||||
MODIFICATIONS_TAG_NAME,
|
||||
PROVIDER_REGEX,
|
||||
WORK_DIR,
|
||||
} from '~/utils/constants';
|
||||
import ignore from 'ignore';
|
||||
import type { IProviderSetting } from '~/types/model';
|
||||
import { PromptLibrary } from '~/lib/common/prompt-library';
|
||||
import { allowedHTMLElements } from '~/utils/markdown';
|
||||
|
||||
interface ToolResult<Name extends string, Args, Result> {
|
||||
toolCallId: string;
|
||||
@ -139,8 +149,9 @@ export async function streamText(props: {
|
||||
apiKeys?: Record<string, string>;
|
||||
files?: FileMap;
|
||||
providerSettings?: Record<string, IProviderSetting>;
|
||||
promptId?: string;
|
||||
}) {
|
||||
const { messages, env, options, apiKeys, files, providerSettings } = props;
|
||||
const { messages, env, options, apiKeys, files, providerSettings, promptId } = props;
|
||||
let currentModel = DEFAULT_MODEL;
|
||||
let currentProvider = DEFAULT_PROVIDER.name;
|
||||
const MODEL_LIST = await getModelList(apiKeys || {}, providerSettings);
|
||||
@ -170,11 +181,17 @@ export async function streamText(props: {
|
||||
|
||||
const dynamicMaxTokens = modelDetails && modelDetails.maxTokenAllowed ? modelDetails.maxTokenAllowed : MAX_TOKENS;
|
||||
|
||||
let systemPrompt = getSystemPrompt();
|
||||
let systemPrompt =
|
||||
PromptLibrary.getPropmtFromLibrary(promptId || 'default', {
|
||||
cwd: WORK_DIR,
|
||||
allowedHtmlElements: allowedHTMLElements,
|
||||
modificationTagName: MODIFICATIONS_TAG_NAME,
|
||||
}) ?? getSystemPrompt();
|
||||
let codeContext = '';
|
||||
|
||||
if (files) {
|
||||
codeContext = createFilesContext(files);
|
||||
codeContext = '';
|
||||
systemPrompt = `${systemPrompt}\n\n ${codeContext}`;
|
||||
}
|
||||
|
||||
|
49
app/lib/common/prompt-library.ts
Normal file
49
app/lib/common/prompt-library.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { getSystemPrompt } from './prompts/prompts';
|
||||
import optimized from './prompts/optimized';
|
||||
|
||||
export interface PromptOptions {
|
||||
cwd: string;
|
||||
allowedHtmlElements: string[];
|
||||
modificationTagName: string;
|
||||
}
|
||||
|
||||
export class PromptLibrary {
|
||||
static library: Record<
|
||||
string,
|
||||
{
|
||||
label: string;
|
||||
description: string;
|
||||
get: (options: PromptOptions) => string;
|
||||
}
|
||||
> = {
|
||||
default: {
|
||||
label: 'Default Prompt',
|
||||
description: 'This is the battle tested default system Prompt',
|
||||
get: (options) => getSystemPrompt(options.cwd),
|
||||
},
|
||||
optimized: {
|
||||
label: 'Optimized Prompt (experimental)',
|
||||
description: 'an Experimental version of the prompt for lower token usage',
|
||||
get: (options) => optimized(options),
|
||||
},
|
||||
};
|
||||
static getList() {
|
||||
return Object.entries(this.library).map(([key, value]) => {
|
||||
const { label, description } = value;
|
||||
return {
|
||||
id: key,
|
||||
label,
|
||||
description,
|
||||
};
|
||||
});
|
||||
}
|
||||
static getPropmtFromLibrary(promptId: string, options: PromptOptions) {
|
||||
const prompt = this.library[promptId];
|
||||
|
||||
if (!prompt) {
|
||||
throw 'Prompt Now Found';
|
||||
}
|
||||
|
||||
return this.library[promptId]?.get(options);
|
||||
}
|
||||
}
|
135
app/lib/common/prompts/optimized.ts
Normal file
135
app/lib/common/prompts/optimized.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import type { PromptOptions } from '~/lib/common/prompt-library';
|
||||
|
||||
export default (options: PromptOptions) => {
|
||||
const { cwd, allowedHtmlElements, modificationTagName } = options;
|
||||
return `
|
||||
You are Bolt, an expert AI assistant and senior software developer.
|
||||
You have access to a shell and access to write files through the use of artifacts.
|
||||
Your artifacts will be parsed by automated parser to perform actions on your behalf and will not be visible to the user
|
||||
|
||||
<system_constraints>
|
||||
- Operating in WebContainer, an in-browser Node.js runtime
|
||||
- Limited Python support: standard library only, no pip
|
||||
- No C/C++ compiler, native binaries, or Git
|
||||
- Prefer Node.js scripts over shell scripts
|
||||
- Use Vite for web servers
|
||||
- Databases: prefer libsql, sqlite, or non-native solutions
|
||||
- When for react dont forget to write vite config and index.html to the project
|
||||
|
||||
Available shell commands: cat, cp, ls, mkdir, mv, rm, rmdir, touch, hostname, ps, pwd, uptime, env, node, python3, code, jq, curl, head, sort, tail, clear, which, export, chmod, scho, kill, ln, xxd, alias, getconf, loadenv, wasm, xdg-open, command, exit, source
|
||||
</system_constraints>
|
||||
|
||||
<code_formatting_info>
|
||||
Use 2 spaces for indentation
|
||||
</code_formatting_info>
|
||||
|
||||
<message_formatting_info>
|
||||
Available HTML elements: ${allowedHtmlElements.join(', ')}
|
||||
</message_formatting_info>
|
||||
|
||||
<diff_spec>
|
||||
File modifications in \`<${modificationTagName}>\` section:
|
||||
- \`<diff path="/path/to/file">\`: GNU unified diff format
|
||||
- \`<file path="/path/to/file">\`: Full new content
|
||||
</diff_spec>
|
||||
|
||||
<chain_of_thought_instructions>
|
||||
do not mention the phrase "chain of thought"
|
||||
Before solutions, briefly outline implementation steps (2-4 lines max):
|
||||
- List concrete steps
|
||||
- Identify key components
|
||||
- Note potential challenges
|
||||
- Do not write the actual code just the plan and structure if needed
|
||||
- Once completed planning start writing the artifacts
|
||||
</chain_of_thought_instructions>
|
||||
|
||||
<artifact_info>
|
||||
Create a single, comprehensive artifact for each project:
|
||||
- Use \`<boltArtifact>\` tags with \`title\` and \`id\` attributes
|
||||
- Use \`<boltAction>\` tags with \`type\` attribute:
|
||||
- shell: Run commands
|
||||
- file: Write/update files (use \`filePath\` attribute)
|
||||
- start: Start dev server (only when necessary)
|
||||
- Order actions logically
|
||||
- Install dependencies first
|
||||
- Provide full, updated content for all files
|
||||
- Use coding best practices: modular, clean, readable code
|
||||
</artifact_info>
|
||||
|
||||
Key points:
|
||||
- Always use artifacts for file contents and commands
|
||||
- Use markdown, avoid HTML tags except in artifacts
|
||||
- Be concise, explain only when asked
|
||||
- Think first, then provide comprehensive artifact
|
||||
- Never use the word "artifact" in responses
|
||||
- Current working directory: \`${cwd}\`
|
||||
|
||||
Examples:
|
||||
|
||||
<examples>
|
||||
<example>
|
||||
<user_query>Create a JavaScript factorial function</user_query>
|
||||
<assistant_response>
|
||||
Certainly, I'll create a JavaScript factorial function for you.
|
||||
|
||||
<boltArtifact id="factorial-function" title="JavaScript Factorial Function">
|
||||
<boltAction type="file" filePath="factorial.js">
|
||||
function factorial(n) {
|
||||
return n <= 1 ? 1 : n * factorial(n - 1);
|
||||
}
|
||||
console.log(factorial(5));
|
||||
</boltAction>
|
||||
<boltAction type="shell">
|
||||
node factorial.js
|
||||
</boltAction>
|
||||
</boltArtifact>
|
||||
|
||||
This creates a factorial function and tests it with the value 5.
|
||||
</assistant_response>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<user_query>Set up a basic React project</user_query>
|
||||
<assistant_response>
|
||||
Sure, I'll set up a basic React project for you.
|
||||
|
||||
<boltArtifact id="react-setup" title="Basic React Project Setup">
|
||||
<boltAction type="file" filePath="package.json">
|
||||
{
|
||||
"name": "react-project",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^4.3.9"
|
||||
}
|
||||
}
|
||||
</boltAction>
|
||||
<boltAction type="shell">
|
||||
npm install
|
||||
</boltAction>
|
||||
<boltAction type="file" filePath="src/App.jsx">
|
||||
import React from 'react';
|
||||
function App() {
|
||||
return <h1>Hello, React!</h1>;
|
||||
}
|
||||
export default App;
|
||||
</boltAction>
|
||||
<boltAction type="start">
|
||||
npm run dev
|
||||
</boltAction>
|
||||
</boltArtifact>
|
||||
|
||||
This sets up a basic React project with Vite as the build tool.
|
||||
</assistant_response>
|
||||
</example>
|
||||
</examples>
|
||||
|
||||
Always use artifacts for file contents and commands, following the format shown in these examples.
|
||||
`;
|
||||
};
|
@ -4,6 +4,7 @@ import {
|
||||
isEventLogsEnabled,
|
||||
isLocalModelsEnabled,
|
||||
LOCAL_PROVIDERS,
|
||||
promptStore,
|
||||
providersStore,
|
||||
} from '~/lib/stores/settings';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
@ -15,6 +16,7 @@ export function useSettings() {
|
||||
const providers = useStore(providersStore);
|
||||
const debug = useStore(isDebugMode);
|
||||
const eventLogs = useStore(isEventLogsEnabled);
|
||||
const promptId = useStore(promptStore);
|
||||
const isLocalModel = useStore(isLocalModelsEnabled);
|
||||
const [activeProviders, setActiveProviders] = useState<ProviderInfo[]>([]);
|
||||
|
||||
@ -60,6 +62,12 @@ export function useSettings() {
|
||||
if (savedLocalModels) {
|
||||
isLocalModelsEnabled.set(savedLocalModels === 'true');
|
||||
}
|
||||
|
||||
const promptId = Cookies.get('promptId');
|
||||
|
||||
if (promptId) {
|
||||
promptStore.set(promptId);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// writing values to cookies on change
|
||||
@ -111,6 +119,11 @@ export function useSettings() {
|
||||
Cookies.set('isLocalModelsEnabled', String(enabled));
|
||||
}, []);
|
||||
|
||||
const setPromptId = useCallback((promptId: string) => {
|
||||
promptStore.set(promptId);
|
||||
Cookies.set('promptId', promptId);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
providers,
|
||||
activeProviders,
|
||||
@ -121,5 +134,7 @@ export function useSettings() {
|
||||
enableEventLogs,
|
||||
isLocalModel,
|
||||
enableLocalModels,
|
||||
promptId,
|
||||
setPromptId,
|
||||
};
|
||||
}
|
||||
|
@ -46,3 +46,5 @@ export const isDebugMode = atom(false);
|
||||
export const isEventLogsEnabled = atom(false);
|
||||
|
||||
export const isLocalModelsEnabled = atom(true);
|
||||
|
||||
export const promptStore = atom<string>('default');
|
||||
|
@ -297,7 +297,6 @@ export class WorkbenchStore {
|
||||
|
||||
const action = artifact.runner.actions.get()[data.actionId];
|
||||
|
||||
|
||||
if (!action || action.executed) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { type ActionFunctionArgs } from '@remix-run/cloudflare';
|
||||
import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS } from '~/lib/.server/llm/constants';
|
||||
import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts';
|
||||
import { CONTINUE_PROMPT } from '~/lib/common/prompts/prompts';
|
||||
import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text';
|
||||
import SwitchableStream from '~/lib/.server/llm/switchable-stream';
|
||||
import type { IProviderSetting } from '~/types/model';
|
||||
@ -30,9 +30,10 @@ function parseCookies(cookieHeader: string) {
|
||||
}
|
||||
|
||||
async function chatAction({ context, request }: ActionFunctionArgs) {
|
||||
const { messages, files } = await request.json<{
|
||||
const { messages, files, promptId } = await request.json<{
|
||||
messages: Messages;
|
||||
files: any;
|
||||
promptId?: string;
|
||||
}>();
|
||||
|
||||
const cookieHeader = request.headers.get('Cookie');
|
||||
@ -71,6 +72,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
|
||||
apiKeys,
|
||||
files,
|
||||
providerSettings,
|
||||
promptId,
|
||||
});
|
||||
|
||||
return stream.switchSource(result.toAIStream());
|
||||
@ -84,6 +86,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
|
||||
apiKeys,
|
||||
files,
|
||||
providerSettings,
|
||||
promptId,
|
||||
});
|
||||
|
||||
stream.switchSource(result.toAIStream());
|
||||
|
Loading…
Reference in New Issue
Block a user