Skip to content

Commit 6d3b962

Browse files
committed
allow custom prompt
1 parent 9f094e3 commit 6d3b962

File tree

4 files changed

+142
-20
lines changed

4 files changed

+142
-20
lines changed

components/modals/settings-modal.tsx

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
safeLocalStorage
3737
} from "@/lib/question";
3838
import { PlusIcon, XIcon } from "lucide-react";
39+
import { Textarea } from "@/components/ui/textarea";
3940

4041
interface SettingsModalProps {
4142
language: string;
@@ -63,6 +64,12 @@ export default function SettingsModal({
6364
!["gpt-4", "gpt-4o", "gpt-3.5-turbo"].includes(openAISettings.model),
6465
);
6566

67+
// Load system prompts from localStorage
68+
const [systemPrompts, setSystemPrompts] = useState({
69+
questionPrompt: safeLocalStorage.getItem("system_prompt_question") || "",
70+
answerPrompt: safeLocalStorage.getItem("system_prompt_answer") || "",
71+
});
72+
6673
const [questionSettings, setQuestionSettings] = useState({
6774
type: safeLocalStorage.getItem("question_type") || "all",
6875
category: safeLocalStorage.getItem("question_category") || "all",
@@ -162,6 +169,14 @@ export default function SettingsModal({
162169
safeLocalStorage.setItem("openai_token", openAISettings.token);
163170
}
164171

172+
// Save system prompts if they exist
173+
if (systemPrompts.questionPrompt) {
174+
safeLocalStorage.setItem("system_prompt_question", systemPrompts.questionPrompt);
175+
}
176+
if (systemPrompts.answerPrompt) {
177+
safeLocalStorage.setItem("system_prompt_answer", systemPrompts.answerPrompt);
178+
}
179+
165180
toast({
166181
title: t("toast.settings.title"),
167182
description: t("toast.settings.description"),
@@ -258,6 +273,12 @@ export default function SettingsModal({
258273
token: "",
259274
});
260275

276+
// Reset system prompts
277+
setSystemPrompts({
278+
questionPrompt: "",
279+
answerPrompt: "",
280+
});
281+
261282
// Reset custom model flag
262283
setIsCustomModel(false);
263284

@@ -279,6 +300,8 @@ export default function SettingsModal({
279300
safeLocalStorage.removeItem("openai_endpoint");
280301
safeLocalStorage.removeItem("openai_model");
281302
safeLocalStorage.removeItem("openai_token");
303+
safeLocalStorage.removeItem("system_prompt_question");
304+
safeLocalStorage.removeItem("system_prompt_answer");
282305

283306
setShowResetConfirmation(false);
284307

@@ -304,11 +327,12 @@ export default function SettingsModal({
304327
</DialogHeader>
305328

306329
<Tabs defaultValue="questions" onValueChange={setActiveTab}>
307-
<TabsList className="grid grid-cols-2 mb-4">
330+
<TabsList className="grid grid-cols-3 mb-4">
308331
<TabsTrigger value="questions">
309332
{t("settings.questions")}
310333
</TabsTrigger>
311334
<TabsTrigger value="openai">{t("settings.openai")}</TabsTrigger>
335+
<TabsTrigger value="prompts">{t("settings.prompts")}</TabsTrigger>
312336
</TabsList>
313337

314338
<TabsContent value="questions" className="space-y-4">
@@ -675,6 +699,66 @@ export default function SettingsModal({
675699
</div>
676700
</div>
677701
</TabsContent>
702+
703+
<TabsContent value="prompts" className="space-y-4">
704+
<div className="space-y-4">
705+
<div className="space-y-2">
706+
<Label>{t("settings.questionSystemPrompt") || "Question Generation System Prompt"}</Label>
707+
<Textarea
708+
placeholder={t("settings.questionSystemPromptPlaceholder") || "Enter system prompt for question generation..."}
709+
value={systemPrompts.questionPrompt}
710+
onChange={(e) =>
711+
setSystemPrompts({
712+
...systemPrompts,
713+
questionPrompt: e.target.value
714+
})
715+
}
716+
className="min-h-24"
717+
/>
718+
<p className="text-xs text-muted-foreground">
719+
{t("settings.questionSystemPromptHelp") || "This prompt guides the AI in generating interview questions."}
720+
</p>
721+
</div>
722+
723+
<div className="space-y-2">
724+
<Label>{t("settings.answerSystemPrompt") || "Answer Generation System Prompt"}</Label>
725+
<Textarea
726+
placeholder={t("settings.answerSystemPromptPlaceholder") || "Enter system prompt for answer generation..."}
727+
value={systemPrompts.answerPrompt}
728+
onChange={(e) =>
729+
setSystemPrompts({
730+
...systemPrompts,
731+
answerPrompt: e.target.value
732+
})
733+
}
734+
className="min-h-24"
735+
/>
736+
<p className="text-xs text-muted-foreground">
737+
{t("settings.answerSystemPromptHelp") || "This prompt guides the AI in generating model answers to questions."}
738+
</p>
739+
</div>
740+
741+
<Button
742+
variant="outline"
743+
onClick={() => {
744+
setSystemPrompts({
745+
questionPrompt: "",
746+
answerPrompt: ""
747+
});
748+
safeLocalStorage.removeItem("system_prompt_question");
749+
safeLocalStorage.removeItem("system_prompt_answer");
750+
toast({
751+
title: t("settings.promptsReset") || "Prompts Reset",
752+
description: t("settings.promptsResetDesc") || "System prompts have been reset to default",
753+
duration: 3000,
754+
});
755+
}}
756+
className="w-full"
757+
>
758+
{t("settings.resetPrompts") || "Reset Prompts to Default"}
759+
</Button>
760+
</div>
761+
</TabsContent>
678762
</Tabs>
679763

680764
<DialogFooter>

components/ui/textarea.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@ import * as React from "react"
22

33
import { cn } from "@/lib/utils"
44

5-
const Textarea = React.forwardRef<
6-
HTMLTextAreaElement,
7-
React.ComponentProps<"textarea">
8-
>(({ className, ...props }, ref) => {
9-
return (
10-
<textarea
11-
className={cn(
12-
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
13-
className
14-
)}
15-
ref={ref}
16-
{...props}
17-
/>
18-
)
19-
})
5+
export interface TextareaProps
6+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
7+
8+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
9+
({ className, ...props }, ref) => {
10+
return (
11+
<textarea
12+
className={cn(
13+
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
14+
className
15+
)}
16+
ref={ref}
17+
{...props}
18+
/>
19+
)
20+
}
21+
)
2022
Textarea.displayName = "Textarea"
2123

2224
export { Textarea }

lib/api.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ function getCustomSettings() {
2222
return Object.keys(settings).length > 0 ? settings : null
2323
}
2424

25+
// Helper function to get custom system prompts
26+
function getCustomSystemPrompt(type: 'question' | 'answer'): string | undefined {
27+
if (typeof window === "undefined") return undefined
28+
29+
const key = type === 'question' ? "system_prompt_question" : "system_prompt_answer"
30+
const customPrompt = localStorage.getItem(key)
31+
32+
return customPrompt || undefined
33+
}
34+
2535
// Function to call our backend API endpoint
2636
export async function callOpenAI(prompt: string, systemPrompt?: string, onStream?: (chunk: string) => void) {
2737
try {
@@ -170,11 +180,14 @@ export async function evaluateAnswer(
170180
}
171181

172182
export async function getModelAnswer(question: any, language: string, onStream?: (chunk: string) => void) {
173-
const systemPrompt = `You are an expert in technical interviews. Provide a model answer to the following question.
183+
// Use custom system prompt if available
184+
const customSystemPrompt = getCustomSystemPrompt('answer');
185+
186+
const systemPrompt = customSystemPrompt || `You are an expert in technical interviews. Provide a model answer to the following question.
174187
Your answer should be clear, efficient, and follow best practices.
175188
If it's a coding question, include well-commented code.
176189
If it's a conceptual question, provide a comprehensive explanation.
177-
Return your answer in both English and Chinese.`
190+
Return your answer in both English and Chinese.`;
178191

179192
const questionTitle = question.translations[language]?.title || question.translations.en.title
180193
const questionDescription = question.translations[language]?.description || question.translations.en.description
@@ -230,10 +243,13 @@ export async function getModelAnswer(question: any, language: string, onStream?:
230243

231244
export async function generateQuestion(type: string, category: string, difficulty: string, language: string = 'en') {
232245

233-
const systemPrompt = `You are an expert at creating technical interview questions.
246+
// Use custom system prompt if available
247+
const customSystemPrompt = getCustomSystemPrompt('question');
248+
249+
const systemPrompt = customSystemPrompt || `You are an expert at creating technical interview questions.
234250
Generate a new ${type} question in the ${category} category with ${difficulty} difficulty.
235251
The question should be challenging but solvable within a reasonable time frame.
236-
Return your response in JSON format exactly matching the structure provided, with no additional text.`
252+
Return your response in JSON format exactly matching the structure provided, with no additional text.`;
237253

238254
const prompt = `
239255
Create a new technical interview question with the following parameters:

lib/i18n.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ const translations = {
118118
"settings.codingCustomCategories": "Custom Coding Categories",
119119
"settings.questionCustomCategories": "Custom Question Categories",
120120
"settings.cannotAddCategory": "Cannot Add Category",
121+
"settings.prompts": "AI Prompts",
122+
"settings.questionSystemPrompt": "Question Generation System Prompt",
123+
"settings.questionSystemPromptPlaceholder": "Enter system prompt for question generation...",
124+
"settings.questionSystemPromptHelp": "This prompt guides the AI in generating interview questions.",
125+
"settings.answerSystemPrompt": "Answer Generation System Prompt",
126+
"settings.answerSystemPromptPlaceholder": "Enter system prompt for answer generation...",
127+
"settings.answerSystemPromptHelp": "This prompt guides the AI in generating model answers to questions.",
128+
"settings.resetPrompts": "Reset Prompts to Default",
129+
"settings.promptsReset": "Prompts Reset",
130+
"settings.promptsResetDesc": "System prompts have been reset to default",
121131
"editor.loading": "Loading editor...",
122132
"editor.language": "Language",
123133
"editor.theme": "Theme",
@@ -240,6 +250,16 @@ const translations = {
240250
"settings.codingCustomCategories": "自定义编程类别",
241251
"settings.questionCustomCategories": "自定义问答类别",
242252
"settings.cannotAddCategory": "无法添加类别",
253+
"settings.prompts": "AI 提示词",
254+
"settings.questionSystemPrompt": "问题生成系统提示词",
255+
"settings.questionSystemPromptPlaceholder": "输入问题生成的系统提示词...",
256+
"settings.questionSystemPromptHelp": "该提示词引导 AI 生成面试问题。",
257+
"settings.answerSystemPrompt": "答案生成系统提示词",
258+
"settings.answerSystemPromptPlaceholder": "输入答案生成的系统提示词...",
259+
"settings.answerSystemPromptHelp": "该提示词引导 AI 生成问题的模型答案。",
260+
"settings.resetPrompts": "重置提示词为默认值",
261+
"settings.promptsReset": "提示词已重置",
262+
"settings.promptsResetDesc": "系统提示词已重置为默认值",
243263
"editor.loading": "正在加载编辑器...",
244264
"editor.language": "编程语言",
245265
"editor.theme": "主题",

0 commit comments

Comments
 (0)