Skip to content

Commit aadcfe7

Browse files
committed
feat(script): Refactor AI completion system with multi-model adapter support
1 parent dbee79b commit aadcfe7

18 files changed

Lines changed: 1711 additions & 232 deletions

File tree

packages/plugins/robot/src/constants/model-config.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,22 @@ export const DEFAULT_LLM_MODELS = [
8181
}
8282
},
8383
{
84-
label: 'Qwen Coder编程模型(Flash)',
85-
name: 'qwen3-coder-flash',
84+
label: 'Qwen2.5 Coder编程模型-最快响应',
85+
name: 'qwen-coder-turbo-latest',
8686
capabilities: {
8787
toolCalling: true,
8888
compact: true,
8989
jsonOutput: bailianJsonOutputExtraBody
9090
}
9191
},
9292
{
93-
label: 'Qwen3(14b)',
94-
name: 'qwen3-14b',
95-
capabilities: { compact: true, toolCalling: true, jsonOutput: bailianJsonOutputExtraBody }
96-
},
97-
{
98-
label: 'Qwen3(8b)',
99-
name: 'qwen3-8b',
100-
capabilities: { compact: true, toolCalling: true, jsonOutput: bailianJsonOutputExtraBody }
93+
label: 'Qwen2.5 Coder编程模型(32B)',
94+
name: 'qwen2.5-coder-32b-instruct',
95+
capabilities: {
96+
toolCalling: true,
97+
compact: true,
98+
jsonOutput: bailianJsonOutputExtraBody
99+
}
101100
}
102101
]
103102
},
@@ -120,6 +119,16 @@ export const DEFAULT_LLM_MODELS = [
120119
},
121120
jsonOutput: jsonOutputExtraBody
122121
}
122+
},
123+
{
124+
// TODO: https://api.deepseek.com/beta 支持 FIM
125+
label: 'Deepseek Coder编程模型',
126+
name: 'deepseek-chat',
127+
capabilities: {
128+
toolCalling: true,
129+
compact: true,
130+
jsonOutput: bailianJsonOutputExtraBody
131+
}
123132
}
124133
]
125134
}

packages/plugins/script/meta.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default {
66
width: 600,
77
widthResizable: true,
88
options: {
9-
enableAICompletion: true
9+
enableAICompletion: false // 禁用旧的 AI 补全系统,使用新的 monacopilot
1010
},
1111
confirm: 'close' // 当点击插件栏切换或关闭前是否需要确认, 会调用插件中confirm值指定的方法,e.g. 此处指向 close方法,会调用插件的close方法执行确认逻辑
1212
}

packages/plugins/script/src/Main.vue

