summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--emacs/scripts/copilot.el173
-rw-r--r--llama.cpp_silent_prompt.patch49
2 files changed, 222 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
diff --git a/llama.cpp_silent_prompt.patch b/llama.cpp_silent_prompt.patch
new file mode 100644
index 0000000..71b6ad8
--- /dev/null
+++ b/llama.cpp_silent_prompt.patch
@@ -0,0 +1,49 @@
+commit 90b0d35e400a078545401840f78c0bf02207864d
+Author: tslil clingman <>
+Date: Sun Dec 31 18:50:17 2023 +0100
+
+ add --silent-prompt
+
+diff --git a/common/common.cpp b/common/common.cpp
+index eacaee1..f939b50 100644
+--- a/common/common.cpp
++++ b/common/common.cpp
+@@ -592,6 +592,8 @@ bool gpt_params_parse_ex(int argc, char ** argv, gpt_params & params) {
+ params.numa = true;
+ } else if (arg == "--verbose-prompt") {
+ params.verbose_prompt = true;
++ } else if (arg == "--silent-prompt") {
++ params.silent_prompt = true;
+ } else if (arg == "-r" || arg == "--reverse-prompt") {
+ if (++i >= argc) {
+ invalid_param = true;
+diff --git a/common/common.h b/common/common.h
+index 9659aa0..01c68fa 100644
+--- a/common/common.h
++++ b/common/common.h
+@@ -122,6 +122,7 @@ struct gpt_params {
+ bool use_mlock = false; // use mlock to keep model in memory
+ bool numa = false; // attempt optimizations that help on some NUMA systems
+ bool verbose_prompt = false; // print prompt tokens before generation
++ bool silent_prompt = false; // don't print prompt to stdout
+ bool infill = false; // use infill mode
+ bool dump_kv_cache = false; // dump the KV cache contents for debugging purposes
+ bool no_kv_offload = false; // disable KV offloading
+@@ -240,4 +241,3 @@ void dump_kv_cache_view(const llama_kv_cache_view & view, int row_size = 80);
+
+ // Dump the KV cache view showing individual sequences in each cell (long output).
+ void dump_kv_cache_view_seqs(const llama_kv_cache_view & view, int row_size = 40);
+-
+diff --git a/examples/main/main.cpp b/examples/main/main.cpp
+index c096f11..8676ab4 100644
+--- a/examples/main/main.cpp
++++ b/examples/main/main.cpp
+@@ -461,7 +461,7 @@ int main(int argc, char ** argv) {
+ }
+
+ bool is_antiprompt = false;
+- bool input_echo = true;
++ bool input_echo = !params.silent_prompt;
+ bool need_to_save_session = !path_session.empty() && n_matching_session_tokens < embd_inp.size();
+
+ int n_past = 0;