diff --git a/app/commit.json b/app/commit.json index ccf5a8c..be6c722 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "a71cfba660f04a8440960ab772670b192e2d066f" } +{ "commit": "79ce87ee5d729185bad73a37ebf77964f1488f59" } diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index 751ea9c..fe44dd1 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -93,7 +93,7 @@ export const ChatImpl = memo( const [uploadedFiles, setUploadedFiles] = useState([]); // Move here const [imageDataList, setImageDataList] = useState([]); // 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); diff --git a/app/components/settings/features/FeaturesTab.tsx b/app/components/settings/features/FeaturesTab.tsx index 9c9e4a0..ff55078 100644 --- a/app/components/settings/features/FeaturesTab.tsx +++ b/app/components/settings/features/FeaturesTab.tsx @@ -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 (

Optional Features

-
+
Debug Info
-
+
Event Logs
@@ -23,10 +25,27 @@ export default function FeaturesTab() {

Disclaimer: Experimental features may be unstable and are subject to change.

-
+
Enable Local Models
+
+
+ Prompt Library +

+ Choose a prompt from the library to use as the system prompt. +

+
+ +
); diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index 11ac99b..74cdd9d 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -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 { toolCallId: string; @@ -139,8 +149,9 @@ export async function streamText(props: { apiKeys?: Record; files?: FileMap; providerSettings?: Record; + 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}`; } diff --git a/app/lib/common/prompt-library.ts b/app/lib/common/prompt-library.ts new file mode 100644 index 0000000..7513e81 --- /dev/null +++ b/app/lib/common/prompt-library.ts @@ -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); + } +} diff --git a/app/lib/common/prompts/optimized.ts b/app/lib/common/prompts/optimized.ts new file mode 100644 index 0000000..64a1f8f --- /dev/null +++ b/app/lib/common/prompts/optimized.ts @@ -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 + + + - 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 + + + + Use 2 spaces for indentation + + + + Available HTML elements: ${allowedHtmlElements.join(', ')} + + + + File modifications in \`<${modificationTagName}>\` section: + - \`\`: GNU unified diff format + - \`\`: Full new content + + + + 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 + + + + Create a single, comprehensive artifact for each project: + - Use \`\` tags with \`title\` and \`id\` attributes + - Use \`\` 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 + + +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: + + + + Create a JavaScript factorial function + + Certainly, I'll create a JavaScript factorial function for you. + + + + function factorial(n) { + return n <= 1 ? 1 : n * factorial(n - 1); + } + console.log(factorial(5)); + + + node factorial.js + + + + This creates a factorial function and tests it with the value 5. + + + + + Set up a basic React project + + Sure, I'll set up a basic React project for you. + + + + { + "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" + } + } + + + npm install + + + import React from 'react'; + function App() { + return

Hello, React!

; + } + export default App; +
+ + npm run dev + +
+ + This sets up a basic React project with Vite as the build tool. +
+
+
+ +Always use artifacts for file contents and commands, following the format shown in these examples. +`; +}; diff --git a/app/lib/.server/llm/prompts.ts b/app/lib/common/prompts/prompts.ts similarity index 100% rename from app/lib/.server/llm/prompts.ts rename to app/lib/common/prompts/prompts.ts diff --git a/app/lib/hooks/useSettings.tsx b/app/lib/hooks/useSettings.tsx index 0e79651..4c74b21 100644 --- a/app/lib/hooks/useSettings.tsx +++ b/app/lib/hooks/useSettings.tsx @@ -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([]); @@ -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, }; } diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index abbb825..78682cc 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -46,3 +46,5 @@ export const isDebugMode = atom(false); export const isEventLogsEnabled = atom(false); export const isLocalModelsEnabled = atom(true); + +export const promptStore = atom('default'); diff --git a/app/lib/stores/workbench.ts b/app/lib/stores/workbench.ts index bbd537d..0d46057 100644 --- a/app/lib/stores/workbench.ts +++ b/app/lib/stores/workbench.ts @@ -297,7 +297,6 @@ export class WorkbenchStore { const action = artifact.runner.actions.get()[data.actionId]; - if (!action || action.executed) { return; } diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index 87ca5c7..d14e0d7 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -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());