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