allow modify prompt
This commit is contained in:
parent
e1ecc48460
commit
21e2d3d915
@ -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",
|
||||
|
@ -400,6 +400,10 @@
|
||||
"copyError": "复制到剪贴板失败",
|
||||
"executeError": "执行模拟失败",
|
||||
"loadingRuns": "加载模拟运行中...",
|
||||
"edit": "编辑",
|
||||
"save": "保存",
|
||||
"promptContentModified": "提示词内容已被修改",
|
||||
"promptContentPlaceholder": "在这里输入您的自定义提示词内容...",
|
||||
"status": {
|
||||
"pending": "待执行",
|
||||
"running": "运行中",
|
||||
|
@ -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"
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
@ -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 (
|
||||
|
@ -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>
|
||||
|
44
src/lib/simulator-utils.ts
Normal file
44
src/lib/simulator-utils.ts
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user