summaryrefslogtreecommitdiff
path: root/emacs/scripts/copilot.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs/scripts/copilot.el')
-rw-r--r--emacs/scripts/copilot.el173
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