Skip to content

Commit

Permalink
Return only completion snippet from server to client, Update types
Browse files Browse the repository at this point in the history
  • Loading branch information
arshad-yaseen committed May 26, 2024
1 parent cdff788 commit db3037f
Show file tree
Hide file tree
Showing 20 changed files with 4,569 additions and 1,281 deletions.
5,472 changes: 4,376 additions & 1,096 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions scripts/generate-themes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* This script scans the root `themes` directory to retrieve all VS Code themes,
* then converts them into Monaco Editor-compatible themes.
* The resulting Monaco themes are saved to the `themes.ts` file within the `src` directory.
* Additionally, it updates the `ThemeType` type within the `editor-props.ts` file
* Additionally, it updates the `CustomTheme` type within the `editor-props.ts` file
* to include the newly generated theme names.
*/

Expand Down Expand Up @@ -288,9 +288,9 @@ const processThemes = async () => {

await fs.writeFile(THEMES_DOT_TS_FILE, themesDotTsFileContent);

const themesType = `export type ThemeType = ${themeNames.map(name => `'${name}'`).join(' | ')};`;
const themesType = `type CustomTheme = ${themeNames.map(name => `'${name}'`).join(' | ')};`;

const themesTypeRegex = /export type ThemeType\s*=\s*[^;]*;/s;
const themesTypeRegex = /type CustomTheme\s*=\s*[^;]*;/s;

const typesFile = path.resolve(__dirname, THEMES_TYPE_DEFINED_FILE);
const typesFileContent = await fs.readFile(typesFile, 'utf8');
Expand Down
8 changes: 4 additions & 4 deletions src/classes/config.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {DEFAULT_COMPLETION_MODEL} from '../constants/completion';
import {CompletionModelType} from '../types/completion';
import {CompletionModel} from '../types/completion';

/**
* Configuration class to store the following:
* `model`: The LLM model to use for completion.
*/
class Config {
private static model: CompletionModelType = DEFAULT_COMPLETION_MODEL;
private static model: CompletionModel = DEFAULT_COMPLETION_MODEL;

public static getModel(): CompletionModelType {
public static getModel(): CompletionModel {
return this.model;
}

public static setModel(value: CompletionModelType): void {
public static setModel(value: CompletionModel): void {
this.model = value;
}
}
Expand Down
34 changes: 21 additions & 13 deletions src/classes/copilot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import {
} from '../constants/completion';
import {generateSystemPrompt, generateUserPrompt} from '../helpers/prompt';
import {
CompletionRequestParams,
CompletionRequest,
CompletionResponse,
GroqCompletion,
GroqCompletionCreateParams,
} from '../types/completion';
import {CopilotOptions} from '../types/copilot';
import {POST} from '../utils/http';
import HTTP from '../utils/http';
import Config from './config';

/**
Expand Down Expand Up @@ -40,9 +41,14 @@ class Copilot {
Config.setModel(options?.model || DEFAULT_COMPLETION_MODEL);
}

/**
* Sends a completion request to Groq API and returns the completion.
* @param {CompletionRequest} params - The metadata required to generate the completion.
* @returns {Promise<CompletionResponse>} The completed code snippet.
*/
public async complete({
completionMetadata,
}: CompletionRequestParams): Promise<GroqCompletion | {error: string}> {
}: CompletionRequest): Promise<CompletionResponse> {
try {
const model = Config.getModel();

Expand All @@ -65,19 +71,21 @@ class Copilot {
'Content-Type': 'application/json',
};

const completion = await POST<GroqCompletion, GroqCompletionCreateParams>(
GROQ_API_ENDPOINT,
body,
{
headers,
error: 'Error while fetching from groq API',
},
);
const completion = await HTTP.POST<
GroqCompletion,
GroqCompletionCreateParams
>(GROQ_API_ENDPOINT, body, {
headers,
error: 'Error while fetching from groq API',
});

return completion;
return {
completion: completion.choices[0].message.content,
};
} catch (error) {
console.error(error);
return {
error: `An unexpected error occurred: ${error}`,
error: error.message,
};
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/constants/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {EditorBuiltInTheme, EditorOptionsType} from '../types/common';
import {EditorBuiltInTheme, EditorOptions} from '../types/common';

export const EDITOR_DEFAULT_OPTIONS: EditorOptionsType = {
export const EDITOR_DEFAULT_OPTIONS: EditorOptions = {
scrollBeyondLastColumn: 0,
codeLens: false,
minimap: {
Expand Down
6 changes: 3 additions & 3 deletions src/constants/completion.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {CompletionModelType} from '../types/completion';
import {CompletionModel} from '../types/completion';

export const COMPLETION_MODEL_IDS: Record<CompletionModelType, string> = {
export const COMPLETION_MODEL_IDS: Record<CompletionModel, string> = {
llama: 'llama3-70b-8192',
};

export const DEFAULT_COMPLETION_MODEL: CompletionModelType = 'llama';
export const DEFAULT_COMPLETION_MODEL: CompletionModel = 'llama';

export const GROQ_API_ENDPOINT =
'https://api.groq.com/openai/v1/chat/completions';
13 changes: 5 additions & 8 deletions src/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
} from './constants/common';
import useStartCompletion from './hooks/use-start-completion';
import themes from './themes';
import {
import type {
EditorBuiltInTheme,
EditorOptionsType,
EditorType,
EditorOptions,
StandaloneCodeEditor,
} from './types/common';
import EditorProps from './types/editor-props';
import {deepMerge} from './utils/common';
Expand All @@ -30,7 +30,7 @@ const Editor = ({
);

const onEditorDidMount = React.useCallback(
(editor: EditorType, monaco: Monaco) => {
(editor: StandaloneCodeEditor, monaco: Monaco) => {
setMonacoInstance(monaco);

if (
Expand Down Expand Up @@ -62,10 +62,7 @@ const Editor = ({
key={theme}
theme={theme}
onMount={onEditorDidMount}
options={deepMerge<EditorOptionsType>(
props.options,
EDITOR_DEFAULT_OPTIONS,
)}
options={deepMerge<EditorOptions>(props.options, EDITOR_DEFAULT_OPTIONS)}
/>
);
};
Expand Down
43 changes: 23 additions & 20 deletions src/helpers/get-completion.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {
EditorModelType,
EditorPositionType,
EditorModel,
EditorPosition,
FetchCompletionItemParams,
} from '../types/common';
import {
CompletionMetadata,
CompletionRequestParams,
GroqCompletion,
CompletionRequest,
CompletionResponse,
} from '../types/completion';
import {
determineCompletionMode,
getCodeBeforeAndAfterCursor,
} from '../utils/completion/syntax-parser';
import {isValidCompletion} from '../utils/completion/validate-completion';
import {POST} from '../utils/http';
import HTTP from '../utils/http';

export const fetchCompletionItem = async ({
filename,
Expand All @@ -25,14 +25,22 @@ export const fetchCompletionItem = async ({
model,
position,
token,
}: FetchCompletionItemParams) => {
}: FetchCompletionItemParams): Promise<string | null> => {
if (!isValidCompletion(position, model, language) || !code) {
return null;
}

const abortController = new AbortController();
const controller = new AbortController();

const data = await POST<GroqCompletion, CompletionRequestParams>(
if (token.isCancellationRequested) {
controller.abort();
return null;
}

const {completion, error} = await HTTP.POST<
CompletionResponse,
CompletionRequest
>(
endpoint,
{
completionMetadata: constructCompletionMetadata({
Expand All @@ -49,20 +57,15 @@ export const fetchCompletionItem = async ({
'Content-Type': 'application/json',
},
error: 'Error while fetching completion item',
signal: abortController.signal,
signal: controller.signal,
},
);

if (token.isCancellationRequested) {
abortController.abort();
return null;
}

if (data.error) {
if (error || !completion) {
return null;
}

return data.choices[0].message.content;
return completion;
};

// Construct completion metadata based on the cursor position and code.
Expand Down Expand Up @@ -93,7 +96,7 @@ export const constructCompletionMetadata = ({
return {
filename,
language,
technologies: technologies || undefined,
technologies,
externalContext,
codeBeforeCursor,
codeAfterCursor,
Expand All @@ -105,9 +108,9 @@ export const constructCompletionMetadata = ({

// Compute a cache key based on the cursor's position and preceding text
// This key is used to cache completion results for the same code context
export const computeCacheKeyForCompletion = (
cursorPosition: EditorPositionType,
model: EditorModelType,
export const computeCompletionCacheKey = (
cursorPosition: EditorPosition,
model: EditorModel,
): string => {
const {codeBeforeCursor} = getCodeBeforeAndAfterCursor(cursorPosition, model);

Expand Down
4 changes: 2 additions & 2 deletions src/helpers/prompt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {CompletionMetadata, CompletionMode} from '../types/completion';
import {TechnologiesType} from '../types/editor-props';
import {Technologies} from '../types/editor-props';
import {joinWithAnd} from '../utils/common';

const CURSOR_PLACEHOLDER = '<<CURSOR>>';
Expand Down Expand Up @@ -29,7 +29,7 @@ export const generateSystemPrompt = (metadata: CompletionMetadata): string => {
};

const formatTechnology = (
technologies?: TechnologiesType,
technologies?: Technologies,
language?: string,
): string => {
if (!technologies?.length && !language) return '';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';

/**
* Debounces a function that returns a Promise, and calls it instantly after typing stops.
* Debounces a function that returns a Promise.
*
* @param func - The function to debounce. This should be a function that returns a Promise.
* @param delay - The delay in milliseconds to wait before considering that typing has stopped.
* @returns A debounced version of the function.
*/
const useTypingDebounceFn = <T extends (...args: any[]) => Promise<any>>(
const useDebounceFn = <T extends (...args: any[]) => Promise<any>>(
func: T,
delay: number = 1000,
): ((...funcArgs: Parameters<T>) => Promise<ReturnType<T>>) => {
Expand Down Expand Up @@ -41,4 +41,4 @@ const useTypingDebounceFn = <T extends (...args: any[]) => Promise<any>>(
return debouncedFunc;
};

export default useTypingDebounceFn;
export default useDebounceFn;
36 changes: 18 additions & 18 deletions src/hooks/use-start-completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,36 @@ import {Monaco} from '@monaco-editor/react';

import {LocalCodePredictionEngine} from '../classes/local-code-prediction-engine';
import {
computeCacheKeyForCompletion,
computeCompletionCacheKey,
fetchCompletionItem,
} from '../helpers/get-completion';
import {EditorInlineCompletionType} from '../types/common';
import {EditorInlineCompletion} from '../types/common';
import {
CompletionSpeedType,
EndpointType,
ExternalContextType,
FilenameType,
TechnologiesType,
CompletionSpeed,
Endpoint,
ExternalContext,
Filename,
Technologies,
} from '../types/editor-props';
import useTypingDebounceFn from './use-typing-debounce-fn';
import useDebounceFn from './use-debounce-fn';

const localPredictionEngine = new LocalCodePredictionEngine();

const useStartCompletion = (
filename: FilenameType | undefined,
endpoint: EndpointType | undefined,
technologies: TechnologiesType | undefined,
filename: Filename | undefined,
endpoint: Endpoint | undefined,
technologies: Technologies | undefined,
language: string | undefined,
completionSpeed: CompletionSpeedType | undefined,
externalContext: ExternalContextType | undefined,
completionSpeed: CompletionSpeed | undefined,
externalContext: ExternalContext | undefined,
monacoInstance: Monaco | null,
) => {
const completionCache = React.useRef<Map<string, EditorInlineCompletionType>>(
const completionCache = React.useRef<Map<string, EditorInlineCompletion>>(
new Map(),
);
const isCompletionHandled = React.useRef<boolean>(false);

const fetchCompletionItemDebounced = useTypingDebounceFn(
const fetchCompletionItemDebounced = useDebounceFn(
fetchCompletionItem,
completionSpeed === 'little-faster' ? 350 : 600,
);
Expand All @@ -59,7 +59,7 @@ const useStartCompletion = (
position.column,
);

const cacheKey = computeCacheKeyForCompletion(position, model);
const cacheKey = computeCompletionCacheKey(position, model);

// Check if the completion is already cached
if (completionCache.current.has(cacheKey)) {
Expand Down Expand Up @@ -98,7 +98,7 @@ const useStartCompletion = (
}

try {
// Fetch the completion item from the LLM model
// Fetch the completion item from the Groq
const completion = await fetchCompletionItemDebounced({
filename,
endpoint,
Expand All @@ -112,7 +112,7 @@ const useStartCompletion = (
});

if (completion) {
const newItem: EditorInlineCompletionType = {
const newItem: EditorInlineCompletion = {
insertText: completion,
range: cursorRange,
};
Expand Down
Loading

0 comments on commit db3037f

Please sign in to comment.