Haben Sie jemals gedacht, dass die Entwicklung eines Code-Vervollständigungstools wie GitHub Copilot komplex wäre? Überraschenderweise ist es gar nicht so schwer, wie es scheint!
Als Ingenieur war ich schon immer fasziniert davon, wie Code-Vervollständigungstools unter der Haube funktionieren. Also habe ich den Prozess rückentwickelt, um zu sehen, ob ich selbst eines bauen könnte.
Hier ist einer, den ich selbst erstellt und veröffentlicht habe – LLM-Autocompleter
Da KI-gestützte Tools in der Softwareentwicklung zur Norm werden, ist die Erstellung eines eigenen Code-Completion-Tools eine großartige Möglichkeit, mehr über Language Server Protocol (LSP), APIs und die Integration mit erweiterten Modellen wie OpenAIs GPT zu erfahren. Außerdem ist es ein unglaublich lohnendes Projekt.
Code-Vervollständigungstools kombinieren im Wesentlichen einen Language Server Protocol (LSP)-Server mit Inline-Code-Vervollständigungsmechanismen von Plattformen wie VS Code. In diesem Tutorial nutzen wir die Inline-Vervollständigungs-API von VS Code und erstellen unseren eigenen LSP-Server.
Bevor wir eintauchen, wollen wir verstehen, was ein LSP-Server ist.
Ein LSP-Server ist ein Backend-Dienst, der sprachspezifische Funktionen für Texteditoren oder integrierte Entwicklungsumgebungen (IDEs) bereitstellt. Es fungiert als Brücke zwischen dem Editor (dem Client) und den sprachspezifischen Tools und bietet Funktionen wie:
Code-Vervollständigung (Vorschlagen von Codeausschnitten während der Eingabe),
Gehe zu Definition (Navigieren zu dem Teil des Codes, in dem ein Symbol definiert ist),
Fehlerprüfung (Hervorhebung von Syntaxfehlern in Echtzeit).
Die Idee hinter dem Language Server Protocol (LSP) besteht darin, das Protokoll für die Kommunikation solcher Server und Entwicklungstools zu standardisieren. Auf diese Weise kann ein einzelner Sprachserver in mehreren Entwicklungstools wiederverwendet werden und LSP ist nur ein Protokoll.
Durch die Standardisierung der Kommunikation dieser Server mit Editoren über LSP können Entwickler sprachspezifische Funktionen erstellen, die nahtlos auf einer Vielzahl von Plattformen funktionieren, wie VS Code, Sublime Text und sogar Vim.
Da Sie nun die Grundlagen von LSP verstanden haben, beginnen wir Schritt für Schritt mit der Entwicklung unseres eigenen Code-Vervollständigungstools.
Wir beginnen mit der Verwendung einer Beispiel-Inline-Vervollständigungserweiterung, die von VS Code bereitgestellt wird. Sie können es direkt von GitHub klonen:
vscode-sample-inlinecompletion
Jetzt richten wir den LSP-Server ein. Sie können der folgenden Struktur folgen
. ├── client // Language Client │ ├── src │ │ ├── test // End to End tests for Language Client / Server │ │ └── extension.ts // Language Client entry point ├── package.json // The extension manifest. └── server // Language Server └── src └── server.ts // Language Server entry point
Weitere Informationen finden Sie auch unter lsp-sample
Ich würde Ihnen ein paar Code-Teile geben. Sie müssen Dinge zusammenfügen, die Sie lernen sollen. Das Bild unten zeigt, was wir bauen werden.
Lass uns zu client/src/extension.ts gehen und alles aus der Aktivierungsfunktion entfernen
export function activate(context: ExtensionContext) { }
Lass uns mit der Einrichtung beginnen
extension.ts
export function activate(context: ExtensionContext) { const serverModule = context.asAbsolutePath( path.join("server", "out", "server.js") ); const debugOptions = { execArgv: ['--nolazy', '-- inspect=6009'] }; // communication with the server using Stdio const serverOptions: ServerOptions = { run: { module: serverModule, transport: TransportKind.stdio, }, debug: { module: serverModule, transport: TransportKind.stdio, options: debugOptions } }; const clientOptions: LanguageClientOptions = { documentSelector: [{ scheme: 'file' }], initializationOptions: serverConfiguration }; client = new LanguageClient( 'LSP Server Name', serverOptions, clientOptions ); client.start(); }
Einige Informationen
Wir haben verschiedene Arten von Protokollen, denen wir folgen können, um zwischen Server und Client zu kommunizieren.
Weitere Informationen finden Sie unter microsoft-lsp-docs
Warum stdio? Stdio ist eines der am weitesten verbreiteten Kommunikationsprotokolle zwischen Clients und Servern. Dadurch kann der LSP-Server, den wir erstellen, nicht nur in VS Code, sondern auch in anderen Editoren wie Vim und Sublime Text funktionieren.
server.ts
const methodStore: Record<string, any> = { exit, initialize, shutdown, }; process.stdin.on("data", async (bufferChuck) => { buffer += bufferChuck; while (true) { try { // Check for the Content-Length line const lengthMatch = buffer.match(/Content-Length: (\d+)\r\n/); if (!lengthMatch) break; const contentLength = parseInt(lengthMatch[1], 10); const messageStart = buffer.indexOf("\r\n\r\n") + 4; // Continue unless the full message is in the buffer if (buffer.length < messageStart + contentLength) break; const rawMessage = buffer.slice(messageStart, messageStart + contentLength); const message = JSON.parse(rawMessage); const method = methodStore[message.method]; if (method) { const result = await method(message); if (result !== undefined) { respond(message.id, result); } } buffer = buffer.slice(messageStart + contentLength); } catch (error: any) { const errorMessage = { jsonrpc: "2.0", method: "window/showMessage", params: { type: 1, // Error type message: `Error processing request: ${error.message}` } }; const errorNotification = JSON.stringify(errorMessage); const errorNotificationLength = Buffer.byteLength(errorNotification, "utf-8"); const errorHeader = `Content-Length: ${errorNotificationLength}\r\n\r\n`; process.stdout.write(errorHeader + errorNotification); } } });
initialize.ts
export const initialize = (message: RequestMessage): InitializeResult => { return { capabilities: { completionProvider: { resolveProvider: true }, textDocumentSync: TextDocumentSyncKind.Incremental, codeActionProvider: { resolveProvider: true } }, serverInfo: { name: "LSP-Server", version: "1.0.0", }, }; };
exit.ts
export const exit = () => { process.exit(0); };
shutdown.ts
export const shutdown = () => { return null; };
Sobald Sie mit der Grundfunktion fertig sind, können Sie den vscode jetzt im Debugging-Modus ausführen, indem Sie die F5-Taste auf der Tastatur verwenden oder der Debugging-Anleitung folgen
Jetzt beginnen wir mit dem Hinzufügen eines Inline-Anbieters und erhalten die entsprechende Anfrage und Antwort
Fügen wir eine neue Methode zum methodStore hinzu
server.ts
const methodStore: Record<string, any> = { exit, initialize, shutdown, "textDocument/generation": generation };
generation.ts
export const generation = async (message: any) => { if(!message && message !== undefined) return {}; const text = message.params.textDocument.text as string; if(!text) return {}; const cursorText = getNewCursorText(text, message.params.position.line, message.params.position.character); const response = await getResponseFromOpenAI(cursorText, message.params.fsPath); return { generatedText: response, } } function getNewCursorText(text: string, line: number, character: number): string { const lines = text.split('\n'); if (line < 0 || line >= lines.length) return text; const targetLine = lines[line]; if (character < 0 || character > targetLine.length) return text; lines[line] = targetLine.slice(0, character) + '<CURSOR>' + targetLine.slice(character); return lines.join('\n'); } const getResponseFromOpenAI = async (text: string, fsPath: stiring): Promise<string> => { const message = { "role": "user", "content": text }; const systemMetaData: Paramaters = { max_token: 128, max_context: 1024, messages: [], fsPath: fsPath } const messages = [systemPrompt(systemMetaData), message] const chatCompletion: OpenAI.Chat.ChatCompletion | undefined = await this.open_ai_client?.chat.completions.create({ messages: messages, model: "gpt-3.5-turbo", max_tokens: systemMetaData?.max_tokens ?? 128, }); if (!chatCompletion) return ""; const generatedResponse = chatCompletion.choices[0].message.content; if (!generatedResponse) return ""; return generatedResponse; }
template.ts
interface Parameters { max_tokens: number; max_context: number; messages: any[]; fsPath: string; } export const systemPrompt = (paramaters: Parameters | null) => { return { "role": "system", "content": ` Instructions: - You are an AI programming assistant. - Given a piece of code with the cursor location marked by <CURSOR>, replace <CURSOR> with the correct code. - First, think step-by-step. - Describe your plan for what to build in pseudocode, written out in great detail. - Then output the code replacing the <CURSOR>. - Ensure that your completion fits within the language context of the provided code snippet. - Ensure, completion is what ever is needed, dont write beyond 1 or 2 line, unless the <CURSOR> is on start of a function, class or any control statment(if, switch, for, while). Rules: - Only respond with code. - Only replace <CURSOR>; do not include any previously written code. - Never include <CURSOR> in your response. - Handle ambiguous cases by providing the most contextually appropriate completion. - Be consistent with your responses. - You should only generate code in the language specified in the META_DATA. - Never mix text with code. - your code should have appropriate spacing. META_DATA: ${paramaters?.fsPath}` }; };
Jetzt registrieren wir die Inline-Anbieter
extension.ts
import {languages} from "vscode"; function getConfiguration(configName: string) { if(Object.keys(workspace.getConfiguration(EXTENSION_ID).get(configName)).length > 0){ return workspace.getConfiguration(EXTENSION_ID).get(configName); } return null; } const inLineCompletionConfig = getConfiguration("inlineCompletionConfiguration"); export function activate(context: ExtensionContext) { // OTHER CODE languages.registerInlineCompletionItemProvider( { pattern: "**" }, { provideInlineCompletionItems: (document: TextDocument, position: Position) => { const mode = inLineCompletionConfig["mode"] || 'slow'; return provideInlineCompletionItems(document, position, mode); }, } ); } let lastInlineCompletion = Date.now(); let lastPosition: Position | null = null; let inlineCompletionRequestCounter = 0; const provideInlineCompletionItems = async (document: TextDocument, position: Position, mode: 'fast' | 'slow') => { const params = { textDocument: { uri: document.uri.toString(), text: document.getText(), }, position: position, fsPath: document.uri.fsPath.toString() }; inlineCompletionRequestCounter += 1; const localInCompletionRequestCounter = inlineCompletionRequestCounter; const timeSinceLastCompletion = (Date.now() - lastInlineCompletion) / 1000; const minInterval = mode === 'fast' ? 0 : 1 / inLineCompletionConfig["maxCompletionsPerSecond"]; if (timeSinceLastCompletion < minInterval) { await new Promise(r => setTimeout(r, (minInterval - timeSinceLastCompletion) * 1000)); } if (inlineCompletionRequestCounter === localInCompletionRequestCounter) { lastInlineCompletion = Date.now(); let cancelRequest = CancellationToken.None; if (lastPosition && position.isAfter(lastPosition)) { cancelRequest = CancellationToken.Cancelled; } lastPosition = position; try { const result = await client.sendRequest("textDocument/generation", params, cancelRequest); const snippetCode = new SnippetString(result["generatedText"]); return [new InlineCompletionItem(snippetCode)]; } catch (error) { console.error("Error during inline completion request", error); client.sendNotification("window/showMessage", { type: 1, // Error type message: "An error occurred during inline completion: " + error.message }); return []; } } else { return []; } };
Dieser Blog bietet die Grundlage, die Sie zum Erstellen Ihres eigenen Code-Vervollständigungstools benötigen, aber die Reise endet hier noch nicht. Ich ermutige Sie, diesen Code zu experimentieren, zu erforschen und zu verbessern und verschiedene Funktionen von LSP und KI zu erkunden, um das Tool an Ihre Bedürfnisse anzupassen.
Wer auch immer versucht, dies umzusetzen, ich möchte, dass er lernt, recherchiert und Dinge zusammenfügt.
Verstehen von LSP-Servern: Sie haben gelernt, was ein LSP-Server ist, wie er sprachspezifische Tools unterstützt und warum er für die editorübergreifende Unterstützung von entscheidender Bedeutung ist.
VS-Code-Erweiterungen erstellen: Sie haben untersucht, wie Sie Code-Vervollständigungen mithilfe von APIs in VS-Code integrieren.
KI-gesteuerte Code-Vervollständigung: Durch die Verbindung mit den GPT-Modellen von OpenAI haben Sie gesehen, wie maschinelles Lernen die Entwicklerproduktivität durch intelligente Vorschläge steigern kann.
Wenn Sie hierher kommen, freue ich mich zu erfahren, was Sie gelernt haben.
Bitte klicken Sie auf „Gefällt mir“, wenn Sie heute etwas Neues aus meinem Blog gelernt haben.
Vernetzen Sie sich mit mir – LinkedIn
Das obige ist der detaillierte Inhalt vonErstellen Sie Ihren eigenen GitHub-Copilot: Eine Schritt-für-Schritt-Anleitung für Tools zur Codevervollständigung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!