diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8bf16fe..208a8f2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -242,6 +242,7 @@ model Model { model SimulatorRun { id String @id @default(cuid()) userId String + name String @default("Simulation Run") // 运行名称 promptId String promptVersionId String? // 可选,如果选择了特定版本 modelId String diff --git a/src/app/api/simulator/[id]/route.ts b/src/app/api/simulator/[id]/route.ts index 0c971a7..b5f119b 100644 --- a/src/app/api/simulator/[id]/route.ts +++ b/src/app/api/simulator/[id]/route.ts @@ -20,7 +20,25 @@ export async function GET( id, userId: user.id, }, - include: { + select: { + id: true, + name: true, + status: true, + userInput: true, + promptContent: true, + output: true, + error: true, + createdAt: true, + completedAt: true, + temperature: true, + maxTokens: true, + topP: true, + frequencyPenalty: true, + presencePenalty: true, + inputTokens: true, + outputTokens: true, + totalCost: true, + duration: true, prompt: { select: { id: true, name: true, content: true } }, @@ -90,7 +108,17 @@ export async function PATCH( ...(duration !== undefined && { duration }), ...(status === "completed" && { completedAt: new Date() }), }, - include: { + select: { + id: true, + name: true, + status: true, + output: true, + error: true, + inputTokens: true, + outputTokens: true, + totalCost: true, + duration: true, + completedAt: true, prompt: { select: { id: true, name: true } }, diff --git a/src/app/api/simulator/route.ts b/src/app/api/simulator/route.ts index abadfc5..ab632b9 100644 --- a/src/app/api/simulator/route.ts +++ b/src/app/api/simulator/route.ts @@ -26,7 +26,19 @@ export async function GET(request: NextRequest) { const [runs, total] = await Promise.all([ prisma.simulatorRun.findMany({ where, - include: { + select: { + id: true, + name: true, + status: true, + userInput: true, + output: true, + error: true, + createdAt: true, + completedAt: true, + inputTokens: true, + outputTokens: true, + totalCost: true, + duration: true, prompt: { select: { id: true, name: true } }, @@ -70,6 +82,7 @@ export async function POST(request: NextRequest) { const body = await request.json(); const { + name, promptId, promptVersionId, modelId, @@ -80,18 +93,40 @@ export async function POST(request: NextRequest) { topP, frequencyPenalty, presencePenalty, + // 用于创建新prompt的字段 + createNewPrompt, + newPromptName, + newPromptContent, } = body; - // 验证用户是否拥有该prompt - const prompt = await prisma.prompt.findFirst({ - where: { - id: promptId, - userId: user.id, - }, - }); + let finalPromptId = promptId; - if (!prompt) { - return NextResponse.json({ error: "Prompt not found" }, { status: 404 }); + // 如果是创建新prompt模式 + if (createNewPrompt && newPromptContent) { + // 创建新的prompt + const newPrompt = await prisma.prompt.create({ + data: { + userId: user.id, + name: newPromptName || name || "New Prompt", + content: newPromptContent, + visibility: "private", + }, + }); + finalPromptId = newPrompt.id; + } else if (promptId) { + // 验证用户是否拥有该prompt + const prompt = await prisma.prompt.findFirst({ + where: { + id: promptId, + userId: user.id, + }, + }); + + if (!prompt) { + return NextResponse.json({ error: "Prompt not found" }, { status: 404 }); + } + } else { + return NextResponse.json({ error: "Either promptId or newPromptContent is required" }, { status: 400 }); } // 验证模型是否可用 @@ -108,7 +143,8 @@ export async function POST(request: NextRequest) { const run = await prisma.simulatorRun.create({ data: { userId: user.id, - promptId, + name: name || "Simulation Run", + promptId: finalPromptId, promptVersionId, modelId, userInput, diff --git a/src/app/simulator/[id]/page.tsx b/src/app/simulator/[id]/page.tsx index e8dac4c..7d6f5bf 100644 --- a/src/app/simulator/[id]/page.tsx +++ b/src/app/simulator/[id]/page.tsx @@ -33,6 +33,7 @@ import { getPromptContent } from '@/lib/simulator-utils' interface SimulatorRun { id: string + name: string status: string userInput: string promptContent?: string | null @@ -252,7 +253,7 @@ export default function SimulatorRunPage({ params }: { params: Promise<{ id: str

- {run.prompt.name} + {run.name}

diff --git a/src/app/simulator/new/page.tsx b/src/app/simulator/new/page.tsx index 0397c6c..71f800a 100644 --- a/src/app/simulator/new/page.tsx +++ b/src/app/simulator/new/page.tsx @@ -65,6 +65,10 @@ export default function NewSimulatorRunPage() { const [editablePromptContent, setEditablePromptContent] = useState('') const [isEditingPrompt, setIsEditingPrompt] = useState(false) + // 新增状态 + const [simulatorName, setSimulatorName] = useState('') + const [promptInputMode, setPromptInputMode] = useState<'select' | 'create'>('select') // 选择模式:选择现有提示词 或 创建新提示词 + // Advanced settings const [temperature, setTemperature] = useState('0.7') const [maxTokens, setMaxTokens] = useState('') @@ -128,6 +132,11 @@ export default function NewSimulatorRunPage() { } else if (prompt) { setEditablePromptContent(prompt.content) } + + // 自动填充名称(如果当前名称为空) + if (!simulatorName && prompt) { + setSimulatorName(`${prompt.name} 模拟运行`) + } } const handleVersionChange = (versionId: string) => { @@ -186,29 +195,68 @@ export default function NewSimulatorRunPage() { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() - if (!selectedPromptId || !selectedModelId || !userInput.trim()) { + + // 验证必填字段 + if (!selectedModelId || !userInput.trim() || !simulatorName.trim()) { + return + } + + // 在创建模式下,需要有提示词内容 + if (promptInputMode === 'create' && !editablePromptContent.trim()) { + return + } + + // 在选择模式下,需要选择提示词 + if (promptInputMode === 'select' && !selectedPromptId) { return } setIsCreating(true) try { + const requestBody: { + name: string; + modelId: string; + userInput: string; + temperature: number; + maxTokens?: number; + topP: number; + frequencyPenalty: number; + presencePenalty: number; + createNewPrompt?: boolean; + newPromptName?: string; + newPromptContent?: string; + promptId?: string; + promptVersionId?: string; + promptContent?: string; + } = { + name: simulatorName, + modelId: selectedModelId, + userInput, + temperature: parseFloat(temperature), + maxTokens: maxTokens ? parseInt(maxTokens) : undefined, + topP: parseFloat(topP), + frequencyPenalty: parseFloat(frequencyPenalty), + presencePenalty: parseFloat(presencePenalty), + } + + if (promptInputMode === 'create') { + // 创建新提示词模式 + requestBody.createNewPrompt = true + requestBody.newPromptName = simulatorName.replace(' 模拟运行', '') + requestBody.newPromptContent = editablePromptContent + } else { + // 选择现有提示词模式 + requestBody.promptId = selectedPromptId + requestBody.promptVersionId = selectedVersionId || undefined + requestBody.promptContent = getCustomPromptContent() + } + const response = await fetch('/api/simulator', { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ - promptId: selectedPromptId, - promptVersionId: selectedVersionId || undefined, - modelId: selectedModelId, - userInput, - promptContent: getCustomPromptContent(), - temperature: parseFloat(temperature), - maxTokens: maxTokens ? parseInt(maxTokens) : undefined, - topP: parseFloat(topP), - frequencyPenalty: parseFloat(frequencyPenalty), - presencePenalty: parseFloat(presencePenalty), - }), + body: JSON.stringify(requestBody), }) if (response.ok) { @@ -267,104 +315,178 @@ export default function NewSimulatorRunPage() {
) : (
- {/* Select Prompt */} + {/* Prompt Configuration */}

- {t('selectPrompt')} + 提示词配置

- Choose the prompt you want to test with the AI model + 选择现有提示词或直接输入新的提示词内容

-
-
- - + 选择现有提示词 + +
- - {selectedPrompt && selectedPrompt.versions.length > 0 && ( -
- - handlePromptChange(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" + required + > + + {prompts.map(prompt => ( + ))} - + +
+ + {selectedPrompt && selectedPrompt.versions.length > 0 && ( +
+ + +
+ )} + + )} + + {/* 名称输入框 - 在提示词选择下方 */} +
+ + setSimulatorName(e.target.value)} + placeholder="输入模拟运行的名称" + className="mt-1" + required + /> +

+ 为此次模拟运行设置一个易于识别的名称 +

+
+ + {promptInputMode === 'create' && ( +
+

+ 💡 系统将自动创建一个新的提示词并关联到此模拟运行 +

)} - {editablePromptContent && ( + {/* 提示词内容显示/编辑 */} + {(editablePromptContent || promptInputMode === 'create') && (
- -
- {!isEditingPrompt && ( - - )} - {isEditingPrompt && ( - <> + + {promptInputMode === 'select' && ( +
+ {!isEditingPrompt && ( - - - )} -
+ )} + {isEditingPrompt && ( + <> + + + + )} +
+ )}
- {isEditingPrompt ? ( + + {(isEditingPrompt || promptInputMode === 'create') ? (