allow modify prompt

This commit is contained in:
songtianlun 2025-08-09 11:07:28 +08:00
parent e1ecc48460
commit 21e2d3d915
8 changed files with 179 additions and 12 deletions

View File

@ -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",

View File

@ -400,6 +400,10 @@
"copyError": "复制到剪贴板失败",
"executeError": "执行模拟失败",
"loadingRuns": "加载模拟运行中...",
"edit": "编辑",
"save": "保存",
"promptContentModified": "提示词内容已被修改",
"promptContentPlaceholder": "在这里输入您的自定义提示词内容...",
"status": {
"pending": "待执行",
"running": "运行中",

View File

@ -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"

View File

@ -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 = {

View File

@ -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,

View File

@ -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 (

View File

@ -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() {
<select
id="version"
value={selectedVersionId}
onChange={(e) => setSelectedVersionId(e.target.value)}
onChange={(e) => handleVersionChange(e.target.value)}
className="w-full mt-1 bg-background border border-input rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ring"
>
<option value="">{t('useLatestVersion')}</option>
@ -261,14 +321,63 @@ export default function NewSimulatorRunPage() {
</div>
)}
{promptContent && (
{editablePromptContent && (
<div>
<Label className="text-sm font-medium">{t('promptContent')}</Label>
<div className="mt-1 p-4 bg-muted rounded-md border max-h-48 overflow-y-auto">
<pre className="text-sm text-foreground whitespace-pre-wrap font-mono">
{promptContent}
</pre>
<div className="flex items-center justify-between mb-2">
<Label className="text-sm font-medium">{t('promptContent')}</Label>
<div className="flex items-center space-x-2">
{!isEditingPrompt && (
<Button
type="button"
variant="outline"
size="sm"
onClick={handleEditPrompt}
>
<Edit className="h-3 w-3 mr-1" />
{t('edit')}
</Button>
)}
{isEditingPrompt && (
<>
<Button
type="button"
variant="outline"
size="sm"
onClick={handleCancelPromptEdit}
>
{t('cancel')}
</Button>
<Button
type="button"
variant="default"
size="sm"
onClick={handleSavePromptEdit}
>
{t('save')}
</Button>
</>
)}
</div>
</div>
{isEditingPrompt ? (
<Textarea
value={editablePromptContent}
onChange={(e) => setEditablePromptContent(e.target.value)}
className="min-h-32 font-mono text-sm"
placeholder={t('promptContentPlaceholder')}
/>
) : (
<div className="mt-1 p-4 bg-muted rounded-md border max-h-48 overflow-y-auto">
<pre className="text-sm text-foreground whitespace-pre-wrap font-mono">
{editablePromptContent}
</pre>
</div>
)}
{getCustomPromptContent() && (
<p className="text-xs text-muted-foreground mt-2">
{t('promptContentModified')}
</p>
)}
</div>
)}
</div>

View File

@ -0,0 +1,44 @@
// 模拟器运行相关的工具函数
interface SimulatorRun {
promptContent?: string | null
prompt: {
content: string
}
promptVersion?: {
content: string
} | null
}
/**
*
* > >
*/
export function getPromptContent(run: SimulatorRun): string {
// 1. 优先使用自定义提示词内容
if (run.promptContent) {
return run.promptContent
}
// 2. 其次使用选择的版本内容
if (run.promptVersion?.content) {
return run.promptVersion.content
}
// 3. 最后使用原始提示词内容
return run.prompt.content
}
/**
*
*/
export function isPromptContentModified(run: SimulatorRun): boolean {
return !!run.promptContent
}
/**
*
*/
export function getOriginalPromptContent(run: Pick<SimulatorRun, 'prompt' | 'promptVersion'>): string {
return run.promptVersion?.content || run.prompt.content
}