|
| 1 | +/** |
| 2 | + * AI 补全适配器主入口 |
| 3 | + */ |
| 4 | +import { getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register' |
| 5 | +import { createSmartPrompt } from '../builders/promptBuilder.js' |
| 6 | +import { FIMPromptBuilder } from '../builders/fimPromptBuilder.js' |
| 7 | +import { detectModelType, calculateTokens, getStopSequences } from '../utils/modelUtils.js' |
| 8 | +import { cleanCompletion, buildLowcodeMetadata } from '../utils/completionUtils.js' |
| 9 | +import { buildQwenMessages, callQwenAPI } from './qwenAdapter.js' |
| 10 | +import { buildDeepSeekMessages, callDeepSeekAPI } from './deepseekAdapter.js' |
| 11 | +import { QWEN_CONFIG, DEFAULTS, ERROR_MESSAGES, MODEL_CONFIG } from '../constants.js' |
| 12 | + |
| 13 | +/** |
| 14 | + * 创建请求处理器 |
| 15 | + * @returns {Function} 请求处理函数 |
| 16 | + */ |
| 17 | +export function createCompletionHandler() { |
| 18 | + const fimBuilder = new FIMPromptBuilder(QWEN_CONFIG) |
| 19 | + |
| 20 | + return async (params) => { |
| 21 | + try { |
| 22 | + // 1. 获取 AI 配置 |
| 23 | + const { completeModel, apiKey, baseUrl } = getMetaApi(META_SERVICE.Robot).getSelectedQuickModelInfo() || {} |
| 24 | + |
| 25 | + if (!completeModel || !apiKey || !baseUrl) { |
| 26 | + return { |
| 27 | + completion: null, |
| 28 | + error: ERROR_MESSAGES.CONFIG_MISSING |
| 29 | + } |
| 30 | + } |
| 31 | + |
| 32 | + // 2. 提取代码上下文 |
| 33 | + const { |
| 34 | + textBeforeCursor = '', |
| 35 | + textAfterCursor = '', |
| 36 | + language = DEFAULTS.LANGUAGE, |
| 37 | + filename |
| 38 | + } = params.body?.completionMetadata || {} |
| 39 | + |
| 40 | + // 3. 构建低代码元数据和 prompt |
| 41 | + const lowcodeMetadata = buildLowcodeMetadata() |
| 42 | + const { context, instruction, fileContent } = createSmartPrompt({ |
| 43 | + textBeforeCursor, |
| 44 | + textAfterCursor, |
| 45 | + language, |
| 46 | + filename, |
| 47 | + technologies: DEFAULTS.TECHNOLOGIES, |
| 48 | + lowcodeMetadata |
| 49 | + }) |
| 50 | + |
| 51 | + // 4. 检测模型类型 |
| 52 | + const modelType = detectModelType(completeModel) |
| 53 | + |
| 54 | + let completionText = null |
| 55 | + let cursorContext = null |
| 56 | + |
| 57 | + // 5. 根据模型类型调用不同的 API |
| 58 | + if (modelType === MODEL_CONFIG.QWEN.TYPE) { |
| 59 | + // ===== Qwen 流程 ===== |
| 60 | + const { messages, cursorContext: ctx } = buildQwenMessages(fileContent, fimBuilder) |
| 61 | + cursorContext = ctx |
| 62 | + |
| 63 | + const config = { |
| 64 | + model: completeModel, |
| 65 | + maxTokens: calculateTokens(cursorContext), |
| 66 | + stopSequences: getStopSequences(cursorContext, MODEL_CONFIG.QWEN.TYPE) |
| 67 | + } |
| 68 | + |
| 69 | + completionText = await callQwenAPI(messages, config, apiKey, baseUrl) |
| 70 | + } else { |
| 71 | + // ===== DeepSeek 流程(默认) ===== |
| 72 | + const { messages } = buildDeepSeekMessages(context, instruction, fileContent) |
| 73 | + |
| 74 | + const config = { model: completeModel } |
| 75 | + const httpClient = getMetaApi(META_SERVICE.Http) |
| 76 | + |
| 77 | + completionText = await callDeepSeekAPI(messages, config, apiKey, baseUrl, httpClient) |
| 78 | + } |
| 79 | + |
| 80 | + // 6. 处理补全结果 |
| 81 | + if (completionText) { |
| 82 | + completionText = completionText.trim() |
| 83 | + |
| 84 | + // eslint-disable-next-line no-console |
| 85 | + console.log('✅ 收到补全:', completionText.substring(0, DEFAULTS.LOG_PREVIEW_LENGTH)) |
| 86 | + |
| 87 | + completionText = cleanCompletion(completionText, modelType, cursorContext) |
| 88 | + |
| 89 | + return { |
| 90 | + completion: completionText, |
| 91 | + error: null |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + return { |
| 96 | + completion: null, |
| 97 | + error: ERROR_MESSAGES.NO_COMPLETION |
| 98 | + } |
| 99 | + } catch (error) { |
| 100 | + // eslint-disable-next-line no-console |
| 101 | + console.error('❌ AI 补全请求失败:', error) |
| 102 | + return { |
| 103 | + completion: null, |
| 104 | + error: error.message || ERROR_MESSAGES.REQUEST_FAILED |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | +} |
0 commit comments