diff --git a/src/vs/editor/contrib/leap/browser/LeapInterfaces.ts b/src/vs/editor/contrib/leap/browser/LeapInterfaces.ts index 9200ccff39bbc..3129fb80c80af 100644 --- a/src/vs/editor/contrib/leap/browser/LeapInterfaces.ts +++ b/src/vs/editor/contrib/leap/browser/LeapInterfaces.ts @@ -6,13 +6,14 @@ import { ALogger, StudyGroup } from "../../rtv/browser/RTVInterfaces"; // remote and local versions. export interface OpenAIRequest { 'model': string; - 'messages': OpenAIMessage[]; + 'messages'?: OpenAIMessage[]; + 'prompt': string | null; 'suffix'?: string | null; 'max_tokens'?: number | null; 'temperature'?: number | null; 'top_p'?: number | null; 'n'?: number | null; - 'stream'?: boolean | null; + 'stream': true; 'logprobs'?: number | null; 'echo'?: boolean | null; 'stop'?: string | string[]; @@ -80,6 +81,62 @@ export abstract class ALeapUtils implements ILeapUtils { } return chatText; } + + cleanUpCompletions(request: OpenAIRequest, codes: string[]): string[] { + const prompt = request.prompt; + if (prompt !== null && prompt.length > 1) { + // The new `instruct` model *tends* to start with '\n' + indentation + // so we manually remove that here if it matches the end of the prompt + for (const i in codes) { + let completion = codes[i]; + if (completion.startsWith('\n') && + (prompt.endsWith(' ') || prompt.endsWith('\t'))) { + // Check that the prompt and completion use the same indentation + const indent_char: string = prompt.at(prompt.length - 1)!; + if (completion.at(1) !== indent_char) { + console.warn('Prompt and completion use different indentation characters. Skipping cleanup.'); + continue; + } + + completion = completion.substring(1); + + // Find the prompt indent level + let prompt_indent = 0; + for (let j = prompt.length - 1; j >= 0; j--) { + if (prompt.at(j) !== indent_char) { + prompt_indent = prompt.length - j - 1; + break; + } + } + + // Remove that many indents from the start of the completion + // First check that this is safe + let safe = true; + for (let j = 0; j < prompt_indent; j++) { + if (!completion.startsWith(indent_char)) { + safe = false; + break; + } + } + + if (!safe) { + console.warn('Completion did not have at least the same amount of indentation as the prompt. Skipping cleanup.'); + continue; + } + + // We already removed the newline char earlier. + const new_completion = completion.substring(prompt_indent); + console.log('Cleaned up completion from:\n', codes[i], '\nTo:\n', new_completion); + codes[i] = new_completion; + } else { + console.debug('Completion did not start with newline and indentation. Skipping cleanup.'); + continue; + } + } + } + + return codes; + } } export class LeapConfig { diff --git a/src/vs/editor/contrib/leap/browser/LeapUtils.ts b/src/vs/editor/contrib/leap/browser/LeapUtils.ts index 4c199f2de56aa..ef861e07bf398 100644 --- a/src/vs/editor/contrib/leap/browser/LeapUtils.ts +++ b/src/vs/editor/contrib/leap/browser/LeapUtils.ts @@ -5,20 +5,19 @@ import * as path from 'path'; import { ALeapLogger, ILeapLogger, LeapConfig, ILeapUtils, ALeapUtils, OpenAIMessage, OpenAIRequest } from 'vs/editor/contrib/leap/browser/LeapInterfaces'; import { StudyGroup } from '../../rtv/browser/RTVInterfaces'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { getOSEnvVariable } from '../../rtv/browser/RTVUtils'; -const LEAP_PROMPT = getOSEnvVariable('LEAP_PROMPT'); class LocalUtils extends ALeapUtils { public readonly EOL: string = os.EOL; private _openAi: OpenAI; - private _requestTemplate = { - model: "gpt-3.5-turbo-1106", + private _requestTemplate: OpenAIRequest = { + model: "gpt-3.5-turbo-instruct", temperature: 0.5, n: 5, max_tokens: 512, stop: [this.EOL + this.EOL], stream: true, + prompt: null, }; constructor() { @@ -36,20 +35,18 @@ class LocalUtils extends ALeapUtils { } async getCompletions(request: OpenAIRequest, signal: AbortSignal, progressCallback: (e: any) => void): Promise { - // @ts-ignore - const completionArgs: OpenAI.ChatCompletionCreateParamsStreaming = request; - const completions = await this._openAi.chat.completions.create(completionArgs); - + const completions = await this._openAi.completions.create(request); signal.onabort = ((_) => { completions.controller.abort(); }); + const codes = Array.from({ length: (request.n || 1) }, () => ""); for await (const part of completions) { const i = part.choices[0].index; - const delta = part.choices[0].delta.content ?? ''; + const delta = part.choices[0].text ?? ''; codes[i] += delta; progressCallback(part); } - return codes; + return this.cleanUpCompletions(request, codes); } getLogger(editor: ICodeEditor): ILeapLogger { @@ -57,12 +54,11 @@ class LocalUtils extends ALeapUtils { } async buildRequest(prefix: string, suffix: string): Promise { - const messages = this.parsePromptFile(LEAP_PROMPT, { prefix, suffix }); - const rs = { + return { ...this._requestTemplate, - messages, + prompt: prefix, + suffix: suffix }; - return rs; } parsePromptFile(filename: string, substitutions: { [key: string]: string; }): OpenAIMessage[] { diff --git a/src/vs/editor/contrib/leap/browser/remote/LeapUtils.ts b/src/vs/editor/contrib/leap/browser/remote/LeapUtils.ts index 61e43004d8248..fe94264d1f892 100644 --- a/src/vs/editor/contrib/leap/browser/remote/LeapUtils.ts +++ b/src/vs/editor/contrib/leap/browser/remote/LeapUtils.ts @@ -1,24 +1,22 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { LeapConfig, ILeapUtils, ALeapUtils, OpenAIRequest, ALeapLogger, ILeapLogger, OpenAIMessage } from 'vs/editor/contrib/leap/browser/LeapInterfaces'; +import { LeapConfig, ILeapUtils, ALeapUtils, OpenAIRequest, ALeapLogger, ILeapLogger } from 'vs/editor/contrib/leap/browser/LeapInterfaces'; import { LogEventData, LogResultData } from 'vs/editor/contrib/rtv/browser/RTVInterfaces'; class RemoteUtils extends ALeapUtils { public readonly EOL: string = '\n'; - private _requestTemplate = { - model: "gpt-3.5-turbo", + private _requestTemplate: OpenAIRequest = { + model: "gpt-3.5-turbo-instruct", temperature: 0.5, n: 5, max_tokens: 512, stop: [this.EOL + this.EOL], stream: true, + prompt: null, }; - private _promptTemplate = ""; - constructor() { super(); this.fetchRequestTemplate(); - this.fetchPromptTemplate(); } async getConfig(): Promise { @@ -31,7 +29,7 @@ class RemoteUtils extends ALeapUtils { } ); const body: LeapConfig = await rq.json(); - console.log('Recevied LEAP config from server:', body); + console.log('Received LEAP config from server:', body); return body; } @@ -63,7 +61,7 @@ class RemoteUtils extends ALeapUtils { } reader.cancel(); - console.log('Recevied completions from server:\n', jsonStr); + console.log('Received completions from server:\n', jsonStr); // Now that we have the entries, we need to parse them. const codes = Array.from({ length: (request.n || 1) }, () => ""); @@ -82,7 +80,7 @@ class RemoteUtils extends ALeapUtils { console.log("Trying to parse as JSON:\n", entry); const part = JSON.parse(entry); const i = part.choices[0].index; - const delta = part.choices[0].delta.content ?? ''; + const delta = part.choices[0].text ?? ''; codes[i] += delta; } catch (e) { console.error("Failed to parse entry. Skipping:\n", entry); @@ -90,19 +88,18 @@ class RemoteUtils extends ALeapUtils { } } - return codes; + return this.cleanUpCompletions(request, codes); } getLogger(_editor: ICodeEditor): ILeapLogger { return new LeapLogger(); } - // TODO: use the correct path for vscode/src/prompts/implement_it.txt async buildRequest(prefix: string, suffix: string): Promise { - const messages = this.parsePromptFile({ prefix, suffix }); return { ...this._requestTemplate, - messages, + prompt: prefix, + suffix: suffix }; } @@ -116,29 +113,9 @@ class RemoteUtils extends ALeapUtils { } ); const body: OpenAIRequest = await res.json(); - // @ts-ignore this._requestTemplate = body; return body; } - - private async fetchPromptTemplate(): Promise { - const queries = window.location.search; - const res = await fetch( - `/promptTemplate${queries}`, - { - method: 'GET', - mode: 'same-origin' - } - ); - const body: string = await res.text(); - // @ts-ignore - this._promptTemplate = body; - return body; - } - - parsePromptFile(substitutions: { [key: string]: string; }): OpenAIMessage[] { - return this.createPromptFromTemplate(this._promptTemplate, substitutions); - } } export function getUtils(): ILeapUtils {