Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switched back to the completion API #57

Open
wants to merge 1 commit into
base: leap
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions src/vs/editor/contrib/leap/browser/LeapInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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 {
Expand Down
24 changes: 10 additions & 14 deletions src/vs/editor/contrib/leap/browser/LeapUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -36,33 +35,30 @@ class LocalUtils extends ALeapUtils {
}

async getCompletions(request: OpenAIRequest, signal: AbortSignal, progressCallback: (e: any) => void): Promise<string[]> {
// @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 {
return new LeapLogger(editor);
}

async buildRequest(prefix: string, suffix: string): Promise<OpenAIRequest> {
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[] {
Expand Down
43 changes: 10 additions & 33 deletions src/vs/editor/contrib/leap/browser/remote/LeapUtils.ts
Original file line number Diff line number Diff line change
@@ -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<LeapConfig> {
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) }, () => "");
Expand All @@ -82,27 +80,26 @@ 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);
}
}
}

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<OpenAIRequest> {
const messages = this.parsePromptFile({ prefix, suffix });
return {
...this._requestTemplate,
messages,
prompt: prefix,
suffix: suffix
};
}

Expand All @@ -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<string> {
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 {
Expand Down
Loading