Skip to content

Instantly share code, notes, and snippets.

@tlkahn
Created April 7, 2025 05:52
Show Gist options
  • Save tlkahn/d5978e2e2e82a8eb1b4f40f1bea1118c to your computer and use it in GitHub Desktop.
Save tlkahn/d5978e2e2e82a8eb1b4f40f1bea1118c to your computer and use it in GitHub Desktop.
My emacs copilot
(defun insert-json-entries (json-str)
(let ((json-object-type 'hash-table))
(cl-loop for entry across (json-read-from-string json-str) do
(maphash (lambda (key value)
(insert (format (if (string= key "role") "## %s:\n\n" "%s\n\n")
(string-trim value "[ \t\n\r.]+"))))
entry))))
(defun ask-for-input ()
"Prompt the user for input using the minibuffer."
(interactive)
(let ((input (read-string "Ask about: ")))
input))
(defun get-marked-text ()
"Return the marked text."
(interactive)
(if (use-region-p)
(buffer-substring-no-properties (region-beginning) (region-end))
""))
(defun display-in-side-window (str)
(let ((win (next-window)))
(if (and (or (not win) (equal win (selected-window))) (not (string= (buffer-name (window-buffer (selected-window))) "*ChatGPT*")))
(if (> (frame-pixel-width) (frame-pixel-height))
(setq win (split-window-right))
(setq win (split-window-below))))
(with-selected-window win
(switch-to-buffer "*ChatGPT*")
(setq-local window-min-width 40)
(setq-local window-min-height 1)
(erase-buffer)
(goto-char (point-min))
(global-visual-line-mode)
(insert (read (decode-coding-string str 'utf-8-auto-dos)))
(goto-char (point-min)))))
(defun current-buffer-filename ()
"Return current buffer's filename or name."
(interactive)
(or (buffer-file-name) (buffer-name)))
(defun askgpt(from model &rest args)
(interactive)
(let* (
(current-buffer-filename (current-buffer-filename))
(current-active-buffer (current-buffer))
(marked-text (get-marked-text))
(question (generate-ask from marked-text args))
(url "http://localhost:8888/askgpt")
)
(progn
(let* (
(url-request-method "POST")
(url-request-extra-headers `(("Content-Type" . "application/json")))
(url-request-data (encode-coding-string (json-encode `(("model" . ,model) ("prompt" . ,question) ("filepath" . ,current-buffer-filename))) 'utf-8))
)
(url-retrieve url (lambda (status)
(let ((buffer (current-buffer)))
(with-current-buffer buffer
(goto-char (point-min))
(search-forward-regexp "\n\n")
(setq result (buffer-substring-no-properties (point) (point-max)))
(kill-buffer buffer)
(display-in-side-window result)
(message "done")
)
)
))
))))
(defun generate-ask (from marked-text &optional args)
"Generate an ask for the selected text based on FROM keyword."
(interactive)
(message "args: %S" args)
(pcase from
('query-selection
(prompt-on-query-selection marked-text (ask-for-input)))
('translate-into-english
(prompt-on-translatee (ask-for-input)))
('translate-selection
(apply #'prompt-on-translate-selection (cons marked-text args)))
(_ (if (functionp from)
(funcall from marked-text)
(error "Invalid value for FROM: %s" from)))))
(defun prompt-on-query-selection (marked-text input)
(let ((append-str "")) ;;. Make sure your reply and code are in org-mode format."))
(pcase marked-text
("" (concat input append-str))
(_ (format "%s" (concat "Within the context of \"" (current-buffer-filename) "\", given the text:\n```\n" marked-text "\n```\nPlease answer this question: " input append-str))))
)
)
(defun prompt-on-translatee (translatee)
(format "What's English for %s?" translatee))
(defvar askgpt-lang-alist
'(("en" . "English")
("fr" . "French")
("jp" . "Japanese")
("es" . "Spanish")
("zh-CN" . "Chinese"))
"Association list of language aliases and their corresponding full names.")
(defvar default-translation-language "English" "Default language for translation.")
(defun askgpt-get-lang (alias)
(or (cdr (assoc alias askgpt-lang-alist)) default-translation-language))
(defun prompt-on-translate-selection (marked-text source-lang target-lang)
(format "Translate the following text from %s to %s (keeping all personal names in their original form):\n%s"
(askgpt-get-lang source-lang) (askgpt-get-lang target-lang) marked-text))
(defun rewrite-current-selection (marked-text)
(format "Rewrite the given text in plain and elegant English: %s" marked-text))
(defun shorten-current-selection (marked-text)
(format "Rewrite the given text, using short sentences, paying attention to the rhythm and cadence of words: %s" marked-text))
(defun linusize-current-selection (marked-text)
(format "As a genius programmer like Linus Torvalds, rewrite the given code to make it more concise and elegant:\n ```\n%s\n```" marked-text))
(defun latexize-current-selection (marked-text)
(format "Rewrite the math expressions and formalae within the given text in Latex: %s" marked-text))
(defun code-update-current-selection (marked-text)
(format "Update the given code to make it work:\n```\n%s\n```" marked-text))
(defun explain-current-selection (marked-text)
(format "Explain the given code:\n```\n%s\n```" marked-text))
(defun explain-current-text (marked-text)
(format "Can you explain the following text in a plain and precise language, using metaphors and ascii diagrams where helpful about the given statements? Also pay special attention to those underlined texts:\n-----\n%s\n" marked-text))
(defun testcode-current-selection (marked-text)
(format "Write tests for the given code (use pytest if in python):\n```\n%s\n```" marked-text))
(defun debug-current-selection (marked-text)
(format "What's wrong with the given code:\n```\n%s\n``` Please only include the code diff between original code and suggested update in your answer and skip all other contents." marked-text))
(defun write-code-to-solve (marked-text)
(let ((lang (if (and (boundp 'askgpt-default-lang-write-code-to-solve)
askgpt-default-lang-write-code-to-solve)
askgpt-default-lang-write-code-to-solve
(read-string "Enter language: "))))
(format "Can you provide the solution to the given text in %s:\n```\n%s\n``` Please provide time complexity, space complexity of your solution, and the rationale" lang marked-text)))
(defun write-coverletter-for-jd (marked-text)
(let ((jd (read-string "Enter JD: ")))
(format "Write a cover letter for a position with the following job description:\n```\n%s\n```" jd)))
(defun write-time-space-complexity (marked-text)
(format "What is the time and space complexity of the following code:\n```\n%s\n```" marked-text))
(defun report-error (marked-text)
(interactive)
(let ((error-text (read-string "Enter the error: ")))
(if marked-text
(format "With regard to the given code:\n```\n%s\n```\nNow I have the error:\n```\n%s\n```\nHow to solve it?" marked-text error-text)
(format "Now I have the error:\n```\n%s\n```\nHow to solve it?" error-text))))
(defun format-code (marked-text)
(format "Format the following code correctly. Keep the original comments.\n```\n%s\n```" marked-text))
(defun summarize-history (marked-text)
(format "Provide a detailed but concise summary of the following text as our continuing conversation. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files/code we're working on, and what we're going to do next.\n\n%s\n" marked-text))
(defun continue-conversation (marked-text)
(format "continue"))
(defun review-and-update (markded-text)
(format "review the above answer. any suggested update?")
)
(defun format-text (marked-text)
(let ((format-type (read-string "Format type: " nil nil "markdown")))
(format "Reformat the following text to %s format:\n```\n%s\n```" format-type marked-text)))
(defun confirm-or-refine (marked-text)
(format "Are you sure?"))
(defun ask-questions (marked-text)
(format "Now based on previous converation, think thorough what you are not sure about my meaning. Then ask me one or more questions to help clarify your uncertainties. If you are sure of your understanding on my questions and assumptions on my motives, you can say ok."))
(defun generate-not-working-message (marked-text)
(format "This doesn't work."))
(defun summarize-org-heading (marked-text)
(format "summarize the given text to be as short as possible enough to fit into a org mode header: %s" marked-text))
(defun koanize (marked-text)
(format "Treat the given code as a pendantic koan showcasing some modern C++ tips/gotchas. Summarize and list them now."))
(defun recode-current-selection (marked-text)
(let* ((file-ext (file-name-extension (buffer-file-name)))
(default-lang (cond ((string-equal file-ext "py") "Python")
((string-equal file-ext "js") "ECMAScript")
((string-equal file-ext "el") "Emacs Lisp")
((string-equal file-ext "c") "C")
((string-equal file-ext "cpp") "C++")
((string-equal file-ext "rs") "Rust")
((member file-ext '("ts" "tsx")) "Typescript")
(t file-ext)))
(lang (read-string (format "Which language do you want me to recode into? (default: %s) " default-lang) nil nil default-lang)))
(format "Firstly briefly explain the given code; then accordingly rewrite it in %s; only show me the code without any further explanations: \n```\n%s\n```" lang marked-text)))
(defvar askgpt-default-model "gemini-2.0-flash-exp"
"Default model for askgpt functions. Change this value to use a different model.")
(defvar askgpt-default-lang-write-code-to-solve "c++")
(defun askgpt-helper (from model &rest args)
(interactive)
(apply 'askgpt from model args))
(defmacro define-askgpt-function (name from &optional model &rest args)
`(defun ,name ()
(interactive)
(apply #'askgpt-helper ',from ,(or model 'askgpt-default-model) ',args)))
(define-askgpt-function askgpt-from-query-selection query-selection)
(define-askgpt-function askgpt4-from-query-selection query-selection "gpt-4o-2024-11-20")
(define-askgpt-function askgpt-translate-into-english translate-into-english "gemini-2.0-flash-exp")
(define-askgpt-function askgpt-from-translate-selection translate-selection "gemini-2.0-flash-exp" "jp" "en")
(define-askgpt-function askgpt-to-rewrite-current-selection rewrite-current-selection "gemini-1.5-flash")
(define-askgpt-function askgpt-to-shorten-current-selection shorten-current-selection "gemini-1.5-flash")
(define-askgpt-function askgpt-to-linusize-current-selection linusize-current-selection)
(define-askgpt-function askgpt-to-latexize-current-selection latexize-current-selection "gemini-2.0-flash-exp")
(define-askgpt-function askgpt-to-code-update-current-selection code-update-current-selection)
(define-askgpt-function askgpt-to-explain-current-selection explain-current-selection)
(define-askgpt-function askgpt-to-explain-current-text explain-current-text)
(define-askgpt-function askgpt-to-testcode-current-selection testcode-current-selection "claude-3-5-sonnet-20240620")
(define-askgpt-function askgpt-to-recode-current-selection recode-current-selection)
(define-askgpt-function askgpt-to-write-code-to-solve write-code-to-solve "claude-3-7-sonnet-20250219")
(define-askgpt-function askgpt-to-write-coverletter write-coverletter-for-jd "gemini-1.5-flash")
(define-askgpt-function askgpt-to-debug-current-selection debug-current-selection "claude-3-5-sonnet-20240620")
(define-askgpt-function askgpt-to-write-time-space-complexity write-time-space-complexity "gemini-1.5-flash")
(define-askgpt-function askgpt-to-format-code format-code "gemini-2.0-flash-exp")
(define-askgpt-function askgpt-to-format-text format-text "gemini-2.0-flash-exp")
(define-askgpt-function askgpt-to-confirm confirm-or-refine)
(define-askgpt-function askgpt-to-summarize-org-heading summarize-org-heading "gemini-2.0-flash-exp")
(define-askgpt-function askgpt-to-generate-not-working-message generate-not-working-message)
(define-askgpt-function askgpt-to-summarize-history summarize-history "gemini-2.0-flash-exp")
(define-askgpt-function askgpt-to-continue-conversation continue-conversation "gemini-2.0-flash-exp")
(define-askgpt-function askgpt-to-review-and-update review-and-update "azure-DeepSeek-R1")
(define-askgpt-function askgpt-to-report-error report-error)
(define-askgpt-function askgpt-to-ask-questions ask-questions)
(straight-use-package '(hydra :type git :host github :repo "abo-abo/hydra"))
(defhydra askgpt-menu (:color pink :hint nil)
"
^Text^ ^Code^
_i_: ask _l_: linusize
_I_: Ask _z_: latexize
_t_: transel _u_: update
_T_: transword _e_: tests
_w_: rewrite _r_: recode
_h_: shorten _x_: explain
_v_: coverletter _c_: code
_C_: complexity _!_: debug
_F_: edit _f_: format
_s_: sure _W_: meh
_O_: orgheading _n_: continue
_X_: hardexplain _R_: review
_S_: sumhist _E_: error
_q_: question
"
("m" show-askgpt-messages :exit t)
("M" show-askgpt-cache :exit t)
("x" clear-askgpt-cache :exit t)
("i" askgpt-from-query-selection :color blue :exit t)
("I" askgpt4-from-query-selection :exit t)
("t" askgpt-from-translate-selection :exit t)
("T" askgpt-translate-into-english :exit t)
("w" askgpt-to-rewrite-current-selection :exit t)
("h" askgpt-to-shorten-current-selection :exit t)
("l" askgpt-to-linusize-current-selection :exit t)
("z" askgpt-to-latexize-current-selection :exit t)
("u" askgpt-to-code-update-current-selection :exit t)
("x" askgpt-to-explain-current-selection :exit t)
("e" askgpt-to-testcode-current-selection :exit t)
("!" askgpt-to-debug-current-selection :exit t)
("C" askgpt-to-write-time-space-complexity :exit t)
("r" askgpt-to-recode-current-selection :exit t)
("c" askgpt-to-write-code-to-solve :exit t)
("v" askgpt-to-write-coverletter :exit t)
("f" askgpt-to-format-code :exit t)
("F" askgpt-to-format-text :exit t)
("s" askgpt-to-confirm :exit t)
("O" askgpt-to-summarize-org-heading :exit t)
("W" askgpt-to-generate-not-working-message :exit t)
("X" askgpt-to-explain-current-text :exit t)
("S" askgpt-to-summarize-history :exit t)
("n" askgpt-to-continue-conversation :exit t)
("R" askgpt-to-review-and-update :exit t)
("E" askgpt-to-report-error :exit t)
("q" askgpt-to-ask-questions :exit t)
)
(defun askgpt-set-default-model ()
"Set the default model for AskGPT functions interactively using Helm."
(interactive)
(let ((model-candidates '(
"claude-3-7-sonnet-20250219"
"gemini-2.0-flash"
"azure-DeepSeek-R1"
)))
(helm :sources (helm-build-sync-source "askgpt-models"
:candidates model-candidates
:action (lambda (candidate)
(setq askgpt-default-model candidate)
(message "Default model set to: %s" candidate)))
:buffer "*helm-askgpt-models*")))
(defun askgpt-show-default-model ()
"Display the current default model for AskGPT in the minibuffer."
(interactive)
(message "Current AskGPT default model: %s" askgpt-default-model))
(defun askgpt-set-default-lang ()
"Set the default language for write-code-to-solve function interactively using Helm."
(interactive)
(let ((language-candidates '("typescript" "javascript" "python" "java" "c++" "c#" "ruby" "go" "php" "swift" "English")))
(helm :sources (helm-build-sync-source "askgpt-languages"
:candidates language-candidates
:action (lambda (candidate)
(setq askgpt-default-lang-write-code-to-solve candidate)
(message "Default language set to: %s" candidate)))
:buffer "*helm-askgpt-languages*")))
(defun show-askgpt-cache ()
(interactive)
(let* ((current-buffer-filename (current-buffer-filename))
(message-history (shell-command-to-string (format "redis-cli get '%s' | jq" current-buffer-filename))))
(with-current-buffer (get-buffer-create "*ChatGPT*")
(visual-line-mode t)
(erase-buffer)
(insert-json-entries message-history)
(markdown-mode)
(markdown-toggle-markup-hiding nil)))
(switch-to-buffer "*ChatGPT*"))
(defun parse-and-pop-last-two-entries ()
(interactive)
(parse-and-pop-last-two-entries-internal (current-buffer-filename)))
(defun parse-and-pop-last-two-entries-internal (filename)
"Remove the last two entries from the JSON array stored in Redis under FILENAME."
(condition-case err
(let* ((get-cmd (format "redis-cli get '%s'" filename))
(message-history-str (shell-command-to-string get-cmd))
(message-history (json-parse-string message-history-str))
(history-length (length message-history))
(updated-history (if (> history-length 2)
(seq-subseq message-history 0 (- history-length 2))
message-history))
(updated-history-json (json-serialize updated-history)))
(with-temp-buffer
(insert updated-history-json)
(let ((output-buffer (generate-new-buffer "*redis-output*")))
(call-process-region (point-min) (point-max)
"redis-cli" nil
(list output-buffer t) ; Redirect both stdout and stderr to buffer
nil
"-x" "set" filename)
(with-current-buffer output-buffer
(let ((output (buffer-string)))
(if (string-match-p "ERR" output)
(message "Redis error: %s" output)
(message "Redis output: %s" output))))
)
)
(message "Removed last two entries from message history for %s" filename)
t)
(error
(message "Error: %s" (error-message-string err))
nil)))
(defun show-askgpt-messages ()
(interactive)
(let* ((current-buffer-filename (current-buffer-filename))
(message-history (shell-command-to-string (concat
"curl -s -X POST -H \"Content-Type: application/json\" -d '{\"filepath\": \""
current-buffer-filename
"\"}' http://localhost:8888/agc_history")))
)
(switch-to-buffer "*ChatGPT*")
(visual-line-mode t)
(delete-region (point-min) (point-max))
(goto-char (point-min))
(insert-json-entries message-history)
(markdown-toggle-markup-hiding t)
)
)
(defun sqlite-table-exists-p (db table-name)
(not (null (car (sqlite-select db
(format "SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" table-name))))))
(defun askgpt-save-json (json-str db-path)
(let ((json-object-type 'hash-table)
(db (sqlite-open db-path)))
(unwind-protect
(progn
(unless (sqlite-table-exists-p db "messages")
(sqlite-execute db
"CREATE TABLE IF NOT EXISTS messages
(id INTEGER PRIMARY KEY, role VARCHAR(16), content TEXT)"))
(sqlite-execute db "DELETE FROM messages")
(cl-loop for entry across (json-read-from-string json-str)
do
(sqlite-execute db
"INSERT INTO messages (role, content) VALUES (?,?)"
(list (gethash "role" entry)
(gethash "content" entry)))))
(sqlite-close db))))
(defun archive-askgpt-cache ()
(interactive)
(let* ((current-buffer-filename (current-buffer-filename))
(timestamp (format-time-string "%Y%m%d%H%M%S"))
(archive-key (format "AskgptArchive:%s:%s" current-buffer-filename timestamp))
(db-dir "~/.askgpt/db")
(timestamp (format-time-string "%Y%m%d_%H%M%S"))
(db-name (format "%s-%s" (md5 current-buffer-filename) timestamp))
(db-path (expand-file-name (concat db-name ".db") db-dir)))
(if (= 1 (string-to-number (shell-command-to-string (format "redis-cli EXISTS '%s'" current-buffer-filename))))
(progn
(make-directory db-dir t)
(askgpt-save-json
(shell-command-to-string (format "redis-cli GET '%s'" current-buffer-filename))
db-path)
(shell-command (format "redis-cli DEL '%s'" current-buffer-filename))
(shell-command (format "redis-cli SET 'AskgptHashPathMap:%s' '%s'" db-path current-buffer-filename))
(message (format "Successfully archived to db: %s\n" db-path))
)
(message "No existing key found for %s, creating new archive" current-buffer-filename)
))
)
(defun copy-askgpt-cache-from-another-buffer ()
"Copy the redis value from another buffer's key to the current buffer's key."
(interactive)
(let* ((current-buffer-filename (current-buffer-filename))
(buffer-candidates (mapcar (lambda (buf)
(let ((buf-file (buffer-file-name buf)))
(cons (format "%s (%s)"
(buffer-name buf)
(if buf-file buf-file "no file"))
(if buf-file buf-file (buffer-name buf)))))
(buffer-list))))
(helm :sources (helm-build-sync-source "Select source buffer"
:candidates buffer-candidates
:action (lambda (candidate)
(let ((source-buffer-filename candidate))
(if (= 1 (string-to-number
(shell-command-to-string
(format "redis-cli EXISTS '%s'" source-buffer-filename))))
(progn
(let ((redis-value (shell-command-to-string
(format "redis-cli GET '%s'" source-buffer-filename))))
(shell-command (format "redis-cli SET '%s' %s"
current-buffer-filename
(shell-quote-argument redis-value)))
(message "Copied cache from %s to %s"
source-buffer-filename
current-buffer-filename)))
(message "No existing cache found for %s" source-buffer-filename)))))
:buffer "*helm select buffer for askgpt cache*")))
(defun delete-askgpt-cache ()
(interactive)
(let ((current-buffer-filename (or (buffer-file-name) (buffer-name))))
(when (= 1 (string-to-number (shell-command-to-string (format "redis-cli EXISTS '%s'" current-buffer-filename))))
(shell-command-to-string (format "redis-cli DEL '%s'" current-buffer-filename))
(message "Deleted AskGPT cache for %s" current-buffer-filename))))
(defun search-askgpt-archive-by-path (db-path)
"Search for buffer filename in Redis using the given db-path as key.
Displays result in minibuffern and copies to clipboard."
(interactive "sEnter DB path: ")
(let ((result (shell-command-to-string (format "redis-cli GET 'AskgptHashPathMap:%s'" db-path))))
(if (string-match-p "^(nil)$" result)
(message "No entry found for path: %s" db-path)
(progn
(kill-new result)
(message "Found and copied to clipboard: %s" result)))))
(defun copy-all-markdown-code-blocks-from-buffer (buffer)
"Copy code blocks within the buffer named \"*ChatGPT*\" formatted in markdown mode and insert them into the current buffer."
(interactive)
(let ((code-blocks '()))
(with-current-buffer buffer
(goto-char (point-min))
(while (search-forward-regexp "^```.*" nil t)
(let ((beg (match-end 0)))
(search-forward-regexp "^```" nil t)
(let* ((end (match-beginning 0))
(code (buffer-substring-no-properties beg end))
(first-two-lines (with-temp-buffer
(insert code)
(goto-char (point-min))
(let ((line1 (buffer-substring-no-properties
(line-beginning-position)
(line-end-position))))
(forward-line)
(if (eobp)
line1
(concat line1 " | " (buffer-substring-no-properties
(line-beginning-position)
(line-end-position))))))))
(push (cons first-two-lines code) code-blocks)))))
(if (= (length code-blocks) 0)
(message "No code blocks found")
(if (= (length code-blocks) 1)
(progn
(evil-set-register ?x (cdar code-blocks))
(save-excursion
(forward-line 1)
(evil-paste-from-register ?x)))
(helm :sources (helm-build-sync-source "Code Blocks"
:candidates (nreverse code-blocks)
:action (lambda (candidate)
(evil-set-register ?x candidate)
(save-excursion
(forward-line 1)
(evil-paste-from-register ?x))))
:buffer "*helm code blocks*")))))
(defun copy-buffer-contents (buffer-name &optional converted-to-org)
"Copy all contents from a buffer with BUFFER-NAME to the current buffer. If CONVERTED-TO-ORG is non-nil, convert the contents to org-mode format using md2org."
(let ((contents (with-current-buffer buffer-name (buffer-string))))
(when (and (not (string-empty-p contents))
(not (eq (aref contents (1- (length contents))) ?\n)))
(setq contents (concat contents "\n")))
(when converted-to-org
(setq contents (md2org contents)))
(insert contents)
))
(defun copy-org-code-blocks-from-buffer (buffer)
"Copy Org code blocks from the specified BUFFER without the first and last lines."
(interactive)
(with-current-buffer buffer
(save-excursion
(goto-char (point-min))
(while (re-search-forward org-babel-src-block-regexp nil t)
(let* ((block (match-string 0))
(block-lines (split-string block "\n"))
(block-body (mapconcat 'identity (cdr (butlast block-lines)) "\n")))
(with-current-buffer (other-buffer (current-buffer) t)
(insert block-body "\n\n")))))))
(defun copy-all-code-blocks-from-ChatGPT ()
"Copy all code blocks within the buffer named \"*ChatGPT*\" formatted in markdown mode and insert them into the current buffer."
(interactive)
(copy-all-markdown-code-blocks-from-buffer "*ChatGPT*"))
(defun copy-everything-from-ChatGPT (as-paragraph)
(interactive)
(save-excursion
(if as-paragraph (forward-line 1))
(copy-buffer-contents "*ChatGPT*" as-paragraph)
)
)
(defun dismiss-ChatGPT-window()
(interactive)
(let ((buf (get-buffer "*ChatGPT*")))
(when buf
(delete-windows-on buf)))
)
(defun convert-copy-from-ChatGPT ()
(interactive)
(let ((current-buffer (current-buffer))
(current-point (point)))
(switch-to-buffer "*ChatGPT*")
(mark-whole-buffer)
(convert-marked-text #'md2org "Copied from *ChatGPT*")
(switch-to-buffer current-buffer)
(goto-char current-point)
(yank)))
(defun askgpt-unserialize-cache-from-db (&optional from-archive-of-this-buffer)
(interactive "P")
(let* ((current-buffer-fullpath (current-buffer-filename))
(db-dir "~/.askgpt/db")
(db-name-prefix (md5 current-buffer-fullpath))
(db-files (directory-files db-dir t
(if from-archive-of-this-buffer
(format "^%s-.*\\.db$" db-name-prefix)
"^.*-.*\\.db$"))))
(if (not db-files)
(message "No matching database files found.")
(let ((selected-db (helm :sources (helm-build-sync-source "Database Files"
:candidates (mapcar #'file-name-nondirectory db-files)
:action (lambda (candidate)
(expand-file-name candidate db-dir)))
:buffer "*helm db files*")))
(when selected-db
(let* ((db (sqlite-open selected-db))
(records (sqlite-select db "SELECT role, content FROM messages"))
(json-array (json-encode (mapcar (lambda (record)
(list (cons "role" (nth 0 record))
(cons "content" (nth 1 record))))
records)))
(temp-file (make-temp-file "redis-json")))
(unwind-protect
(when (and current-buffer-fullpath records)
(with-temp-file temp-file
(insert json-array))
(shell-command (format "redis-cli -x SET '%s' < %s"
current-buffer-fullpath
temp-file)))
(sqlite-close db)
(delete-file temp-file))))))))
(defun askgpt-raw-request (question &optional model)
(let* ((filepath (buffer-file-name))
(model (or model "gemini-2.0-flash-exp"))
(url "http://localhost:8888/askgpt")
(url-request-method "POST")
(url-request-extra-headers '(("Content-Type" . "application/json")))
(url-request-data
(encode-coding-string
(json-encode `(("model" . ,model) ("prompt" . ,question) ("filepath" . ,filepath)))
'utf-8))
(response-buffer (url-retrieve-synchronously url)))
(with-current-buffer response-buffer
(goto-char (point-min))
(re-search-forward "\n\n")
(buffer-substring-no-properties (point) (point-max)))))
(provide 'askgpt)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment