first-update
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
/**
|
||||
* 根据标签ID获取问题列表
|
||||
*/
|
||||
export async function GET(request, { params }) {
|
||||
try {
|
||||
const { projectId } = params;
|
||||
const { searchParams } = new URL(request.url);
|
||||
const tagId = searchParams.get('tagId');
|
||||
|
||||
// 验证参数
|
||||
if (!projectId) {
|
||||
return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!tagId) {
|
||||
return NextResponse.json({ error: '标签ID不能为空' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 获取标签信息
|
||||
const tag = await db.tags.findUnique({
|
||||
where: { id: tagId }
|
||||
});
|
||||
|
||||
if (!tag) {
|
||||
return NextResponse.json({ error: '标签不存在' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 获取或创建蒸馏文本块
|
||||
let distillChunk = await db.chunks.findFirst({
|
||||
where: {
|
||||
projectId,
|
||||
name: 'Distilled Content'
|
||||
}
|
||||
});
|
||||
|
||||
if (!distillChunk) {
|
||||
// 创建一个特殊的蒸馏文本块
|
||||
distillChunk = await db.chunks.create({
|
||||
data: {
|
||||
name: 'Distilled Content',
|
||||
projectId,
|
||||
fileId: 'distilled',
|
||||
fileName: 'distilled.md',
|
||||
content:
|
||||
'This text block is used to store questions generated through data distillation and is not related to actual literature.',
|
||||
summary: 'Questions generated through data distillation',
|
||||
size: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
const questions = await db.questions.findMany({
|
||||
where: {
|
||||
projectId,
|
||||
label: tag.label,
|
||||
chunkId: distillChunk.id
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json(questions);
|
||||
} catch (error) {
|
||||
console.error('[distill/questions/by-tag] 获取问题失败:', String(error));
|
||||
return NextResponse.json({ error: error.message || '获取问题失败' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { distillQuestionsPrompt } from '@/lib/llm/prompts/distillQuestions';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
const LLMClient = require('@/lib/llm/core');
|
||||
|
||||
/**
|
||||
* 生成问题接口:根据某个标签链路构造指定数量的问题
|
||||
*/
|
||||
export async function POST(request, { params }) {
|
||||
try {
|
||||
const { projectId } = params;
|
||||
|
||||
// 验证项目ID
|
||||
if (!projectId) {
|
||||
return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 });
|
||||
}
|
||||
|
||||
const { tagPath, currentTag, tagId, count = 5, model, language = 'zh' } = await request.json();
|
||||
|
||||
if (!currentTag || !tagPath) {
|
||||
const errorMsg = language === 'en' ? 'Tag information cannot be empty' : '标签信息不能为空';
|
||||
return NextResponse.json({ error: errorMsg }, { status: 400 });
|
||||
}
|
||||
|
||||
// 首先获取或创建蒸馏文本块
|
||||
let distillChunk = await db.chunks.findFirst({
|
||||
where: {
|
||||
projectId,
|
||||
name: 'Distilled Content'
|
||||
}
|
||||
});
|
||||
|
||||
if (!distillChunk) {
|
||||
// 创建一个特殊的蒸馏文本块
|
||||
distillChunk = await db.chunks.create({
|
||||
data: {
|
||||
name: 'Distilled Content',
|
||||
projectId,
|
||||
fileId: 'distilled',
|
||||
fileName: 'distilled.md',
|
||||
content:
|
||||
'This text block is used to store questions generated through data distillation and is not related to actual literature.',
|
||||
summary: 'Questions generated through data distillation',
|
||||
size: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取已有的问题,避免重复
|
||||
const existingQuestions = await db.questions.findMany({
|
||||
where: {
|
||||
projectId,
|
||||
label: currentTag,
|
||||
chunkId: distillChunk.id // 使用蒸馏文本块的 ID
|
||||
},
|
||||
select: { question: true }
|
||||
});
|
||||
|
||||
const existingQuestionTexts = existingQuestions.map(q => q.question);
|
||||
|
||||
const llmClient = new LLMClient(model);
|
||||
const prompt = await distillQuestionsPrompt(
|
||||
language,
|
||||
{ tagPath, currentTag, count, existingQuestionTexts },
|
||||
projectId
|
||||
);
|
||||
const { answer } = await llmClient.getResponseWithCOT(prompt);
|
||||
|
||||
let questions = [];
|
||||
try {
|
||||
questions = JSON.parse(answer);
|
||||
} catch (error) {
|
||||
console.error('解析问题JSON失败:', String(error));
|
||||
// 尝试使用正则表达式提取问题
|
||||
const matches = answer.match(/"([^"]+)"/g);
|
||||
if (matches) {
|
||||
questions = matches.map(match => match.replace(/"/g, ''));
|
||||
}
|
||||
}
|
||||
|
||||
// 保存问题到数据库
|
||||
const savedQuestions = [];
|
||||
for (const questionText of questions) {
|
||||
const question = await db.questions.create({
|
||||
data: {
|
||||
question: questionText,
|
||||
projectId,
|
||||
label: currentTag,
|
||||
chunkId: distillChunk.id
|
||||
}
|
||||
});
|
||||
savedQuestions.push(question);
|
||||
}
|
||||
|
||||
return NextResponse.json(savedQuestions);
|
||||
} catch (error) {
|
||||
console.error('生成问题失败:', String(error));
|
||||
return NextResponse.json({ error: error.message || '生成问题失败' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
/**
|
||||
* 更新标签接口
|
||||
*/
|
||||
export async function PUT(request, { params }) {
|
||||
try {
|
||||
const { projectId, tagId } = params;
|
||||
|
||||
// 验证参数
|
||||
if (!projectId || !tagId) {
|
||||
return NextResponse.json({ error: '项目ID和标签ID不能为空' }, { status: 400 });
|
||||
}
|
||||
|
||||
const { label } = await request.json();
|
||||
|
||||
if (!label || !label.trim()) {
|
||||
return NextResponse.json({ error: '标签名称不能为空' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 检查标签是否存在
|
||||
const existingTag = await db.tags.findUnique({
|
||||
where: { id: tagId }
|
||||
});
|
||||
|
||||
if (!existingTag) {
|
||||
return NextResponse.json({ error: '标签不存在' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 检查项目ID是否匹配
|
||||
if (existingTag.projectId !== projectId) {
|
||||
return NextResponse.json({ error: '无权限编辑此标签' }, { status: 403 });
|
||||
}
|
||||
|
||||
// 检查新标签名称是否已存在(同级标签)
|
||||
const duplicateTag = await db.tags.findFirst({
|
||||
where: {
|
||||
projectId,
|
||||
label: label.trim(),
|
||||
parentId: existingTag.parentId,
|
||||
id: { not: tagId }
|
||||
}
|
||||
});
|
||||
|
||||
if (duplicateTag) {
|
||||
return NextResponse.json({ error: '同级标签名称已存在' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 更新标签
|
||||
const updatedTag = await db.tags.update({
|
||||
where: { id: tagId },
|
||||
data: { label: label.trim() }
|
||||
});
|
||||
|
||||
return NextResponse.json(updatedTag);
|
||||
} catch (error) {
|
||||
console.error('[标签编辑] 更新标签失败:', String(error));
|
||||
return NextResponse.json({ error: error.message || '更新标签失败' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
/**
|
||||
* 获取项目的所有蒸馏标签
|
||||
*/
|
||||
export async function GET(request, { params }) {
|
||||
try {
|
||||
const { projectId } = params;
|
||||
|
||||
// 验证项目ID
|
||||
if (!projectId) {
|
||||
return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 获取所有标签
|
||||
const tags = await db.tags.findMany({
|
||||
where: {
|
||||
projectId
|
||||
},
|
||||
orderBy: {
|
||||
label: 'asc'
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json(tags);
|
||||
} catch (error) {
|
||||
console.error('获取蒸馏标签失败:', String(error));
|
||||
return NextResponse.json({ error: error.message || '获取蒸馏标签失败' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { distillTagsPrompt } from '@/lib/llm/prompts/distillTags';
|
||||
import { db } from '@/lib/db';
|
||||
import { getProject } from '@/lib/db/projects';
|
||||
|
||||
const LLMClient = require('@/lib/llm/core');
|
||||
|
||||
/**
|
||||
* 生成标签接口:根据顶级主题、某级标签构造指定数量的子标签
|
||||
*/
|
||||
export async function POST(request, { params }) {
|
||||
try {
|
||||
const { projectId } = params;
|
||||
|
||||
// 验证项目ID
|
||||
if (!projectId) {
|
||||
return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 });
|
||||
}
|
||||
|
||||
const { parentTag, parentTagId, tagPath, count = 10, model, language = 'zh' } = await request.json();
|
||||
|
||||
if (!parentTag) {
|
||||
const errorMsg = language === 'en' ? 'Topic tag name cannot be empty' : '主题标签名称不能为空';
|
||||
return NextResponse.json({ error: errorMsg }, { status: 400 });
|
||||
}
|
||||
|
||||
// 查询现有标签
|
||||
const existingTags = await db.tags.findMany({
|
||||
where: {
|
||||
projectId,
|
||||
parentId: parentTagId || null
|
||||
}
|
||||
});
|
||||
|
||||
const existingTagNames = existingTags.map(tag => tag.label);
|
||||
|
||||
// 创建LLM客户端
|
||||
const llmClient = new LLMClient(model);
|
||||
|
||||
// 生成提示词
|
||||
const prompt = await distillTagsPrompt(
|
||||
language,
|
||||
{ tagPath, parentTag, existingTags: existingTagNames, count },
|
||||
projectId
|
||||
);
|
||||
|
||||
// 调用大模型生成标签
|
||||
const { answer } = await llmClient.getResponseWithCOT(prompt);
|
||||
|
||||
// 解析返回的标签
|
||||
let tags = [];
|
||||
|
||||
try {
|
||||
tags = JSON.parse(answer);
|
||||
} catch (error) {
|
||||
console.error('解析标签JSON失败:', String(error));
|
||||
// 尝试使用正则表达式提取标签
|
||||
const matches = answer.match(/"([^"]+)"/g);
|
||||
if (matches) {
|
||||
tags = matches.map(match => match.replace(/"/g, ''));
|
||||
}
|
||||
}
|
||||
|
||||
// 保存标签到数据库
|
||||
const savedTags = [];
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const tagName = tags[i];
|
||||
try {
|
||||
const tag = await db.tags.create({
|
||||
data: {
|
||||
label: tagName,
|
||||
projectId,
|
||||
parentId: parentTagId || null
|
||||
}
|
||||
});
|
||||
savedTags.push(tag);
|
||||
} catch (error) {
|
||||
console.error(`[标签生成] 保存标签 ${tagName} 失败:`, String(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return NextResponse.json(savedTags);
|
||||
} catch (error) {
|
||||
console.error('[标签生成] 生成标签失败:', String(error));
|
||||
console.error('[标签生成] 错误堆栈:', error.stack);
|
||||
return NextResponse.json({ error: error.message || '生成标签失败' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user