feat(API): 添加提示词优化相关接口

- 新增 /api/prompt/optimize 接口用于调用 AI 优化提示词
- 支持简洁版和详细版两种优化模式
- 支持 Claude 原生格式和 OpenAI 兼容格式
- 新增 /api/prompt/history 接口管理优化历史记录
- 支持查询和删除历史记录
This commit is contained in:
gaoziman 2025-12-22 00:06:26 +08:00
parent fae1dfb7c9
commit 4b4732a583
2 changed files with 263 additions and 0 deletions

View File

@ -0,0 +1,97 @@
import { NextResponse } from 'next/server';
import { db } from '@/drizzle/db';
import { promptOptimizations } from '@/drizzle/schema';
import { eq, desc } from 'drizzle-orm';
import { getCurrentUser } from '@/lib/auth';
// GET /api/prompt/history - 获取用户的优化历史
export async function GET() {
try {
// 获取当前用户
const user = await getCurrentUser();
if (!user) {
return NextResponse.json(
{ error: '请先登录' },
{ status: 401 }
);
}
// 查询该用户的优化历史(最近 50 条)
const history = await db
.select()
.from(promptOptimizations)
.where(eq(promptOptimizations.userId, user.userId))
.orderBy(desc(promptOptimizations.createdAt))
.limit(50);
return NextResponse.json({
success: true,
data: history,
});
} catch (error) {
console.error('获取优化历史失败:', error);
return NextResponse.json(
{ error: '获取历史记录失败' },
{ status: 500 }
);
}
}
// DELETE /api/prompt/history - 删除指定的优化历史
export async function DELETE(request: Request) {
try {
// 获取当前用户
const user = await getCurrentUser();
if (!user) {
return NextResponse.json(
{ error: '请先登录' },
{ status: 401 }
);
}
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json(
{ error: '缺少记录 ID' },
{ status: 400 }
);
}
// 验证记录属于当前用户后删除
const record = await db
.select()
.from(promptOptimizations)
.where(eq(promptOptimizations.id, parseInt(id)))
.limit(1);
if (record.length === 0) {
return NextResponse.json(
{ error: '记录不存在' },
{ status: 404 }
);
}
if (record[0].userId !== user.userId) {
return NextResponse.json(
{ error: '无权删除此记录' },
{ status: 403 }
);
}
await db
.delete(promptOptimizations)
.where(eq(promptOptimizations.id, parseInt(id)));
return NextResponse.json({
success: true,
});
} catch (error) {
console.error('删除优化历史失败:', error);
return NextResponse.json(
{ error: '删除失败' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,166 @@
import { NextResponse } from 'next/server';
import { db } from '@/drizzle/db';
import { promptOptimizations, userSettings } from '@/drizzle/schema';
import { eq } from 'drizzle-orm';
import { getCurrentUser } from '@/lib/auth';
import { decryptApiKey } from '@/lib/crypto';
// 简洁版系统提示词
const CONCISE_SYSTEM_PROMPT = `你是一个提示词优化专家。用户会给你一个原始的想法或问题,请帮助优化成简洁明了的提示词。
1.
2.
3. 使
4. 150
5. `;
// 详细版系统提示词
const DETAILED_SYSTEM_PROMPT = `你是一个提示词优化专家。用户会给你一个原始的想法或问题,请帮助优化成结构化的详细提示词。
1. ****
2. ****
3. ****
4. ****
5. ****
- 使
-
-
- `;
// 规范化 URL
function normalizeBaseUrl(url: string): string {
return url.replace(/\/+$/, '');
}
// POST /api/prompt/optimize - 优化提示词
export async function POST(request: Request) {
try {
// 获取当前用户
const user = await getCurrentUser();
if (!user) {
return NextResponse.json(
{ error: '请先登录' },
{ status: 401 }
);
}
const body = await request.json();
const { originalPrompt, mode } = body;
if (!originalPrompt || !mode) {
return NextResponse.json(
{ error: '缺少必要参数' },
{ status: 400 }
);
}
if (!['concise', 'detailed'].includes(mode)) {
return NextResponse.json(
{ error: '无效的优化模式' },
{ status: 400 }
);
}
// 获取用户设置
const settings = await db.query.userSettings.findFirst({
where: eq(userSettings.userId, user.userId),
});
if (!settings?.cchApiKey) {
return NextResponse.json(
{ error: '请先在设置中配置 API Key' },
{ status: 400 }
);
}
// 解密 API Key
const apiKey = decryptApiKey(settings.cchApiKey);
const cchUrl = settings.cchUrl || process.env.CCH_DEFAULT_URL || 'https://claude.leocoder.cn/';
const apiFormat = (settings.apiFormat as 'claude' | 'openai') || 'claude';
// 选择系统提示词
const systemPrompt = mode === 'concise' ? CONCISE_SYSTEM_PROMPT : DETAILED_SYSTEM_PROMPT;
// 调用 AI 进行优化
let optimizedPrompt: string;
if (apiFormat === 'openai') {
// OpenAI 兼容格式
const response = await fetch(`${normalizeBaseUrl(cchUrl)}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
model: 'claude-sonnet-4-5-20250929',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: `请优化以下提示词:\n\n${originalPrompt}` },
],
temperature: 0.7,
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 调用失败: ${errorText}`);
}
const data = await response.json();
optimizedPrompt = data.choices?.[0]?.message?.content || '';
} else {
// Claude 原生格式
const response = await fetch(`${normalizeBaseUrl(cchUrl)}/v1/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01',
},
body: JSON.stringify({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 2048,
system: systemPrompt,
messages: [
{ role: 'user', content: `请优化以下提示词:\n\n${originalPrompt}` },
],
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 调用失败: ${errorText}`);
}
const data = await response.json();
optimizedPrompt = data.content?.[0]?.text || '';
}
// 清理 <think> 标签(如果有)
optimizedPrompt = optimizedPrompt.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
// 保存到数据库
await db.insert(promptOptimizations).values({
userId: user.userId,
originalPrompt,
optimizedPrompt,
mode,
});
return NextResponse.json({
success: true,
optimizedPrompt,
});
} catch (error) {
console.error('提示词优化失败:', error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : '优化失败,请稍后重试' },
{ status: 500 }
);
}
}