Skip to content

Commit

Permalink
Improve prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
arshad-yaseen committed May 17, 2024
1 parent 1dffd96 commit bc547d8
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 182 deletions.
138 changes: 25 additions & 113 deletions docs/components/editor-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,130 +1,42 @@
import {useState} from 'react';

import {Tabs, TabsList, TabsTrigger} from '@/components/ui/tabs';
import {TabsContent} from '@radix-ui/react-tabs';
import {motion} from 'framer-motion';
import {Editor, EditorProps, Theme} from 'monacopilot';
import {Editor, Theme} from 'monacopilot';
import {useTheme} from 'next-themes';

interface File {
filename: string;
language: string;
path: string;
content: string;
}

const COPILOT_ENDPOINT = '/api/copilot';

const EDITOR_OPTIONS: EditorProps['options'] = {
padding: {top: 60, bottom: 16},
overviewRulerBorder: false,
overviewRulerLanes: 0,
scrollBeyondLastLine: false,
fontFamily: 'var(--font-mono)',
fontSize: 15,
renderLineHighlightOnlyWhenFocus: true,
lineDecorationsWidth: 0,
const EDITOR_DEFAULTS = {
value: `// Start coding here to see the autocompletions in action!`,
language: 'javascript',
options: {
padding: {top: 16, bottom: 16},
overviewRulerBorder: false,
overviewRulerLanes: 0,
scrollBeyondLastLine: false,
fontFamily: 'var(--font-mono)',
fontSize: 15,
scrollbar: {alwaysConsumeMouseWheel: false},
},
};

const EditorDemo = () => {
const [files, setFiles] = useState<Array<File>>([
{
filename: 'index.js',
language: 'javascript',
path: './index.js',
content: `import { add } from './utils';\n\nconst result = add(1, 2);`,
},
{
filename: 'utils.js',
language: 'javascript',
path: './utils.js',
content:
'export function add(a, b) { return a + b; }\nexport function subtract(a, b) { return a - b; }',
},
{
filename: 'constants.js',
language: 'javascript',
path: './constants.js',
content: 'export const PI = 3.14159;',
},
]);
const {resolvedTheme} = useTheme();
const theme: Theme =
resolvedTheme === 'dark' ? 'codesandbox-dark' : 'github-light';

return (
<motion.div
initial={{opacity: 0, y: 6}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.4, delay: 0.6}}
className="rounded-xl my-8 overflow-hidden bg-background border shadow-md shadow-neutral-50 dark:shadow-neutral-900 md:w-[700px] w-full h-[400px] relative">
<Tabs defaultValue="index.js" className="w-full h-full">
<TabsList className="absolute top-0 border-b left-0 rounded-b-none z-50 w-full justify-start px-3 h-12 gap-2 bg-neutral-50 dark:bg-neutral-950">
{files.map(file => (
<TabsTrigger
key={file.filename}
value={file.filename}
className="rounded-md border font-mono">
{file.filename}
</TabsTrigger>
))}
</TabsList>
{files.map(file => (
<TabsContent
key={file.filename}
value={file.filename}
className="w-full h-full">
<RenderEditor
filename={file.filename}
value={file.content}
language={file.language}
externalContext={files
.filter(f => f.filename !== file.filename)
.map(f => ({path: f.path, content: f.content}))}
setValue={(filename, value) =>
setFiles(prevFiles =>
prevFiles.map(f =>
f.filename === filename ? {...f, content: value} : f,
),
)
}
/>
</TabsContent>
))}
</Tabs>
className="rounded-xl my-8 overflow-hidden bg-background border shadow-md shadow-neutral-50 dark:shadow-neutral-900 md:w-[700px] w-full h-[400px]">
<Editor
endpoint="/api/copilot"
language={EDITOR_DEFAULTS.language}
theme={theme}
className="w-full"
defaultValue={EDITOR_DEFAULTS.value}
options={EDITOR_DEFAULTS.options}
/>
</motion.div>
);
};

interface RenderEditorProps extends EditorProps {
setValue: (filename: string, value: string) => void;
}

const RenderEditor = ({
setValue,
filename,
value,
language,
externalContext,
}: RenderEditorProps) => {
const {resolvedTheme} = useTheme();
const theme: Theme =
resolvedTheme === 'dark' ? 'codesandbox-dark' : 'github-light';

return (
<Editor
endpoint={COPILOT_ENDPOINT}
theme={theme}
options={EDITOR_OPTIONS}
onChange={newValue => {
if (filename && newValue) {
setValue(filename, newValue);
}
}}
value={value}
language={language}
className="w-full z-10"
loading=""
externalContext={externalContext}
/>
);
};

