当前位置:   article > 正文

vscode插件开发笔记——大模型应用之AI编程助手_vscode大模型插件

vscode大模型插件

系列文章目录


前言

最近在开发vscode插件相关的项目,网上很少有关于大模型作为AI 编程助手这方面的教程。因此,借此机会把最近写的几个demo分享记录一下。

一、代码补全

思路:

  1. 读取vscode插件上鼠标光标的上下文信息。
  2. 将提示词和上下文代码作为输入,通过modelfusion调用大模型得到输出。
  3. 将输出内容插入到光标所在位置,通过Declaration在vscode页面进行显示。
  4. 设置快捷键,如果代码补全正确,按下快捷键后自动插入代码。反之光标移动,代码自动消失。

下面直接上代码。
extension.ts 文件:

import * as vscode from 'vscode';
import {
    BaseUrlApiConfiguration,
    openaicompatible,
	streamText,
  } from 'modelfusion';
import{ previewInsertion, getBeforeText, getAfterText }from '../src/main'

export function activate(context: vscode.ExtensionContext) {

	// Use the console to output diagnostic information (console.log) and errors (console.error)
	// This line of code will only be executed once when your extension is activated
	console.log('Congratulations, your extension "dytest" is now active!');

   let globalText:string|undefined ; 
   let globalindex: vscode.Position | undefined;
	// 如何删除字符
	const editor = vscode.window.activeTextEditor;


	// 代码补全命令
	const disposable = vscode.commands.registerCommand('dytest.helloWorld', async () => {
	if (!editor) {
		return;
	}
	// 代码所在位置
	const code =editor.document.getText();

	//光标所在位置
	const offset =editor.document.offsetAt(editor.selection.active);
	const index = editor.document.positionAt(offset);
	
	
	const position = editor.selection.active;
	const document = editor.document;

	const beforeText = getBeforeText(document, position);
	const afterText = getAfterText(document, position);

	// 调用大模型
	const code_prompt='提示词';
	const code_instruction='{{{prefix}}}[BLANK]{{{suffix}}}';
	const code_instruction_2=code_instruction.replace("{{{prefix}}}", beforeText).replace("{{{suffix}}}",afterText)
	 console.log(code_instruction_2)
const text2 = await streamText({
    model: openaicompatible
      .ChatTextGenerator({
        // 这里使用的是自己部署的大模型,url和openai的一样。但是模型不是用的openai系列的。如果要改,可以换成openai的。
        api: new BaseUrlApiConfiguration({
          baseUrl: "模型的url",
          headers: {
            Authorization: `模型的api`,
          },
        }),
        provider: "openaicompatible-fireworksai", // optional, only needed for logging and custom configs
        model: "自己的模型",
      })
      .withInstructionPrompt(),
      prompt: {
                system:
                code_prompt,
                instruction:
                code_instruction_2
              },
  });
  
 
  let responseUntilNow = "";
  for await (const textPart of text2) {
    // process.stdout.write(textPart);
    responseUntilNow += textPart;
  }
  console.log(responseUntilNow)
	  // 进行代码补全的提示
	  const previewinsertion =previewInsertion(editor,index,responseUntilNow);
	  globalText=responseUntilNow;
	  globalindex=index

		vscode.window.showInformationMessage('Hello World from dy_test!');
	});

	
	
	context.subscriptions.push(disposable);

	const disposable2 = vscode.commands.registerCommand('extension.myCommand', async () => {

		editor?.edit((editBuilder => {
			if (!globalindex || !globalText){
				return;
			}
			const lines = globalText.split(/\r\n|\n/); // 分割文本
			editBuilder.insert(globalindex, lines[0])
			globalindex=undefined;
			globalText=undefined;
		}));
		vscode.window.showInformationMessage('Hello World from myextension!');
	  });
	
	  let timeout: NodeJS.Timeout | undefined;
	  context.subscriptions.push(disposable2);

	  // 注册事件监听器以在特定条件下调用命令
	  context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => {
		if (editor) {
            // 延迟调用命令
            if (timeout) {
                clearTimeout(timeout);
            }
            timeout = setTimeout(() => {
                vscode.commands.executeCommand('dytest.helloWorld');
            }, 200); // 延迟
        }
    }));

    context.subscriptions.push(vscode.window.onDidChangeTextEditorSelection(event => {
		if (timeout) {
            clearTimeout(timeout);
        }
        timeout = setTimeout(() => {
            vscode.commands.executeCommand('dytest.helloWorld');
        }, 200); // 延迟
    }));
}
export function deactivate() {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125

main.ts文件:


import {parse}from '@babel/parser'
import traverse from '@babel/traverse'
import * as vscode from 'vscode';

// 代码补全插入
export function previewInsertion(editor: vscode.TextEditor, position: vscode.Position, text: string, ) {
  const range = new vscode.Range(position, position.translate({characterDelta: text.length}));
  let currentDecoration: vscode.DecorationOptions[] | undefined = undefined;
  let decorationType: vscode.TextEditorDecorationType | undefined = undefined;
 

const lines = text.split(/\r\n|\n/); // 分割文本
let lineCount = 0;
let totalCharacterCount = 0;

// 计算总行数和总字符数
for (const line of lines) {
    lineCount++;
    totalCharacterCount += line.length;
}
totalCharacterCount--;
console.log(lineCount); // 输出非空行数


if (!decorationType) {
    decorationType = vscode.window.createTextEditorDecorationType({
     light: { // Light theme settings
         after: {
             contentText: lines[0],
             fontStyle: 'italic',
             color: '#7f8c8d', // 灰色
             backgroundColor: 'transparent',
            //  textDecoration: 'none',
              margin: '0 0 0 0',
         },
     },
     dark: { // Dark theme settings
         after: {
             contentText: lines[0],
             fontStyle: 'italic',
             color: '#95a5a6', // 灰色,适合深色主题
             backgroundColor: 'transparent',
            //  textDecoration: 'none',
             margin: '0 0 0 0',
         }, 
 }
});
}

const endPosition=position.translate({lineDelta:lineCount-1,characterDelta:totalCharacterCount })
console.log("position:",position)
console.log("endPosition:",endPosition)
 // 创建装饰器选项
 
 currentDecoration = [{
    range: new vscode.Range(position, position),
    hoverMessage: new vscode.MarkdownString('按“tab”键确认'),
}];


// 应用装饰器
editor.setDecorations(decorationType, currentDecoration);



// 监听鼠标移动事件,移除装饰
const mouseMoveDisposable = vscode.window.onDidChangeTextEditorSelection(
        (event) => {
            if (event.textEditor === editor) {
                editor.setDecorations(decorationType, []);
                currentDecoration = undefined;
              
            }
        },
        null,
    );


  

   
}
  // 获取文本上下文信息
export function getBeforeText(document: vscode.TextDocument, position: vscode.Position): string {
    const range = new vscode.Range(new vscode.Position(0, 0), position);
    return document.getText(range);
}
export function getAfterText(document: vscode.TextDocument, position: vscode.Position): string {
    const lastLine = document.lineCount - 1;
    const lastLineLength = document.lineAt(lastLine).text.length;
    const range = new vscode.Range(position, new vscode.Position(lastLine, lastLineLength));
    return document.getText(range);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

package.json

添加两个命令

"commands": [
  {
    "command": "dytest.helloWorld",
    "title": "Hello World"
  },
  {  "command": "extension.myCommand",
     "title": "My Command"
      }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

快捷键

   "keybindings": [
    {
      "command": "extension.myCommand",
      "key": "ctrl+shift",
      "when": "editorTextFocus"
    }
  ]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

实现效果:

代码补全(2)

目前遇到的问题:函数级代码生成后,通过Decoration装饰器无法显示全部的。希望有大佬指点一下。

二、LLM+搜索引擎

  1. 访问搜索引擎,该方式如果网页存在反爬机制会造成爬取失败。
async function baiduSearch(query, page = 1) {
    const start = (page - 1) * 10;
    const encodedQuery = encodeURIComponent(query);
    const url = `https://www.abd.com/s?wd=${encodedQuery}&pn=${start}`;

    try {
        const response = await axios.get(url);
        if (response.status === 200) {
            // 假设你想解析返回的HTML并获取搜索结果
            const html = response.data;
            console.log(html)
            // 这里你可以使用cheerio或类似的库来解析HTML
            // 并从中抽取你需要的信息
        } else {
            console.error(`Request failed with status ${response.status}`);
        }
    } catch (error) {
        console.error(error);
    }
}

// 使用函数
baiduSearch('阿里巴巴');

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  1. 调用AI 引擎进行LLM
import Exa from "exa-js";

// 根据输入的关键词到搜索引擎中进行查询,然后通过LLM得到所需要的信息。
async function ExaSearch(topic=null,numresults=null,categorys=null,includehtmltag=null,maxcharacters=1024,startpublisheddate=null,endpublisheddate=null,startcrawldate=null,endcrawldate=null) {
const exa = new Exa("null");
const result = await exa.searchAndContents(
    topic,
  {
    type: "neural",
    useAutoprompt: true,
    numResults: numresults,
    text: {
      includeHtmlTags: includehtmltag,
      maxCharacters: maxcharacters
    },
    startPublishedDate:startpublisheddate,
    endPublishedDate: endpublisheddate,
    startCrawlDate: startcrawldate,
    endCrawlDate: endcrawldate,
    category: categorys
  }
)
console.log(result);
const text= JSON.stringify(extractAllValues(result));// 解析网页返回的json格式中的所有的文本内容。
console.log(text);
const model=直接使用modelfusion中的模型进行调用。

const prompt =null;
prompt.replace('${topic}', topic)


const engeernering = await generateText({   
    model,
    prompt: {
      system: prompt, // optional
      instruction: text,
    },
   });
  // console.log(test)
  console.log("engeernering: ",engeernering);


 // extract the topic from the text:
 return engeernering
  }
const startPublishedDate="2024-01-01T06:31:37.881Z";
const endPublishedDate= "2024-07-29T06:31:37.881Z";
const startCrawlDate="2024-01-01T06:31:37.881Z";
const endCrawlDate= "2024-07-29T06:31:37.881Z";
const query=  "找一下agent相关的文章。"
const categorys='research paper'
ExaSearch(query,2,categorys,null,1024,startPublishedDate,endPublishedDate,startCrawlDate,endCrawlDate)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/920042
推荐阅读
相关标签
  

闽ICP备14008679号