Skip to content

Commit 4e39df7

Browse files
authored
feat(cloud): auto completion (#39)
1 parent 400b824 commit 4e39df7

File tree

7 files changed

+199
-106
lines changed

7 files changed

+199
-106
lines changed

.vscode/launch.json

+7-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
"version": "0.2.0",
77
"configurations": [
88
{
9-
"name": "Run Extension",
9+
"name": "Watch & Launch Extension",
1010
"type": "extensionHost",
1111
"request": "launch",
1212
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
13-
"outFiles": ["${workspaceFolder}/out/**/*.js"],
14-
"preLaunchTask": "${defaultBuildTask}"
13+
"outFiles": ["${workspaceFolder}/dist/*.js"],
14+
"preLaunchTask": "npm: watch",
15+
"debugWebviews": true,
16+
"runtimeExecutable": "${execPath}",
17+
"smartStep": true,
18+
"sourceMaps": true
1519
},
1620
{
1721
"name": "Extension Tests",

.vscode/tasks.json

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
"kind": "build",
1717
"isDefault": true
1818
}
19+
},
20+
{
21+
"type": "npm",
22+
"script": "watch",
23+
"group": {
24+
"kind": "build",
25+
"isDefault": true
26+
},
27+
"isBackground": true
1928
}
2029
]
2130
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { OpenAI } from "@langchain/openai";
2+
import { configuration } from "../utils/configuration";
3+
import Logger from "../logger";
4+
5+
type Parameters = {
6+
temperature: number;
7+
n_predict: number;
8+
controller?: AbortController;
9+
};
10+
11+
export const sendCompletionsRequestCloud = async (
12+
prompt: string,
13+
parameters: Parameters
14+
) => {
15+
const apiKey = configuration.get("cloud.apiToken");
16+
17+
const model = new OpenAI({
18+
maxRetries: 0,
19+
openAIApiKey: apiKey,
20+
configuration: {
21+
baseURL: configuration.get("cloud.endpoint"),
22+
},
23+
stop: ["<|file_separator|>"],
24+
temperature: parameters.temperature,
25+
maxTokens: parameters.n_predict,
26+
});
27+
try {
28+
const response = await model.invoke(prompt, {
29+
configurable: {
30+
signal: parameters.controller,
31+
},
32+
maxConcurrency: 1,
33+
});
34+
35+
return response;
36+
} catch (error) {
37+
Logger.error(error);
38+
}
39+
};

src/common/completion/index.ts

+91-58
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { getPromptCompletion } from "../prompt";
44
import { abortInterval, delay } from "./utils/intervals";
55
import { getAdditionalDocuments } from "./utils/getAdditionalDocuments";
66
import Logger from "../logger";
7-
import { sendCompletionRequest } from "./localCompletion";
7+
import { sendCompletionRequestLocal } from "./localCompletion";
88
import { servers } from "../server";
99
import { configuration } from "../utils/configuration";
1010
import { state } from "../utils/state";
11+
import { sendCompletionsRequestCloud } from "./cloudCompletion";
12+
import statusBar from "../statusBar";
1113

