Skip to content

Commit f109075

Browse files
committed
Refactor AI completion module: enhance model handling, improve context analysis, and streamline prompt generation
1 parent 4703226 commit f109075

21 files changed

Lines changed: 147 additions & 481 deletions

File tree

packages/plugins/robot/src/components/header-extension/robot-setting/RobotSetting.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ const syncModelSelection = () => {
246246
)
247247
}
248248
249-
const notifyMissingApiKey = (service: any) => {
249+
const notifyMissingApiKey = (service: ModelService) => {
250250
useNotify({
251251
type: 'warning',
252252
title: '未配置API Key',
@@ -364,7 +364,7 @@ const addService = () => {
364364
state.showServiceDialog = true
365365
}
366366
367-
const editService = (service: ModelService) => {
367+
const editService = (service: any) => {
368368
state.editingService = JSON.parse(JSON.stringify(service))
369369
state.showServiceDialog = true
370370
}

packages/plugins/robot/src/composables/core/useConfig.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,22 @@ const getFallbackCompletionModel = (services: ModelService[], preferredServiceId
145145
}
146146
}
147147

148+
const resolveCompletionModelSelection = (services: ModelService[], serviceId = '', modelName = '') => {
149+
if (serviceId && modelName) {
150+
const selectedService = services.find((service) => service.id === serviceId)
151+
const selectedModel = selectedService?.models.find((model) => model.name === modelName)
152+
153+
if (isCompletionCapableModel(selectedService, selectedModel)) {
154+
return {
155+
serviceId,
156+
modelName
157+
}
158+
}
159+
}
160+
161+
return getFallbackCompletionModel(services, serviceId)
162+
}
163+
148164
const initBuiltInServices = (): ModelService[] => {
149165
return getAIModelOptions().map((service: any) => ({
150166
id: service.provider,
@@ -229,19 +245,21 @@ const migrateOldSettings = (oldSettings: any): RobotSettings | null => {
229245
services.push(customService)
230246
}
231247

232-
// 确定默认模型和快速模型
248+
// 确定默认模型、快速模型和代码补全模型
233249
const selectedModel = activeName === 'existingModels' ? existModel : customizeModel
234250
const defaultServiceId =
235251
activeName === 'existingModels' ? services.find((s) => s.baseUrl === selectedModel?.baseUrl)?.id : ''
252+
const legacyCompleteModelName = selectedModel?.completeModel || ''
236253

237254
const quickModel = {
238255
serviceId: defaultServiceId || '',
239-
modelName: selectedModel?.completeModel || ''
256+
modelName: legacyCompleteModelName
240257
}
241-
const completionModel =
242-
quickModel.modelName && quickModel.serviceId
243-
? getFallbackCompletionModel(services, quickModel.serviceId)
244-
: getFallbackCompletionModel(services, defaultServiceId || services[0]?.id || '')
258+
const completionModel = resolveCompletionModelSelection(
259+
services,
260+
defaultServiceId || services[0]?.id || '',
261+
legacyCompleteModelName
262+
)
245263

246264
return {
247265
version: SETTING_VERSION,
@@ -546,13 +564,6 @@ const getSelectedQuickModelInfo = (): SelectedModelInfo => {
546564
(m) => m.name === robotSettingState.quickModel.modelName
547565
)
548566
const { name = '', label = '', capabilities = {} } = currentModel || {}
549-
const completionProtocol =
550-
inferCompletionProtocol({
551-
provider: currentService?.provider,
552-
baseUrl: currentService?.baseUrl,
553-
modelName: name,
554-
capabilities
555-
}) || null
556567

557568
const { models, ...service } = currentService ?? ({} as Partial<ModelService>)
558569

@@ -567,7 +578,6 @@ const getSelectedQuickModelInfo = (): SelectedModelInfo => {
567578
// 模型兼容字段
568579
model: robotSettingState.quickModel.modelName,
569580
completeModel: robotSettingState.quickModel.modelName || '',
570-
completionProtocol,
571581
// 服务兼容字段
572582
baseUrl: currentService?.baseUrl || '',
573583
apiKey: currentService?.apiKey || ''

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ export const DEFAULT_LLM_MODELS = [
9494
name: 'qwen-coder-turbo-latest',
9595
capabilities: {
9696
toolCalling: true,
97-
compact: true,
9897
jsonOutput: bailianJsonOutputExtraBody,
9998
completionProtocol: 'qwen'
10099
}
@@ -122,7 +121,6 @@ export const DEFAULT_LLM_MODELS = [
122121
name: 'deepseek-chat',
123122
capabilities: {
124123
toolCalling: true,
125-
compact: true,
126124
completionProtocol: 'deepseek',
127125
reasoning: {
128126
extraBody: {

packages/plugins/script/src/Main.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,9 @@ export default {
122122
// 保留原有的 ESLint
123123
state.linterWorker = initLinter(editor, monacoRef.value.getMonaco(), state) as any
124124
125-
const { aiCompletionEnabled, aiCompletionTrigger = 'onIdle' } =
126-
getMergeMeta('engine.plugins.pagecontroller')?.options || {}
125+
const pageControllerOptions = getMergeMeta('engine.plugins.pagecontroller')?.options || {}
126+
const aiCompletionEnabled = pageControllerOptions.aiCompletionEnabled ?? pageControllerOptions.enableAICompletion
127+
const aiCompletionTrigger = pageControllerOptions.aiCompletionTrigger || 'onIdle'
127128
128129
if (aiCompletionEnabled) {
129130
try {

packages/plugins/script/src/ai-completion/adapters/deepseekAdapter.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
import { HTTP_CONFIG, ERROR_MESSAGES, DEEPSEEK_CONFIG } from '../constants.js'
22

3+
export function buildDeepSeekCompletionsUrl(baseUrl) {
4+
const normalizedBaseUrl = String(baseUrl || '').trim()
5+
6+
if (!normalizedBaseUrl) {
7+
return ''
8+
}
9+
10+
const normalizePath = (path = '') => {
11+
const trimmedPath = path.replace(/\/+$/, '')
12+
13+
if (trimmedPath.endsWith('/beta/completions')) {
14+
return trimmedPath
15+
}
16+
17+
if (trimmedPath.endsWith('/beta')) {
18+
return `${trimmedPath}/completions`
19+
}
20+
21+
if (/\/v1$/i.test(trimmedPath)) {
22+
return trimmedPath.replace(/\/v1$/i, '/beta/completions')
23+
}
24+
25+
return `${trimmedPath}/beta/completions`
26+
}
27+
28+
try {
29+
const parsedUrl = new URL(normalizedBaseUrl)
30+
parsedUrl.pathname = normalizePath(parsedUrl.pathname)
31+
return parsedUrl.toString()
32+
} catch {
33+
return normalizePath(normalizedBaseUrl)
34+
}
35+
}
36+
337
/**
438
* 构建 DeepSeek FIM 格式的请求参数
539
* @param {string} fileContent - 文件内容(包含 [CURSOR] 标记)
@@ -27,8 +61,7 @@ export function buildDeepSeekFIMParams(fileContent, fimBuilder, metadata = {}) {
2761
* @returns {Promise<string>} 补全文本
2862
*/
2963
export async function callDeepSeekAPI(prompt, suffix, config, apiKey, baseUrl) {
30-
// 构建 DeepSeek FIM API URL:将 /v1 替换为 /beta/completions
31-
const completionsUrl = baseUrl.replace(DEEPSEEK_CONFIG.PATH_REPLACE, DEEPSEEK_CONFIG.COMPLETION_PATH) + '/completions'
64+
const completionsUrl = buildDeepSeekCompletionsUrl(baseUrl)
3265

3366
const requestBody = {
3467
model: config.model,

packages/plugins/script/src/ai-completion/adapters/index.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createSmartPrompt } from '../builders/promptBuilder.js'
33
import { FIMPromptBuilder } from '../builders/fimPromptBuilder.js'
44
import { detectModelType, calculateTokens, getStopSequences } from '../utils/modelUtils.js'
55
import { cleanCompletion, buildLowcodeMetadata } from '../utils/completionUtils.js'
6-
import { buildQwenMessages, callQwenAPI } from './qwenAdapter.js'
6+
import { buildQwenFIMPrompt, callQwenAPI } from './qwenAdapter.js'
77
import { buildDeepSeekFIMParams, callDeepSeekAPI } from './deepseekAdapter.js'
88
import { QWEN_CONFIG, DEEPSEEK_CONFIG, DEFAULTS, ERROR_MESSAGES, MODEL_CONFIG } from '../constants.js'
99

@@ -56,7 +56,6 @@ export function createCompletionHandler() {
5656
textAfterCursor,
5757
language,
5858
filename,
59-
technologies: DEFAULTS.TECHNOLOGIES,
6059
lowcodeMetadata
6160
})
6261

@@ -87,15 +86,15 @@ export function createCompletionHandler() {
8786

8887
if (modelType === MODEL_CONFIG.QWEN.TYPE) {
8988
// ===== Qwen 流程 =====
90-
const { messages, cursorContext: ctx } = buildQwenMessages(fileContent, qwenFimBuilder, fimMetadata)
89+
const { prompt, cursorContext: ctx } = buildQwenFIMPrompt(fileContent, qwenFimBuilder, fimMetadata)
9190
cursorContext = ctx
9291

9392
completionText = await callQwenAPI(
94-
messages,
93+
prompt,
9594
{
9695
model: completeModel,
9796
maxTokens: calculateTokens(ctx),
98-
stopSequences: getStopSequences(ctx, modelType)
97+
stopSequences: getStopSequences(ctx)
9998
},
10099
apiKey,
101100
baseUrl
@@ -115,7 +114,7 @@ export function createCompletionHandler() {
115114
{
116115
model: completeModel,
117116
maxTokens: calculateTokens(ctx),
118-
stopSequences: getStopSequences(ctx, modelType)
117+
stopSequences: getStopSequences(ctx)
119118
},
120119
apiKey,
121120
baseUrl
@@ -124,7 +123,7 @@ export function createCompletionHandler() {
124123

125124
// 7. 处理补全结果
126125
if (completionText) {
127-
completionText = cleanCompletion(completionText, modelType, cursorContext, textAfterCursor)
126+
completionText = cleanCompletion(completionText, cursorContext, textAfterCursor)
128127

129128
if (!completionText) {
130129
return {

packages/plugins/script/src/ai-completion/adapters/qwenAdapter.js

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,36 @@
11
import { QWEN_CONFIG, HTTP_CONFIG, ERROR_MESSAGES } from '../constants.js'
22

33
/**
4-
* 构建 Qwen FIM 格式的 messages
4+
* 构建 Qwen FIM prompt
55
* @param {string} fileContent - 文件内容(包含 [CURSOR] 标记)
66
* @param {Object} fimBuilder - FIM 构建器实例
7-
* @param {Object} metadata - 元数据(language, lowcodeMetadata 等)
8-
* @returns {{ messages: Array, cursorContext: Object }} Messages 和上下文
7+
* @param {Object} metadata - 元数据
8+
* @returns {{ prompt: string, cursorContext: Object }} Prompt 和上下文
99
*/
10-
export function buildQwenMessages(fileContent, fimBuilder, metadata = {}) {
10+
export function buildQwenFIMPrompt(fileContent, fimBuilder, metadata = {}) {
1111
const { fimPrompt, cursorContext } = fimBuilder.buildOptimizedFIMPrompt(fileContent, metadata)
1212

1313
return {
14-
messages: [
15-
{
16-
role: 'user',
17-
content: fimPrompt
18-
}
19-
],
14+
prompt: fimPrompt,
2015
cursorContext
2116
}
2217
}
2318

2419
/**
2520
* 调用 Qwen Completions API
26-
* @param {Array} messages - Messages 数组
21+
* @param {string} prompt - FIM prompt
2722
* @param {Object} config - 配置对象
2823
* @param {string} apiKey - API 密钥
2924
* @param {string} baseUrl - 基础 URL
3025
* @returns {Promise<string>} 补全文本
3126
*/
32-
export async function callQwenAPI(messages, config, apiKey, baseUrl) {
27+
export async function callQwenAPI(prompt, config, apiKey, baseUrl) {
3328
// 构建完整的 Completions API URL
3429
const completionsUrl = `${baseUrl}${QWEN_CONFIG.COMPLETION_PATH}`
3530

3631
const requestBody = {
3732
model: config.model,
38-
prompt: messages[0].content, // FIM prompt
33+
prompt,
3934
max_tokens: config.maxTokens,
4035
temperature: QWEN_CONFIG.DEFAULT_TEMPERATURE,
4136
top_p: QWEN_CONFIG.TOP_P,

packages/plugins/script/src/ai-completion/builders/fimPromptBuilder.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class FIMPromptBuilder {
3232
return {
3333
prefix: fileContent,
3434
suffix: '',
35-
cursorContext: { type: 'unknown', hasPrefix: true, hasSuffix: false }
35+
cursorContext: { type: 'unknown' }
3636
}
3737
}
3838

@@ -118,18 +118,14 @@ export class FIMPromptBuilder {
118118
/**
119119
* 分析光标上下文
120120
* @param {string} prefix - 前缀代码
121-
* @param {string} suffix - 后缀代码
122121
* @returns {Object} 上下文信息
123122
*/
124-
analyzeCursorContext(prefix, suffix) {
123+
analyzeCursorContext(prefix) {
125124
const context = {
126125
type: 'unknown',
127-
hasPrefix: prefix.trim().length > 0,
128-
hasSuffix: suffix.trim().length > 0,
129126
inFunction: false,
130127
inClass: false,
131128
inObject: false,
132-
inArray: false,
133129
inBlockComment: false,
134130
inLineComment: false,
135131
needsExpression: false,

packages/plugins/script/src/ai-completion/builders/index.js

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)