allow modify prompt
This commit is contained in:
parent
e1ecc48460
commit
21e2d3d915
@ -400,6 +400,10 @@
|
|||||||
"copyError": "Failed to copy to clipboard",
|
"copyError": "Failed to copy to clipboard",
|
||||||
"executeError": "Failed to execute simulation",
|
"executeError": "Failed to execute simulation",
|
||||||
"loadingRuns": "Loading simulation runs...",
|
"loadingRuns": "Loading simulation runs...",
|
||||||
|
"edit": "Edit",
|
||||||
|
"save": "Save",
|
||||||
|
"promptContentModified": "Prompt content has been modified",
|
||||||
|
"promptContentPlaceholder": "Enter your custom prompt content here...",
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"running": "Running",
|
"running": "Running",
|
||||||
|
@ -400,6 +400,10 @@
|
|||||||
"copyError": "复制到剪贴板失败",
|
"copyError": "复制到剪贴板失败",
|
||||||
"executeError": "执行模拟失败",
|
"executeError": "执行模拟失败",
|
||||||
"loadingRuns": "加载模拟运行中...",
|
"loadingRuns": "加载模拟运行中...",
|
||||||
|
"edit": "编辑",
|
||||||
|
"save": "保存",
|
||||||
|
"promptContentModified": "提示词内容已被修改",
|
||||||
|
"promptContentPlaceholder": "在这里输入您的自定义提示词内容...",
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "待执行",
|
"pending": "待执行",
|
||||||
"running": "运行中",
|
"running": "运行中",
|
||||||
|
@ -246,6 +246,7 @@ model SimulatorRun {
|
|||||||
promptVersionId String? // 可选,如果选择了特定版本
|
promptVersionId String? // 可选,如果选择了特定版本
|
||||||
modelId String
|
modelId String
|
||||||
userInput String // 用户输入内容
|
userInput String // 用户输入内容
|
||||||
|
promptContent String? // 自定义提示词内容(如果用户修改了原提示词)
|
||||||
output String? // AI响应输出
|
output String? // AI响应输出
|
||||||
error String? // 错误信息
|
error String? // 错误信息
|
||||||
status String @default("pending") // "pending", "running", "completed", "failed"
|
status String @default("pending") // "pending", "running", "completed", "failed"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { createServerSupabaseClient } from "@/lib/supabase-server";
|
import { createServerSupabaseClient } from "@/lib/supabase-server";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
|
import { getPromptContent } from "@/lib/simulator-utils";
|
||||||
|
|
||||||
export async function POST(
|
export async function POST(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
@ -42,7 +43,7 @@ export async function POST(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 准备AI API请求
|
// 准备AI API请求
|
||||||
const promptContent = run.promptVersion?.content || run.prompt.content;
|
const promptContent = getPromptContent(run);
|
||||||
const finalPrompt = `${promptContent}\n\nUser Input: ${run.userInput}`;
|
const finalPrompt = `${promptContent}\n\nUser Input: ${run.userInput}`;
|
||||||
|
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
|
@ -77,6 +77,7 @@ export async function POST(request: NextRequest) {
|
|||||||
promptVersionId,
|
promptVersionId,
|
||||||
modelId,
|
modelId,
|
||||||
userInput,
|
userInput,
|
||||||
|
promptContent,
|
||||||
temperature = 0.7,
|
temperature = 0.7,
|
||||||
maxTokens,
|
maxTokens,
|
||||||
topP,
|
topP,
|
||||||
@ -114,6 +115,7 @@ export async function POST(request: NextRequest) {
|
|||||||
promptVersionId,
|
promptVersionId,
|
||||||
modelId,
|
modelId,
|
||||||
userInput,
|
userInput,
|
||||||
|
promptContent,
|
||||||
temperature,
|
temperature,
|
||||||
maxTokens,
|
maxTokens,
|
||||||
topP,
|
topP,
|
||||||
|
@ -29,11 +29,13 @@ import Link from 'next/link'
|
|||||||
import { formatDistanceToNow } from 'date-fns'
|
import { formatDistanceToNow } from 'date-fns'
|
||||||
import { zhCN, enUS } from 'date-fns/locale'
|
import { zhCN, enUS } from 'date-fns/locale'
|
||||||
import { useLocale } from 'next-intl'
|
import { useLocale } from 'next-intl'
|
||||||
|
import { getPromptContent } from '@/lib/simulator-utils'
|
||||||
|
|
||||||
interface SimulatorRun {
|
interface SimulatorRun {
|
||||||
id: string
|
id: string
|
||||||
status: string
|
status: string
|
||||||
userInput: string
|
userInput: string
|
||||||
|
promptContent?: string | null
|
||||||
output?: string
|
output?: string
|
||||||
error?: string
|
error?: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
@ -233,7 +235,7 @@ export default function SimulatorRunPage({ params }: { params: Promise<{ id: str
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const promptContent = run.promptVersion?.content || run.prompt.content
|
const promptContent = getPromptContent(run)
|
||||||
const currentOutput = (isExecuting && run.status === 'pending') ? streamOutput : run.output
|
const currentOutput = (isExecuting && run.status === 'pending') ? streamOutput : run.output
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -20,7 +20,8 @@ import {
|
|||||||
Zap,
|
Zap,
|
||||||
DollarSign,
|
DollarSign,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
ChevronDown
|
ChevronDown,
|
||||||
|
Edit
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
@ -61,6 +62,8 @@ export default function NewSimulatorRunPage() {
|
|||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [isCreating, setIsCreating] = useState(false)
|
const [isCreating, setIsCreating] = useState(false)
|
||||||
const [showAdvanced, setShowAdvanced] = useState(false)
|
const [showAdvanced, setShowAdvanced] = useState(false)
|
||||||
|
const [editablePromptContent, setEditablePromptContent] = useState('')
|
||||||
|
const [isEditingPrompt, setIsEditingPrompt] = useState(false)
|
||||||
|
|
||||||
// Advanced settings
|
// Advanced settings
|
||||||
const [temperature, setTemperature] = useState('0.7')
|
const [temperature, setTemperature] = useState('0.7')
|
||||||
@ -101,9 +104,7 @@ export default function NewSimulatorRunPage() {
|
|||||||
}, [user])
|
}, [user])
|
||||||
|
|
||||||
const selectedPrompt = prompts.find(p => p.id === selectedPromptId)
|
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 selectedModel = models.find(m => m.id === selectedModelId)
|
||||||
const promptContent = selectedVersion?.content || selectedPrompt?.content || ''
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!authLoading && user) {
|
if (!authLoading && user) {
|
||||||
@ -114,6 +115,7 @@ export default function NewSimulatorRunPage() {
|
|||||||
const handlePromptChange = (promptId: string) => {
|
const handlePromptChange = (promptId: string) => {
|
||||||
setSelectedPromptId(promptId)
|
setSelectedPromptId(promptId)
|
||||||
setSelectedVersionId('')
|
setSelectedVersionId('')
|
||||||
|
setIsEditingPrompt(false)
|
||||||
|
|
||||||
// Auto-select latest version
|
// Auto-select latest version
|
||||||
const prompt = prompts.find(p => p.id === promptId)
|
const prompt = prompts.find(p => p.id === promptId)
|
||||||
@ -122,9 +124,66 @@ export default function NewSimulatorRunPage() {
|
|||||||
current.version > latest.version ? current : latest
|
current.version > latest.version ? current : latest
|
||||||
)
|
)
|
||||||
setSelectedVersionId(latestVersion.id)
|
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) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (!selectedPromptId || !selectedModelId || !userInput.trim()) {
|
if (!selectedPromptId || !selectedModelId || !userInput.trim()) {
|
||||||
@ -143,6 +202,7 @@ export default function NewSimulatorRunPage() {
|
|||||||
promptVersionId: selectedVersionId || undefined,
|
promptVersionId: selectedVersionId || undefined,
|
||||||
modelId: selectedModelId,
|
modelId: selectedModelId,
|
||||||
userInput,
|
userInput,
|
||||||
|
promptContent: getCustomPromptContent(),
|
||||||
temperature: parseFloat(temperature),
|
temperature: parseFloat(temperature),
|
||||||
maxTokens: maxTokens ? parseInt(maxTokens) : undefined,
|
maxTokens: maxTokens ? parseInt(maxTokens) : undefined,
|
||||||
topP: parseFloat(topP),
|
topP: parseFloat(topP),
|
||||||
@ -246,7 +306,7 @@ export default function NewSimulatorRunPage() {
|
|||||||
<select
|
<select
|
||||||
id="version"
|
id="version"
|
||||||
value={selectedVersionId}
|
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"
|
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>
|
<option value="">{t('useLatestVersion')}</option>
|
||||||
@ -261,14 +321,63 @@ export default function NewSimulatorRunPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{promptContent && (
|
{editablePromptContent && (
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-sm font-medium">{t('promptContent')}</Label>
|
<div className="flex items-center justify-between mb-2">
|
||||||
<div className="mt-1 p-4 bg-muted rounded-md border max-h-48 overflow-y-auto">
|
<Label className="text-sm font-medium">{t('promptContent')}</Label>
|
||||||
<pre className="text-sm text-foreground whitespace-pre-wrap font-mono">
|
<div className="flex items-center space-x-2">
|
||||||
{promptContent}
|
{!isEditingPrompt && (
|
||||||
</pre>
|
<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>
|
</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>
|
||||||
)}
|
)}
|
||||||
</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