DEV Community

Anderson Ribeiro
Anderson Ribeiro

Posted on

Creating (or attempting) to create an autocomplete with OpenAI ChatGPT.

Ok, I had this idea in mind, of how can I create an autocomplete like when we are typing something on Gmail. And since I am kinda obsessed with ChatGPT I thought to myself why not?

So the first thing I needed to create is the textarea. And since I need to show the autocomplete text I needed to add as well a container for the autocompleted text. So here we go.

const Autocomplete = (): ReactElement => {
  return (
    <div className='container-autocomplete'>
      <div className='autocomplete'></div>
      <textarea />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Ok so now we need to add styles to that

* {
  box-sizing: border-box;
}

.container-autocomplete {
  width: 400px;
  height: 200px;
  border: 1px solid #ccc;
  margin: 10px;
  position: relative;
}

textarea {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
  padding: 10px;
  box-sizing: border-box;
  position: absolute;
  resize: none;
  z-index: 1;
  background: transparent;
  font-family: sans-serif;
  font-size: 13px;
}

.autocomplete {
  top: 0;
  left: 0;
  opacity: 0.5;
  position: absolute;
  padding: 10px;
  width: 100%;
  height: 100%;
  overflow: auto;
  font-family: sans-serif;
  font-size: 13px;
}
Enter fullscreen mode Exit fullscreen mode

Next step: create the autocomplete hook. The biggest problem I had to solve is for the ChatGPT 3.5 to behave correctly since it's known to just ignore what the system has to say and add something completely irrelevant as an answer.

So I came with this prompt

You are an autocomplete assistant. Use the user text as a reference for the autocomplete.

Examples:
User: Hello,
Assistant: how are you doing?
User: I wanted to let you know
Assistant: we just arrived
Enter fullscreen mode Exit fullscreen mode

I'm sure it needs more context for something more "powerful" but for now I think it's great. Now we just need to create the hook.

import { 
  ChatCompletionRequestMessage, 
  ChatCompletionRequestMessageRoleEnum, 
  Configuration, 
  OpenAIApi 
} from 'openai';

export const API_KEY_OPEN_AI = 'YOUR_OPEN_AI_SK_KEY';

const configuration = new Configuration({
  apiKey: API_KEY_OPEN_AI,
});

const openai = new OpenAIApi(configuration);

const useAutoComplete = (text: string): [string, () => void] => {
  const [suggestion, setSuggestion] = React.useState<string>('');

  const fetchSuggestion = useCallback(async (text: string): Promise<string> => {
    const messages: ChatCompletionRequestMessage[] = [
      {
        role: ChatCompletionRequestMessageRoleEnum.System,
        content: `You are an autocomplete assistant. Use the user text as a reference for the autocomplete.

Examples:
User: Hello,
Assistant: how are you doing?
User: I wanted to let you know
Assistant: we just arrived`
      },
      {
        role: ChatCompletionRequestMessageRoleEnum.User,
        content: text
      }
    ];

    const response = await openai.createChatCompletion({
      model: 'gpt-3.5-turbo',
      messages
    });

    const data = response.data.choices[0]?.message?.content.trim();

    return data || '';
  }, []);

  useEffect(() => {
    let doNotUpdate = false;

    if (text.length > 10) {
      fetchSuggestion(text)
        .then(suggestion => {
          if (!doNotUpdate) setSuggestion(suggestion);
        });
    }

    return () => {
      doNotUpdate = true;
    };
  }, [text, fetchSuggestion]);

  const clear = useCallback(() => {
    setSuggestion('');
  }, []);

  return [suggestion, clear];
}
Enter fullscreen mode Exit fullscreen mode

Now we need to fix a few things on the autocomplete function

const Autocomplete = (): ReactElement => {
  const [text, setText] = React.useState<string>('');

  const [suggestion, clear] = useAutoComplete(text);

  const handleTextChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setText(e.target.value);
    clear();
  }, [clear]);

  const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === 'Tab') {
      e.preventDefault();
      if (suggestion) {
        setText(text + ' ' + suggestion);
        clear()
      }
    }
  }, [suggestion, text, clear]);

  return (
    <div className='container-autocomplete'>
      <div className='autocomplete'>{text + ' ' + suggestion}</div>
      <textarea 
        value={text} 
        onChange={handleTextChange} 
        onKeyDown={handleKeyDown}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Ok, so how does it work? Everytime the user type something we will send to the autocomplete hook and it will create an autocomplete sentence for us.
The handleTextChange clears the old autocomplete and also sets the new text for the autocompletion.

And finally when the user press Tab it will autocomplete with the new text from the user plus the suggestion.

It was a fun project overall. Now I know it's doable, although it needs some changes so the autocompletion can be reliable and also relevant to what the user is writing.

Top comments (0)