export default EditorDemo;
17 changes: 7 additions & 10 deletions docs/components/features.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,26 @@ import {
CardHeader,
CardTitle,
} from '@/components/ui/card';
import {
DoubleArrowRightIcon,
LightningBoltIcon,
ShadowIcon,
} from '@radix-ui/react-icons';
import {FileIcon, LightningBoltIcon, ShadowIcon} from '@radix-ui/react-icons';

const FEATURES = [
{
icon: LightningBoltIcon,
title: 'Framework Specific Completions',
description: 'Get completions based on the framework you are using.',
},
{
icon: FileIcon,
title: 'Multi-file Completions',
description:
"You can provide other files' code or content and get completions that are relevant to that context.",
},
{
icon: ShadowIcon,
title: '20+ Popular Themes',
description:
'Choose from a wide range of themes to customize your editor easily.',
},
{
icon: DoubleArrowRightIcon,
title: 'Fast completions',
description: 'Get completions in fast and efficient way.',
},
];

const Features = () => {
Expand Down
4 changes: 2 additions & 2 deletions src/classes/copilot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import Config from './config';
*
* @param {string} apiKey - The Groq API key.
* @param {CopilotOptions} [options] - Optional parameters to configure the completion model,
* such as the model ID. Defaults to `llama3-70b-8192` if not specified.
* such as the model ID. Defaults to `llama` if not specified.
*
* @example
* ```typescript
* const copilot = new Copilot(process.env.GROQ_API_KEY, {
* model: 'llama3-70b-8192',
* model: 'llama',
* });
* ```
*/
Expand Down
2 changes: 1 addition & 1 deletion src/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const EDITOR_DEFAULT_OPTIONS: EditorOptionsType = {
automaticLayout: true,
};

export const EDITOR_BUILT_IN_THEMES: Array<EditorBuiltInTheme> = [
export const EDITOR_BUILT_IN_THEMES: EditorBuiltInTheme[] = [
'light',
'vs-dark',
];
2 changes: 1 addition & 1 deletion src/constants/contextual-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ const FILTER_WEIGHTS: number[] = [
const FILTER_INTERCEPT: number = -0.3043572714994554;

// 15% acceptance threshold
const CONTEXTUAL_FILTER_ACCEPT_THRESHOLD: number = 0.13;
const CONTEXTUAL_FILTER_ACCEPT_THRESHOLD: number = 0.15;

export {
FILTER_LANGUAGE_MAP,
Expand Down
68 changes: 40 additions & 28 deletions src/helpers/copilot.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,71 @@
import {CompletionMetadata, CompletionMode} from '../types/completion';

const CURSOR_PLACEHOLDER = '<|cursor|>';
const CURSOR_PLACEHOLDER = '<<CURSOR>>';

const getProperLanguageName = (language?: string): string | undefined => {
return language === 'javascript' ? 'latest JavaScript' : language;
};

const getDescriptionForMode = (mode: CompletionMode): string => {
switch (mode) {
case 'fill-in-the-middle':
return 'filling in the middle of code';
case 'continuation':
return 'continuing the code';
default:
return 'unknown mode';
return 'filling in the middle of the code';
case 'completion':
return 'completing the code';
}
};

export const generateSystemPrompt = (metadata: CompletionMetadata): string => {
const language = getProperLanguageName(metadata.language);
const description = getDescriptionForMode(
metadata.editorState.completionMode,
);
const langText = language || '';
return `You are an expert ${langText} code completion assistant known for exceptional skill in ${description}.`;
};

export const generateUserPrompt = (metadata: CompletionMetadata): string => {
const {
language = 'the language',
filename,
framework,
editorState,
codeBeforeCursor,
codeAfterCursor,
externalContext,
} = metadata;

let prompt = `As an expert ${language} code completion assistant known for high accuracy in ${getDescriptionForMode(editorState.completionMode)}, could you assist with the code at the cursor location marked '${CURSOR_PLACEHOLDER}'? This code is part of ${filename ? `the ${filename} file` : 'a larger project'}. Please `;
const language = getProperLanguageName(metadata.language);
const modeDescription = getDescriptionForMode(editorState.completionMode);
const fileNameText = filename
? `the file named ${filename}`
: 'a larger project';

const frameworkText = framework
? ` The code utilizes the ${framework} framework in ${language}.`
: ` The code is implemented in ${language}.`;

let prompt = `You will be presented with a code snippet where the cursor location is marked with '${CURSOR_PLACEHOLDER}'. Your task is to assist with ${modeDescription}. This code is part of ${fileNameText}. Please `;

switch (editorState.completionMode) {
case 'fill-in-the-middle':
prompt += `generate a completion to fill the middle of the code surrounding '${CURSOR_PLACEHOLDER}'. Ensure the completion precisely replaces '${CURSOR_PLACEHOLDER}', maintaining consistency, semantic accuracy, and relevance to the context.`;
prompt += `generate a completion to fill the middle of the code around '${CURSOR_PLACEHOLDER}'. Ensure the completion replaces '${CURSOR_PLACEHOLDER}' precisely, maintaining consistency, semantic accuracy, and relevance to the context. The completion must start exactly from the cursor position without any preceding or following characters, and it should not introduce any syntactical or semantic errors to the existing code.`;
break;
case 'continuation':
prompt += `provide a continuation from '${CURSOR_PLACEHOLDER}'. The completion should fluidly extend the existing code, precisely replacing '${CURSOR_PLACEHOLDER}' while adhering to ${language} standards and ensuring semantic correctness and contextual appropriateness.`;
case 'completion':
prompt += `provide the necessary completion for '${CURSOR_PLACEHOLDER}' while ensuring consistency, semantic accuracy, and relevance to the context. The completion must start exactly from the cursor position without any preceding or following characters, and it should not introduce any syntactical or semantic errors to the existing code.`;
break;
}

prompt += ` Output only the necessary completion code, without additional explanations or content.`;
prompt += ` Output only the necessary completion code, without additional explanations or content.${frameworkText}`;

if (framework) {
prompt += ` The code utilizes the ${framework} framework in ${language}.`;
} else {
prompt += ` The code is implemented in ${language}.`;
}

return prompt.endsWith('.') ? prompt : prompt + '.';
};
let codeForCompletion = `${codeBeforeCursor}${CURSOR_PLACEHOLDER}${codeAfterCursor}\n\n`;

export const generateUserPrompt = (metadata: CompletionMetadata): string => {
const {codeBeforeCursor, codeAfterCursor, externalContext} = metadata;

let prompt = `${codeBeforeCursor}${CURSOR_PLACEHOLDER}${codeAfterCursor}\n\n`;

// Append external context information if available
if (externalContext && externalContext.length > 0) {
prompt += externalContext
codeForCompletion += externalContext
.map(context => `// Path: ${context.path}\n${context.content}\n`)
.join('\n');
}

return prompt;
prompt += `\n\n<code>\n${codeForCompletion}\n</code>`;

return prompt.endsWith('.') ? prompt : `${prompt}.`;
};
20 changes: 8 additions & 12 deletions src/hooks/use-typing-debounce-fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,32 @@ const useTypingDebounceFn = <T extends (...args: any[]) => Promise<any>>(
func: T,
delay: number = 1000,
): ((...funcArgs: Parameters<T>) => Promise<ReturnType<T>>) => {
const [timer, setTimer] = React.useState<ReturnType<
typeof setTimeout
> | null>(null);
const timerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);

const debouncedFunc = React.useCallback(
(...args: Parameters<T>): Promise<ReturnType<T>> => {
if (timer) {
clearTimeout(timer);
if (timerRef.current) {
clearTimeout(timerRef.current);
}

return new Promise<ReturnType<T>>((resolve, reject) => {
const newTimer = setTimeout(() => {
timerRef.current = setTimeout(() => {
func(...args)
.then(resolve)
.catch(reject);
}, delay);

setTimer(newTimer);
});
},
[func, delay, timer],
[func, delay],
);

React.useEffect(() => {
return () => {
if (timer) {
clearTimeout(timer);
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, [timer]);
}, []);

return debouncedFunc;
};
Expand Down
2 changes: 1 addition & 1 deletion src/types/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface CompletionRequestParams {
completionMetadata: CompletionMetadata;
}

export type CompletionMode = 'fill-in-the-middle' | 'continuation';
export type CompletionMode = 'fill-in-the-middle' | 'completion';

export interface CompletionMetadata {
language: string | undefined;
Expand Down
4 changes: 2 additions & 2 deletions src/types/editor-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export type FrameworkType =
| 'ant design';

export type CompletionSpeedType = 'little-faster' | 'normal';
export type ExternalContextType = Array<{
export type ExternalContextType = {
/**
* The relative path from the current editing code in the editor to an external file.
*
Expand All @@ -59,7 +59,7 @@ export type ExternalContextType = Array<{
* The content of the external file as a string.
*/
content: string;
}>;
}[];

/**
* Themes available for the Rich Monaco Editor.
Expand Down
Loading

0 comments on commit bc547d8

Please sign in to comment.