.emacs.d

the most bloated Emacs config on this planet
git clone https://git.trogloxene.org/.emacs.d.git
Log | Files | Refs

init-gptel.el (7544B)


      1 ;;; init-gptel.el --- ai -*- lexical-binding: t -*-
      2 
      3 ;;; Commentary:
      4 ;; I vibe coded this entire part.
      5 
      6 ;;; Code:
      7 
      8 (my-use-package gptel my-enable-unessentials
      9   :config
     10   (setq gptel-log-level t
     11         gptel-default-mode 'org-mode
     12         gptel-include-reasoning "*gptel-reasoning*")
     13   (add-hook 'gptel-post-response-functions 'gptel-end-of-response)
     14   (add-hook 'gptel-post-stream-hook 'gptel-auto-scroll)
     15   (setq gptel-prompt-prefix-alist '((markdown-mode . "## User: ") (org-mode . "** User: ") (text-mode . "## User: ")))
     16 
     17   (gptel-make-tool
     18    :name "read_buffer"                    ; javascript-style snake_case name
     19    :function (lambda (buffer)                  ; the function that will run
     20                (unless (buffer-live-p (get-buffer buffer))
     21                  (error "Error: buffer %s is not live" buffer))
     22                (with-current-buffer  buffer
     23                  (buffer-substring-no-properties (point-min) (point-max))))
     24    :description "return the contents of an emacs buffer"
     25    :args (list '(:name "buffer"
     26                        :type string            ; :type value must be a symbol
     27                        :description "the name of the buffer whose contents are to be retrieved"))
     28    :category "emacs")                     ; An arbitrary label for grouping
     29 
     30   (gptel-make-openai "gptel-Kimi-Code"
     31     :host "api.kimi.com"
     32     :endpoint "/coding/v1/chat/completions"
     33     :protocol "https"
     34     :key (lambda ()
     35            (auth-info-password (car (auth-source-search :host "api.kimi.com" :user "apikey"))))
     36     :stream t
     37     :models '(k3-256k)
     38     :header (lambda (_)
     39               (when-let* ((key (gptel--get-api-key)))
     40                 `(("User-Agent" . "RooCode/3.30.3")
     41                   ("HTTP-Referer" . "https://github.com/RooVetGit/Roo-Cline")
     42                   ("X-Title" . "Roo Code")
     43                   ("Authorization" . ,(concat "Bearer " key))))))
     44 
     45   (setq gptel-model 'k3-256k
     46         gptel-backend (gptel-get-backend "gptel-Kimi-Code"))
     47 
     48   :init
     49   (defun my-gptel--sanitize-filename (name)
     50     "Clean NAME into a safe kebab-case filename string."
     51     (let ((clean (downcase name)))
     52       (setq clean (replace-regexp-in-string "[^a-z0-9_-]" "-" clean))
     53       (setq clean (replace-regexp-in-string "^-+\\|[-]+$" "" clean))
     54       (setq clean (replace-regexp-in-string "-[-]+" "-" clean))
     55       (string-trim clean)))
     56 
     57   (defun my-gptel-save-session ()
     58     "Save current gptel session to ~/gptel/sessions/ with AI-generated
     59 filename.
     60 The filename is prefixed with a YYYYMMDD-HHmmss timestamp and generated
     61 from the last 1500 characters of the buffer."
     62     (interactive)
     63     (unless gptel-mode
     64       (user-error "Not a gptel session buffer"))
     65     (let* ((buf (current-buffer))
     66            (content (with-current-buffer buf
     67                       (buffer-substring-no-properties (point-min) (point-max))))
     68            (tail (substring content (max 0 (- (length content) 1500))))
     69            (prompt-text (format "Based on the following conversation, generate a short kebab-case
     70 filename (3 to 5 lowercase English words, hyphen-separated, no file
     71 extension). Output ONLY the filename, nothing else.\n\n%s"
     72                                 tail)))
     73       (message "Generating filename with AI...")
     74       (let ((fsm (gptel-request prompt-text
     75                    :system "You are a filename generator. Output ONLY a short kebab-case
     76 filename (3-5 lowercase English words separated by hyphens). No
     77 explanations, no quotes, no punctuation, no file extension."
     78                    :stream nil
     79                    :callback
     80                    (lambda (response info)
     81                      (condition-case err
     82                          (cond
     83                           ((null response)
     84                            (message "Failed to generate filename: %s"
     85                                     (or (plist-get info :status) "unknown error")))
     86                           ((not (stringp response))
     87                            (message "Unexpected response type: %S" response))
     88                           (t
     89                            (let* ((generated (my-gptel--sanitize-filename (string-trim response)))
     90                                   (generated (if (string-empty-p generated)
     91                                                  "gptel-session"
     92                                                generated))
     93                                   (timestamp (format-time-string "%Y%m%dT%H%M%S"))
     94                                   (dir (expand-file-name "~/gptel/sessions/"))
     95                                   (base (concat timestamp "--" generated))
     96                                   (file (expand-file-name (concat base ".org") dir)))
     97                              ;; handle name collision
     98                              (when (file-exists-p file)
     99                                (setq base (concat timestamp "-" generated "-" (format-time-string "%S")))
    100                                (setq file (expand-file-name (concat base ".org") dir)))
    101                              (unless (file-directory-p dir)
    102                                (make-directory dir t))
    103                              ;; write-file triggers gptel--save-state to preserve metadata
    104                              (with-current-buffer buf
    105                                (write-file file))
    106                              (if (file-exists-p file)
    107                                  (progn
    108                                    (pop-to-buffer (find-file-noselect file))
    109                                    (message "Saved gptel session to %s" file))
    110                                (message "ERROR: File was not created: %s" file)))))
    111                        (error (message "gptel save-session error: %S" err)))))))
    112         (unless (plist-get (gptel-fsm-info fsm) :callback)
    113           (message "WARNING: gptel-request did not register callback!"))))))
    114 
    115 (my-use-package gptel-agent my-enable-unessentials
    116   :after gptel
    117   :demand t)
    118 
    119 (my-use-package gptel-magit my-enable-unessentials
    120   :straight
    121   (gptel-magit :fork (:host github :repo "ArthurHeymans/gptel-magit"))
    122   :hook
    123   (magit-mode . gptel-magit-install)
    124   :init
    125   (with-eval-after-load 'gptel
    126     (gptel-make-openai "gptel-Kimi-Code-magit"
    127       :host "api.kimi.com"
    128       :endpoint "/coding/v1/chat/completions"
    129       :protocol "https"
    130       :key (lambda ()
    131              (auth-info-password (car (auth-source-search :host "api.kimi.com" :user "apikey"))))
    132       :stream t
    133       :models '((kimi-for-coding))
    134       :request-params '(:temperature 0.6 :thinking (:type "disabled"))
    135       :header (lambda (_)
    136                 (when-let* ((key (gptel--get-api-key)))
    137                   `(("User-Agent" . "RooCode/3.30.3")
    138                     ("HTTP-Referer" . "https://github.com/RooVetGit/Roo-Cline")
    139                     ("X-Title" . "Roo Code")
    140                     ("Authorization" . ,(concat "Bearer " key))))))
    141 
    142     (setq gptel-magit-model 'kimi-for-coding
    143           gptel-magit-backend (gptel-get-backend "gptel-Kimi-Code-magit"))))
    144 
    145 ;; pi-coding-agent
    146 (my-use-package pilish my-enable-unessentials
    147   :init
    148   (defun my-pilish ()
    149     "Run `pilish' in project root."
    150     (interactive)
    151     (when-let* ((proj (project-current t))
    152                 (default-directory (project-root proj)))
    153       (pilish)))
    154 
    155   :config
    156   (add-hook 'pilish-session-browser-mode-hook
    157             (lambda ()
    158               (setq pilish--session-browser-sort "recent"
    159                     pilish--session-browser-view 'recent)
    160               (pilish--session-browser-rerender)))
    161 
    162   (setq pilish-thinking-display 'hidden))
    163 
    164 (provide 'init-gptel)
    165 ;;; init-gptel.el ends here