diff options
Diffstat (limited to 'emacs/scripts/copilot.el')
| -rw-r--r-- | emacs/scripts/copilot.el | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/emacs/scripts/copilot.el b/emacs/scripts/copilot.el new file mode 100644 index 0000000..3332010 --- /dev/null +++ b/emacs/scripts/copilot.el @@ -0,0 +1,173 @@ +;;; copilot.el --- Emacs Copilot + +;; Copyright 2023 Justine Alexandra Roberts Tunney + +;; Author: Justine Tunney +;; Email: jtunney@mozilla.com +;; License: Apache 2.0 +;; Version: 0.1 + +;; Copyright 2023 Mozilla Foundation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +;;; Commentary: +;; +;; The `copilot-complete' function demonstrates that ~100 lines of LISP +;; is all it takes for Emacs to do that thing Github Copilot and VSCode +;; are famous for doing except superior w.r.t. both quality and freedom +;; +;; Emacs Copilot helps you do pair programming with a local-running LLM +;; that generates code completions within Emacs buffers. The LLM is run +;; as a sub-command that remembers your local editing history on a file +;; by file basis. Tokens stream into your buffer without delay as gen'd +;; and you can hit `C-g' to interrupt your LLM at any time. History and +;; memory can also be deleted from the LLM's context when deleting code +;; from your Emacs buffer that matches up verbatim. Copilot is language +;; agnostic and your programming language is determed by file extension +;; +;; The recommended LLM right now is WizardCoder 34b since it scores the +;; same as GPT-4 on HumanEval. You need a computer like a Mac Studio M2 +;; Ultra in order to use it. If you have a modest system then you could +;; consider downloading the WizardCoder-Python-13b llamafile since it's +;; almost as good, and will even go acceptably fast on CPU-only systems +;; having at least AVX2 and 2200 MT/s RAM. If you're even more strapped +;; for compute and use things like Raspberry Pi, then give Phi-2 a spin +;; +;; To get started, try writing the first line of a function, e.g. +;; +;; bool is_prime(int x) { +;; +;; Then place your caret at the end of the line, and press `C-c C-k` to +;; hand over control to your LLM, which should generate the rest of the +;; function implementation for you. Things are also tuned so the LLM is +;; likely to stop as soon as a function is made. Explanations and other +;; kind of ELI5 commentary is avoided too. +;; +;; Later on, if you were to write something like this: +;; +;; int main() { +;; for (int i = 0; i < 100; +;; +;; And ask your LLM to complete that, then your LLM will likely recall +;; that you two wrote an is_prime() function earlier, even though it's +;; only considering those two lines in the current instruction. You'll +;; most likely then see it decide to generate code to print the primes + +;;; Code: + +(defgroup copilot nil + "Large language model code completion." + :prefix "copilot-" + :group 'editing) + +(defcustom copilot-model + "/home/tslil/llm/gguf_models/dolphin-2.5-mixtral-8x7b.Q4_K_M.gguf" + "Path of llamafile executable with LLM weights." + :type 'string + :group 'copilot) + +(defcustom copilot-llama.cpp + "/home/tslil/llm/llama.cpp/main" + "Path to llama.cpp executable" + :type 'string + :group 'copilot) + +;;;###autoload +(defun copilot-complete () + (interactive) + (let* ((spot (point)) + (inhibit-quit t) + (curfile (buffer-file-name)) + (cash (concat curfile ".cache")) + (hist (concat curfile ".prompt")) + (lang (file-name-extension curfile)) + + ;; extract current line, to left of caret + ;; and the previous line, to give the llm + (code (save-excursion + (dotimes (i 2) + (when (> (line-number-at-pos) 1) + (previous-line))) + (beginning-of-line) + (buffer-substring-no-properties (point) spot))) + + ;; create new prompt for this interaction + (system "\ +You are an Emacs code generator. \ +Writing comments is forbidden. \ +Writing test code is forbidden. \ +Writing English explanations is forbidden. ") + (prompt (format + "[INST]%sGenerate %s code to complete:[/INST]\n```%s\n%s" + (if (file-exists-p cash) "" system) lang lang code))) + + ;; iterate text deleted within editor then purge it from prompt + (when kill-ring + (save-current-buffer + (find-file hist) + (dotimes (i 10) + (let ((substring (current-kill i t))) + (when (and substring (string-match-p "\n.*\n" substring)) + (goto-char (point-min)) + (while (search-forward substring nil t) + (delete-region (- (point) (length substring)) (point)))))) + (save-buffer 0) + (kill-buffer (current-buffer)))) + + ;; append prompt for current interaction to the big old prompt + (write-region prompt nil hist 'append 'silent) + + ;; run llamafile streaming stdout into buffer catching ctrl-g + (with-local-quit + (call-process copilot-llama.cpp nil (list (current-buffer) nil) t + "-m" copilot-model + "--silent-prompt" + "--log-disable" + "--prompt-cache" cash + "--prompt-cache-all" + "--temp" "0" + "-c" "1024" + "-ngl" "3" + "-r" "```" + "-r" "\n}" + "-f" hist)) + + ;; get rid of most markdown syntax + (let ((end (point))) + (save-excursion + (goto-char spot) + (while (search-forward "\\_" end t) + (backward-char) + (delete-backward-char 1 nil) + (setq end (- end 1))) + (goto-char spot) + (while (search-forward "```" end t) + (delete-backward-char 3 nil) + (setq end (- end 3)))) + + ;; append generated code to prompt + (write-region spot end hist 'append 'silent)))) + +;; define `ctrl-c ctrl-k` keybinding for llm code completion +(defun copilot-c-hook () + (define-key c-mode-base-map (kbd "C-x c c") 'copilot-complete)) +(add-hook 'c-mode-common-hook 'copilot-c-hook) +(defun copilot-py-hook () + (define-key python-mode-map (kbd "C-x c c") 'copilot-complete)) +(add-hook 'python-common-hook 'copilot-py-hook) +(global-set-key (kbd "C-x c c") 'copilot-complete) + +(provide 'copilot) + +;;; ansi-mode.el ends here |