Lines changed: 6 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ import { onBeforeUnmount, reactive, provide } from 'vue'
3636
import { Button } from '@opentiny/vue'
3737
import { registerCompletion } from 'monacopilot'
3838
import { VueMonaco, PluginPanel } from '@opentiny/tiny-engine-common/component'
39-
import { useHelp, useLayout, useResource, useCanvas } from '@opentiny/tiny-engine-meta-register'
39+
import { useHelp, useLayout } from '@opentiny/tiny-engine-meta-register'
4040
import { initCompletion } from '@opentiny/tiny-engine-common/js/completion'
4141
import { initLinter } from '@opentiny/tiny-engine-common/js/linter'
4242
import useMethod, { saveMethod, highlightMethod, getMethodNameList, getMethods } from './js/method'
43-
import { requestManager } from './js/requestManager'
44-
import { shouldTriggerCompletion } from './js/completionTrigger'
43+
import { shouldTriggerCompletion } from './ai-completion/triggers/completionTrigger'
44+
import { createCompletionHandler } from './ai-completion/adapters/index'
4545
4646
export const api = {
4747
saveMethod,
@@ -122,33 +122,9 @@ export default {
122122
const monacoInstance = monacoRef.value.getMonaco()
123123
const editorInstance = monacoRef.value.getEditor()
124124
125-
// 构建低代码上下文
126-
const getLowcodeContext = () => {
127-
const { dataSource = [], utils = [], globalState = [] } = useResource().appSchemaState || {}
128-
const { state: pageState = {}, methods = {} } = useCanvas().getPageSchema() || {}
129-
const currentSchema = useCanvas().getCurrentSchema()
130-
131-
return {
132-
dataSource,
133-
utils,
134-
globalState,
135-
state: pageState,
136-
methods,
137-
currentSchema
138-
}
139-
}
140-
141-
// 配置请求管理器
142-
requestManager.setEndpoint('http://localhost:3000/code-completion')
143-
requestManager.setDebounceDelay(300) // 设置防抖延迟为 300ms
144-
requestManager.setDebounceEnabled(true)
145-
146-
// 创建增强的请求处理器
147-
const baseRequestHandler = requestManager.createRequestHandler()
148-
149125
registerCompletion(monacoInstance, editorInstance, {
150126
language: 'javascript',
151-
endpoint: 'http://localhost:3000/code-completion',
127+
endpoint: '/app-center/api/chat/completions',
152128
filename: 'page.js',
153129
trigger: 'onTyping',
154130
maxContextLines: 50,
@@ -172,29 +148,8 @@ export default {
172148
})
173149
},
174150
175-
// 🚀 请求处理器:防抖 + 请求取消 + 低代码元数据
176-
requestHandler: async (params) => {
177-
try {
178-
// 添加低代码元数据
179-
const lowcodeMetadata = getLowcodeContext()
180-
const enhancedParams = {
181-
body: {
182-
completionMetadata: {
183-
...params.body.completionMetadata,
184-
lowcodeMetadata
185-
}
186-
}
187-
}
188-
189-
// 使用请求管理器发送请求(带防抖和取消功能)
190-
return await baseRequestHandler(enhancedParams)
191-
} catch (error: any) {
192-
return {
193-
completion: null,
194-
error: error.message
195-
}
196-
}
197-
}
151+
// 🚀 请求处理器:支持 DeepSeek 和 Qwen 模型
152+
requestHandler: createCompletionHandler() as any
198153
})
199154
} catch (error) {
200155
// eslint-disable-next-line no-console
@@ -208,8 +163,6 @@ export default {
208163
})
209164
// 终止 ESLint worker
210165
;(state.linterWorker as any)?.terminate?.()
211-
// 清理请求管理器
212-
requestManager.reset()
213166
})
214167
215168
return {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* DeepSeek 专用适配器
3+
* 使用 Chat Completions API(通过后端代理)
4+
*/
5+
import { SYSTEM_BASE_PROMPT, createUserPrompt } from '../prompts/templates.js'
6+
import { API_ENDPOINTS, HTTP_CONFIG } from '../constants.js'
7+
8+
/**
9+
* 构建 DeepSeek Chat 格式的 messages
10+
* @param {string} context - 上下文信息
11+
* @param {string} instruction - 指令
12+
* @param {string} fileContent - 文件内容
13+
* @returns {{ messages: Array, cursorContext: null }} Messages 和上下文
14+
*/
15+
export function buildDeepSeekMessages(context, instruction, fileContent) {
16+
// eslint-disable-next-line no-console
17+
console.log('🎯 使用 DeepSeek Chat 格式')
18+
19+
const systemPrompt = `${context}\n\n${SYSTEM_BASE_PROMPT}`
20+
const userPrompt = createUserPrompt(instruction, fileContent)
21+
22+
return {
23+
messages: [
24+
{
25+
role: 'system',
26+
content: systemPrompt
27+
},
28+
{
29+
role: 'user',
30+
content: userPrompt
31+
}
32+
],
33+
cursorContext: null
34+
}
35+
}
36+
37+
/**
38+
* 调用 DeepSeek Chat API(通过后端代理)
39+
* @param {Array} messages - Messages 数组
40+
* @param {Object} config - 配置对象
41+
* @param {string} apiKey - API 密钥
42+
* @param {string} baseUrl - 基础 URL
43+
* @param {Object} httpClient - HTTP 客户端
44+
* @returns {Promise<string>} 补全文本
45+
*/
46+
export async function callDeepSeekAPI(messages, config, apiKey, baseUrl, httpClient) {
47+
const response = await httpClient.post(
48+
API_ENDPOINTS.CHAT_COMPLETIONS,
49+
{
50+
model: config.model,
51+
messages,
52+
baseUrl,
53+
stream: HTTP_CONFIG.STREAM
54+
},
55+
{
56+
headers: {
57+
'Content-Type': HTTP_CONFIG.CONTENT_TYPE,
58+
Authorization: `Bearer ${apiKey || ''}`
59+
}
60+
}
61+
)
62+
63+
return response?.choices?.[0]?.message?.content
64+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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

Comments
 (0)