1214
const logCompletion = () => {
1315
const uuid = randomUUID();
@@ -39,10 +41,12 @@ export const getInlineCompletionProvider = (
3941
"inlineSuggestModeAuto"
4042
);
4143

44+
// If the current mode is not auto and the trigger is automatic, don't suggest any completions.
4245
if (currentInlineSuggestModeAuto !== true && triggerAuto === true) {
4346
return [];
4447
}
4548

49+
// If there is a selected completion item, don't suggest any completions.
4650
if (context.selectedCompletionInfo) {
4751
return [];
4852
}
@@ -52,42 +56,14 @@ export const getInlineCompletionProvider = (
5256
loggerCompletion.info("Completion: started");
5357

5458
const cancelled = await delay(250, token);
59+
5560
if (cancelled) {
5661
loggerCompletion.info("Completion: canceled");
5762
return [];
5863
}
5964

6065
const { abortController, requestFinish } = abortInterval(token);
61-
62-
const modelType = triggerAuto
63-
? configuration.get("completion.autoMode")
64-
: configuration.get("completion.manuallyMode");
65-
66-
const serverUrl = servers[modelType].serverUrl;
67-
68-
const additionalDocuments: vscode.TextDocument[] = configuration.get(
69-
"experimental.useopentabs"
70-
)
71-
? await getAdditionalDocuments()
72-
: [];
73-
74-
const prompt = await getPromptCompletion({
75-
activeDocument: document,
76-
additionalDocuments: additionalDocuments,
77-
position: position,
78-
maxTokenExpect: triggerAuto ? maxToken : 2000,
79-
});
80-
81-
const parameters = triggerAuto
82-
? {
83-
n_predict: 128,
84-
stop: ["\n", "<|file_separator|>"],
85-
}
86-
: {
87-
n_predict: 512,
88-
stop: ["<|file_separator|>"],
89-
temperature: 0.5,
90-
};
66+
const { stopTask } = statusBar.startTask();
9167

9268
try {
9369
Logger.info(
@@ -97,49 +73,106 @@ export const getInlineCompletionProvider = (
9773
sendTelemetry: true,
9874
}
9975
);
76+
if (configuration.get("cloud.use")) {
77+
const prompt = await getPromptCompletion({
78+
activeDocument: document,
79+
additionalDocuments: await getAdditionalDocuments(),
80+
position: position,
81+
maxTokenExpect: 3300,
82+
});
83+
const completion = await sendCompletionsRequestCloud(prompt, {
84+
n_predict: 512,
85+
temperature: 0.5,
86+
controller: abortController,
87+
});
10088

101-
const completion = await sendCompletionRequest(
102-
prompt,
103-
parameters,
104-
abortController,
105-
loggerCompletion.uuid(),
106-
serverUrl
107-
);
89+
if (completion === "" || completion === undefined) {
90+
return [];
91+
}
10892

109-
if (completion === null) {
110-
return [];
111-
}
93+
if (token.isCancellationRequested) {
94+
loggerCompletion.info(
95+
"Request: canceled by new completion cancel token"
96+
);
11297

113-
if (token.isCancellationRequested) {
114-
loggerCompletion.info(
115-
"Request: canceled by new completion cancel token"
116-
);
98+
return [];
99+
}
117100

118-
return [];
119-
}
120-
if (triggerAuto) {
121-
maxToken *= expectedTime / completion.timing;
122-
}
101+
return [
102+
{
103+
insertText: completion,
104+
range: new vscode.Range(position, position),
105+
},
106+
];
107+
} else {
108+
const additionalDocuments: vscode.TextDocument[] = configuration.get(
109+
"experimental.useopentabs"
110+
)
111+
? await getAdditionalDocuments()
112+
: [];
113+
114+
const prompt = await getPromptCompletion({
115+
activeDocument: document,
116+
additionalDocuments: additionalDocuments,
117+
position: position,
118+
maxTokenExpect: triggerAuto ? maxToken : 2000,
119+
});
120+
121+
const parameters = triggerAuto
122+
? {
123+
n_predict: 128,
124+
stop: ["\n", "<|file_separator|>"],
125+
}
126+
: {
127+
n_predict: 512,
128+
stop: ["<|file_separator|>"],
129+
temperature: 0.5,
130+
};
131+
const modelType = triggerAuto
132+
? configuration.get("completion.autoMode")
133+
: configuration.get("completion.manuallyMode");
134+
const completion = await sendCompletionRequestLocal(
135+
prompt,
136+
parameters,
137+
abortController,
138+
loggerCompletion.uuid(),
139+
servers[modelType].serverUrl
140+
);
123141

124-
loggerCompletion.info(`maxToken: ${maxToken}`);
142+
if (completion === null) {
143+
return [];
144+
}
125145

126-
loggerCompletion.info("Request: finished");
146+
if (token.isCancellationRequested) {
147+
loggerCompletion.info(
148+
"Request: canceled by new completion cancel token"
149+
);
127150

128-
return [
129-
{
130-
insertText: completion.content,
131-
range: new vscode.Range(position, position),
132-
},
133-
];
151+
return [];
152+
}
153+
if (triggerAuto) {
154+
maxToken *= expectedTime / completion.timing;
155+
}
156+
loggerCompletion.info("Request: finished");
157+
158+
return [
159+
{
160+
insertText: completion.content,
161+
range: new vscode.Range(position, position),
162+
},
163+
];
164+
}
134165
} catch (error) {
135166
const Error = error as Error;
136167
Logger.error(error);
137168

138169
const errorMessage = Error.message;
139170
vscode.window.showErrorMessage(errorMessage);
140171
} finally {
172+
stopTask();
141173
requestFinish();
142174
}
175+
return [];
143176
},
144177
};
145178

src/common/completion/localCompletion.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as vscode from "vscode";
22
import { randomUUID } from "crypto";
33
import Logger from "../logger";
4-
import statusBar from "../statusBar";
54

65
const logCompletion = (uuid = randomUUID() as string) => {
76
return {
@@ -36,15 +35,13 @@ const defualtParameters = {
3635
slot_id: -1,
3736
};
3837

39-
export const sendCompletionRequest = async (
38+
export const sendCompletionRequestLocal = async (
4039
prompt: string,
4140
parameters: Record<string, any>,
4241
abortController: AbortController,
4342
uuid: string,
4443
url: string
4544
) => {
46-
const { stopTask } = statusBar.startTask();
47-
4845
const loggerCompletion = logCompletion(uuid);
4946

5047
const parametersForCompletion = {
@@ -120,7 +117,5 @@ export const sendCompletionRequest = async (
120117
const errorMessage = Error.message;
121118
vscode.window.showErrorMessage(errorMessage);
122119
return null;
123-
} finally {
124-
stopTask();
125120
}
126121
};

src/common/prompt/promptCompletion.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ const tokenize = async (text: string): Promise<number> => {
2525

2626
const getTextNormalized = (text: string) => {
2727
return text
28-
.replace("<|fim_prefix|>", "")
29-
.replace("<|fim_middle|>", "")
30-
.replace("<|fim_suffix|>", "")
31-
.replace("<|file_separator|>", "");
28+
.replaceAll("<|fim_prefix|>", "")
29+
.replaceAll("<|fim_middle|>", "")
30+
.replaceAll("<|fim_suffix|>", "")
31+
.replaceAll("<|file_separator|>", "");
3232
};
3333

3434
const spliteDocumentByPosition = (
@@ -161,7 +161,7 @@ export const getPromptCompletion = async ({
161161
}) => {
162162
const start = performance.now();
163163

164-
const maxTokenHardLimit = 6000;
164+
const maxTokenHardLimit = 4000;
165165
const maxToken =
166166
maxTokenExpect > maxTokenHardLimit ? maxTokenHardLimit : maxTokenExpect;
167167

@@ -209,7 +209,7 @@ export const getPromptCompletion = async ({
209209

210210
const activeDocumentFileName =
211211
additionalDocumentsText === ""
212-
? ""
212+
? "<|fim_prefix|>"
213213
: "\n" +
214214
"/" +
215215
getRelativePath(activeDocument.uri) +

0 commit comments

Comments
 (0)