From 21e2d3d9151661f88582debe162827ef155cb533 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 9 Aug 2025 11:07:28 +0800 Subject: [PATCH] allow modify prompt --- messages/en.json | 4 + messages/zh.json | 4 + prisma/schema.prisma | 1 + src/app/api/simulator/[id]/execute/route.ts | 3 +- src/app/api/simulator/route.ts | 2 + src/app/simulator/[id]/page.tsx | 4 +- src/app/simulator/new/page.tsx | 129 ++++++++++++++++++-- src/lib/simulator-utils.ts | 44 +++++++ 8 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 src/lib/simulator-utils.ts diff --git a/messages/en.json b/messages/en.json index 6a7996b..e5b86c9 100644 --- a/messages/en.json +++ b/messages/en.json @@ -400,6 +400,10 @@ "copyError": "Failed to copy to clipboard", "executeError": "Failed to execute simulation", "loadingRuns": "Loading simulation runs...", + "edit": "Edit", + "save": "Save", + "promptContentModified": "Prompt content has been modified", + "promptContentPlaceholder": "Enter your custom prompt content here...", "status": { "pending": "Pending", "running": "Running", diff --git a/messages/zh.json b/messages/zh.json index a2b1f53..8b1d5b1 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -400,6 +400,10 @@ "copyError": "复制到剪贴板失败", "executeError": "执行模拟失败", "loadingRuns": "加载模拟运行中...", + "edit": "编辑", + "save": "保存", + "promptContentModified": "提示词内容已被修改", + "promptContentPlaceholder": "在这里输入您的自定义提示词内容...", "status": { "pending": "待执行", "running": "运行中", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index da4e5f1..8bf16fe 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -246,6 +246,7 @@ model SimulatorRun { promptVersionId String? // 可选,如果选择了特定版本 modelId String userInput String // 用户输入内容 + promptContent String? // 自定义提示词内容(如果用户修改了原提示词) output String? // AI响应输出 error String? // 错误信息 status String @default("pending") // "pending", "running", "completed", "failed" diff --git a/src/app/api/simulator/[id]/execute/route.ts b/src/app/api/simulator/[id]/execute/route.ts index 5d6d325..17cfa3b 100644 --- a/src/app/api/simulator/[id]/execute/route.ts +++ b/src/app/api/simulator/[id]/execute/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import { createServerSupabaseClient } from "@/lib/supabase-server"; import { prisma } from "@/lib/prisma"; +import { getPromptContent } from "@/lib/simulator-utils"; export async function POST( request: NextRequest, @@ -42,7 +43,7 @@ export async function POST( }); // 准备AI API请求 - const promptContent = run.promptVersion?.content || run.prompt.content; + const promptContent = getPromptContent(run); const finalPrompt = `${promptContent}\n\nUser Input: ${run.userInput}`; const requestBody = { diff --git a/src/app/api/simulator/route.ts b/src/app/api/simulator/route.ts index b6e62ff..4f28961 100644 --- a/src/app/api/simulator/route.ts +++ b/src/app/api/simulator/route.ts @@ -77,6 +77,7 @@ export async function POST(request: NextRequest) { promptVersionId, modelId, userInput, + promptContent, temperature = 0.7, maxTokens, topP, @@ -114,6 +115,7 @@ export async function POST(request: NextRequest) { promptVersionId, modelId, userInput, + promptContent, temperature, maxTokens, topP, diff --git a/src/app/simulator/[id]/page.tsx b/src/app/simulator/[id]/page.tsx index 40abc8b..fda5443 100644 --- a/src/app/simulator/[id]/page.tsx +++ b/src/app/simulator/[id]/page.tsx @@ -29,11 +29,13 @@ import Link from 'next/link' import { formatDistanceToNow } from 'date-fns' import { zhCN, enUS } from 'date-fns/locale' import { useLocale } from 'next-intl' +import { getPromptContent } from '@/lib/simulator-utils' interface SimulatorRun { id: string status: string userInput: string + promptContent?: string | null output?: string error?: string createdAt: string @@ -233,7 +235,7 @@ export default function SimulatorRunPage({ params }: { params: Promise<{ id: str return null } - const promptContent = run.promptVersion?.content || run.prompt.content + const promptContent = getPromptContent(run) const currentOutput = (isExecuting && run.status === 'pending') ? streamOutput : run.output return ( diff --git a/src/app/simulator/new/page.tsx b/src/app/simulator/new/page.tsx index 26a6d08..0397c6c 100644 --- a/src/app/simulator/new/page.tsx +++ b/src/app/simulator/new/page.tsx @@ -20,7 +20,8 @@ import { Zap, DollarSign, ChevronRight, - ChevronDown + ChevronDown, + Edit } from 'lucide-react' import Link from 'next/link' @@ -61,6 +62,8 @@ export default function NewSimulatorRunPage() { const [isLoading, setIsLoading] = useState(true) const [isCreating, setIsCreating] = useState(false) const [showAdvanced, setShowAdvanced] = useState(false) + const [editablePromptContent, setEditablePromptContent] = useState('') + const [isEditingPrompt, setIsEditingPrompt] = useState(false) // Advanced settings const [temperature, setTemperature] = useState('0.7') @@ -101,9 +104,7 @@ export default function NewSimulatorRunPage() { }, [user]) const selectedPrompt = prompts.find(p => p.id === selectedPromptId) - const selectedVersion = selectedPrompt?.versions.find(v => v.id === selectedVersionId) const selectedModel = models.find(m => m.id === selectedModelId) - const promptContent = selectedVersion?.content || selectedPrompt?.content || '' useEffect(() => { if (!authLoading && user) { @@ -114,6 +115,7 @@ export default function NewSimulatorRunPage() { const handlePromptChange = (promptId: string) => { setSelectedPromptId(promptId) setSelectedVersionId('') + setIsEditingPrompt(false) // Auto-select latest version const prompt = prompts.find(p => p.id === promptId) @@ -122,9 +124,66 @@ export default function NewSimulatorRunPage() { current.version > latest.version ? current : latest ) setSelectedVersionId(latestVersion.id) + setEditablePromptContent(latestVersion.content) + } else if (prompt) { + setEditablePromptContent(prompt.content) } } + const handleVersionChange = (versionId: string) => { + setSelectedVersionId(versionId) + setIsEditingPrompt(false) + + const selectedPrompt = prompts.find(p => p.id === selectedPromptId) + if (versionId && selectedPrompt) { + const version = selectedPrompt.versions.find(v => v.id === versionId) + if (version) { + setEditablePromptContent(version.content) + } + } else if (selectedPrompt) { + // Use latest version if no specific version selected + setEditablePromptContent(selectedPrompt.content) + } + } + + const handleEditPrompt = () => { + setIsEditingPrompt(true) + } + + const handleSavePromptEdit = () => { + setIsEditingPrompt(false) + } + + const handleCancelPromptEdit = () => { + setIsEditingPrompt(false) + // Reset to original content + const selectedPrompt = prompts.find(p => p.id === selectedPromptId) + if (selectedVersionId && selectedPrompt) { + const version = selectedPrompt.versions.find(v => v.id === selectedVersionId) + if (version) { + setEditablePromptContent(version.content) + } + } else if (selectedPrompt) { + setEditablePromptContent(selectedPrompt.content) + } + } + + const getCustomPromptContent = () => { + // Get original content for comparison + const selectedPrompt = prompts.find(p => p.id === selectedPromptId) + let originalContent = '' + + if (selectedVersionId && selectedPrompt) { + const version = selectedPrompt.versions.find(v => v.id === selectedVersionId) + originalContent = version?.content || '' + } else if (selectedPrompt) { + originalContent = selectedPrompt.content + } + + // Only return custom content if it's different from original + return editablePromptContent !== originalContent ? editablePromptContent : undefined + } + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!selectedPromptId || !selectedModelId || !userInput.trim()) { @@ -143,6 +202,7 @@ export default function NewSimulatorRunPage() { promptVersionId: selectedVersionId || undefined, modelId: selectedModelId, userInput, + promptContent: getCustomPromptContent(), temperature: parseFloat(temperature), maxTokens: maxTokens ? parseInt(maxTokens) : undefined, topP: parseFloat(topP), @@ -246,7 +306,7 @@ export default function NewSimulatorRunPage() {