|
|
const axios = require('axios');
|
|
|
const AipSpeechClient = require('baidu-aip-sdk').speech;
|
|
|
|
|
|
const APP_ID = process.env.BAIDU_APP_ID;
|
|
|
const API_KEY = process.env.BAIDU_API_KEY;
|
|
|
const SECRET_KEY = process.env.BAIDU_SECRET_KEY;
|
|
|
const QIANFAN_API_KEY = process.env.QIANFAN_API_KEY;
|
|
|
const QIANFAN_SECRET_KEY = process.env.QIANFAN_SECRET_KEY;
|
|
|
|
|
|
const client = new AipSpeechClient(APP_ID, API_KEY, SECRET_KEY);
|
|
|
|
|
|
async function transcribeAudio(req, res) {
|
|
|
if (!req.file) {
|
|
|
return res.status(400).json({ msg: 'No audio file uploaded.' });
|
|
|
}
|
|
|
|
|
|
const audioBuffer = req.file.buffer;
|
|
|
|
|
|
try {
|
|
|
const result = await client.recognize(audioBuffer, 'wav', 16000, {
|
|
|
dev_pid: 1537,
|
|
|
});
|
|
|
|
|
|
if (result.err_no === 0) {
|
|
|
return res.json({ result: result.result[0] });
|
|
|
}
|
|
|
|
|
|
console.error('Baidu ASR service error:', result);
|
|
|
return res.status(502).json({ msg: 'Baidu ASR service error', error: result });
|
|
|
} catch (error) {
|
|
|
console.error('Error calling Baidu ASR API:', error);
|
|
|
return res.status(500).json({ msg: 'Server error during transcription.' });
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function getQianfanAccessToken() {
|
|
|
if (!QIANFAN_API_KEY || !QIANFAN_SECRET_KEY) {
|
|
|
throw new Error('QIANFAN credentials are not configured');
|
|
|
}
|
|
|
|
|
|
const url = `https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${QIANFAN_API_KEY}&client_secret=${QIANFAN_SECRET_KEY}`;
|
|
|
|
|
|
try {
|
|
|
const response = await axios.get(url);
|
|
|
if (!response.data?.access_token) {
|
|
|
throw new Error('Invalid access token response');
|
|
|
}
|
|
|
return response.data.access_token;
|
|
|
} catch (error) {
|
|
|
const message = error.response?.data || error.message;
|
|
|
console.error('Error getting Qianfan access token:', message);
|
|
|
throw new Error('Failed to get Qianfan access token');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function evaluateFeynmanAttempt(req, res) {
|
|
|
const { originalContent, transcribedText } = req.body || {};
|
|
|
|
|
|
if (!originalContent || !transcribedText) {
|
|
|
return res.status(400).json({ msg: 'Original content and transcribed text are required.' });
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
const accessToken = await getQianfanAccessToken();
|
|
|
const url = `https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant?access_token=${accessToken}`;
|
|
|
|
|
|
const prompt = `你是一个严格而友善的计算机科学学习教练。你的任务是评估学生对一个知识点的复述,并给出反馈。
|
|
|
|
|
|
【原始知识点】:
|
|
|
\`\`\`
|
|
|
${originalContent}
|
|
|
\`\`\`
|
|
|
|
|
|
【学生的口头复述文本】:
|
|
|
\`\`\`
|
|
|
${transcribedText}
|
|
|
\`\`\`
|
|
|
|
|
|
请你完成以下三项任务:
|
|
|
1. **文本润色**: 将学生的复述文本润色成一段通顺、专业、书面化的文字。修正明显的语法错误和口语化表达,但保持其核心观点不变。
|
|
|
2. **综合评价**: 基于原始知识点,对学生的复述进行评价。指出其优点和可以改进的地方。
|
|
|
3. **评分**: 综合考虑准确性、完整性、逻辑性和流畅性,给出一个0到100的整数分数。
|
|
|
|
|
|
请严格按照以下JSON格式返回你的结果,不要包含任何额外的解释或文字。
|
|
|
{
|
|
|
"polishedText": "这里是润色后的文本",
|
|
|
"evaluation": "这里是你的综合评价",
|
|
|
"strengths": ["优点1", "优点2"],
|
|
|
"weaknesses": ["可以改进的地方1", "可以改进的地方2"],
|
|
|
"score": 85
|
|
|
}`;
|
|
|
|
|
|
const response = await axios.post(
|
|
|
url,
|
|
|
{
|
|
|
messages: [
|
|
|
{
|
|
|
role: 'user',
|
|
|
content: prompt,
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
timeout: 20_000,
|
|
|
},
|
|
|
);
|
|
|
|
|
|
const rawResult = response.data?.result || response.data?.body?.result;
|
|
|
if (!rawResult) {
|
|
|
throw new Error('Empty result from Qianfan');
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
const parsed = JSON.parse(rawResult);
|
|
|
return res.json(parsed);
|
|
|
} catch (parseError) {
|
|
|
console.error('Failed to parse Qianfan result:', rawResult);
|
|
|
throw new Error('Invalid JSON returned by Qianfan');
|
|
|
}
|
|
|
} catch (error) {
|
|
|
const message = error.response?.data || error.message;
|
|
|
console.error('Error calling LLM API:', message);
|
|
|
return res.status(500).json({ msg: 'AI evaluation failed', detail: message });
|
|
|
}
|
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
|
transcribeAudio,
|
|
|
evaluateFeynmanAttempt,
|
|
|
}; |