/** * LLM API Routes * 處理 LLM 相關的 API 請求 */ const express = require('express'); const router = express.Router(); const llmService = require('../services/llm.service'); const { asyncHandler, createError } = require('../utils/errorHandler'); /** * POST /api/llm/test/gemini * 測試 Gemini API 連線 */ router.post('/test/gemini', asyncHandler(async (req, res) => { const result = await llmService.testGeminiConnection(); res.json(result); })); /** * POST /api/llm/test/deepseek * 測試 DeepSeek API 連線 */ router.post('/test/deepseek', asyncHandler(async (req, res) => { const result = await llmService.testDeepSeekConnection(); res.json(result); })); /** * POST /api/llm/test/openai * 測試 OpenAI API 連線 */ router.post('/test/openai', asyncHandler(async (req, res) => { const result = await llmService.testOpenAIConnection(); res.json(result); })); /** * POST /api/llm/test/all * 測試所有 LLM API 連線 */ router.post('/test/all', asyncHandler(async (req, res) => { const results = await llmService.testAllConnections(); res.json(results); })); /** * POST /api/llm/generate * 使用 LLM 生成內容 * * Body: * { * "prompt": "你的提示內容", * "provider": "gemini|deepseek|openai", (可選,預設使用 gemini) * "options": { * "temperature": 0.7, * "maxTokens": 2000 * } * } */ router.post('/generate', asyncHandler(async (req, res) => { const { prompt, provider, options } = req.body; if (!prompt) { throw createError('BAD_REQUEST', '缺少必要參數: prompt'); } const result = await llmService.generate(prompt, provider, options); res.json(result); })); /** * POST /api/llm/help-me-fill * Help Me AI 智能填寫功能 * * Body: * { * "cardType": "role|competency|performance|growth", * "cardId": "卡片ID", * "filledFields": { ... }, * "emptyFields": [ ... ], * "context": { ... } * } */ router.post('/help-me-fill', asyncHandler(async (req, res) => { const { cardType, cardId, filledFields, emptyFields, context } = req.body; if (!cardType || !emptyFields || emptyFields.length === 0) { throw createError('BAD_REQUEST', '缺少必要參數'); } // 建立提示詞 const prompt = buildHelpMeFillPrompt(cardType, filledFields, emptyFields, context); // 使用 LLM 生成建議內容 const result = await llmService.generate(prompt, 'gemini', { temperature: 0.7, maxTokens: 2000, }); if (!result.success) { throw createError('LLM_API_ERROR', '生成內容失敗'); } // 解析生成的內容 const suggestions = parseHelpMeFillResponse(result.content, emptyFields); res.json({ success: true, filledCount: Object.keys(suggestions).length, suggestions, }); })); /** * 建立 Help Me Fill 提示詞 */ function buildHelpMeFillPrompt(cardType, filledFields, emptyFields, context) { let prompt = `你是一個 HR 績效評核系統的智能助手。請根據以下資訊,為空白欄位生成合適的內容。\n\n`; prompt += `卡片類型: ${cardType}\n\n`; if (Object.keys(filledFields).length > 0) { prompt += `已填寫的欄位:\n`; for (const [key, value] of Object.entries(filledFields)) { prompt += `- ${key}: ${value}\n`; } prompt += `\n`; } if (context) { prompt += `上下文資訊:\n`; prompt += JSON.stringify(context, null, 2); prompt += `\n\n`; } prompt += `請為以下空白欄位生成內容:\n`; emptyFields.forEach(field => { prompt += `- ${field}\n`; }); prompt += `\n請以 JSON 格式回覆,格式如下:\n`; prompt += `{\n`; emptyFields.forEach(field => { prompt += ` "${field}": "生成的內容",\n`; }); prompt += `}\n`; return prompt; } /** * 解析 Help Me Fill 回應 */ function parseHelpMeFillResponse(content, emptyFields) { try { // 嘗試直接解析 JSON const jsonMatch = content.match(/\{[\s\S]*\}/); if (jsonMatch) { return JSON.parse(jsonMatch[0]); } // 如果無法解析 JSON,則返回空物件 return {}; } catch (error) { console.error('解析 LLM 回應失敗:', error); return {}; } } module.exports = router;