Files

206 lines
8.0 KiB
EmacsLisp

;;; ai/gptel.el -*- lexical-binding: t; -*-
;;; ============================================================================
;;; gptel Configuration
;;; ============================================================================
;; Core gptel setup
(use-package! gptel
:defer t
:config
;; Default backend: OpenRouter
(setq gptel-model 'google/gemini-3-flash-preview
gptel-backend
(gptel-make-openai "OpenRouter"
:host "openrouter.ai"
:endpoint "/api/v1/chat/completions"
:stream t
:key "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"
:models '(google/gemini-3-flash-preview
x-ai/grok-4.1-fast
x-ai/grok-code-fast-1
google/gemini-3-pro-preview
anthropic/claude-sonnet-4.5)))
;; Use org-mode by default for better note-taking
(setq gptel-default-mode 'org-mode)
;; Move cursor to end of response automatically
(add-hook 'gptel-post-response-functions 'gptel-end-of-response)
;; Keybindings - using SPC A for AI (uppercase to avoid conflicts)
(map! :leader
(:prefix ("A" . "AI/gptel")
:desc "gptel chat" "g" #'gptel
:desc "gptel send" "s" #'gptel-send
:desc "gptel menu" "m" #'gptel-menu
:desc "gptel rewrite" "r" #'gptel-rewrite
:desc "Add context" "c" #'gptel-add
:desc "Add file to context" "f" #'gptel-add-file))
;; Additional keybindings in gptel buffer
(map! :map gptel-mode-map
"C-c RET" #'gptel-send
"C-c C-k" #'gptel-abort
"C-c C-c" #'gptel-menu))
;; gptel presets for different tasks
(after! gptel
;; Coding assistant preset
(gptel-make-preset 'coder
:description "Expert coding assistant"
:backend "OpenRouter"
:model 'anthropic/claude-sonnet-4.5
:system "You are an expert coding assistant. Provide high-quality code solutions, refactorings, and explanations. Be concise but thorough. Include error handling and follow best practices.")
;; Code explainer preset
(gptel-make-preset 'explain
:description "Explain code to beginners"
:system "Explain this code to a novice programmer. Use simple language and break down complex concepts.")
;; Refactoring preset
(gptel-make-preset 'refactor
:description "Code refactoring expert"
:model 'anthropic/claude-sonnet-4.5
:system "You are a code refactoring expert. Improve code quality, readability, and performance while maintaining functionality. Explain your changes.")
;; Quick answers preset
(gptel-make-preset 'quick
:description "Quick, concise answers"
:model 'google/gemini-3-flash-preview
:system "Provide quick, concise answers. Be direct and to the point.")
;; Documentation writer preset
(gptel-make-preset 'docs
:description "Documentation writer"
:system "Write clear, comprehensive documentation. Include examples and explain edge cases."))
;;; ============================================================================
;;; gptel-quick - Quick lookups and explanations
;;; ============================================================================
(use-package! gptel-quick
:after gptel
:defer t
:config
;; Include context from gptel-add if available
(setq gptel-quick-use-context t)
(map! :leader
(:prefix "A"
:desc "Quick lookup" "q" #'gptel-quick)))
;;; ============================================================================
;;; gptel-extensions - Extra utility functions
;;; ============================================================================
(use-package! gptel-extensions
:after gptel
:defer t
:config
(map! :leader
(:prefix "A"
:desc "Load buffer into session" "l" #'gptel-extensions-load-buffer
:desc "Send whole buffer" "b" #'gptel-extensions-send-whole-buffer
:desc "Refactor region" "R" #'gptel-extensions-refactor)))
;;; ============================================================================
;;; gptel-autocomplete - Inline code completion
;;; ============================================================================
(use-package! gptel-autocomplete
:after gptel
:defer t
:commands (gptel-complete gptel-accept-completion)
:config
;; Configure context size
(setq gptel-autocomplete-before-context-lines 100
gptel-autocomplete-after-context-lines 20
gptel-autocomplete-temperature 0.1)
;; Keybindings for autocomplete
(map! :leader
(:prefix "A"
:desc "Complete at point" "C" #'gptel-complete
:desc "Accept completion" "a" #'gptel-accept-completion))
;; Also add convenient keybindings in prog-mode
(map! :map prog-mode-map
"M-TAB" #'gptel-complete
"C-c TAB" #'gptel-complete
"C-c C-a" #'gptel-accept-completion))
(defun my/gptel-quick-explain-region ()
"Quickly explain selected code region in the gptel buffer."
(interactive)
(when (use-region-p)
(let* ((code (buffer-substring-no-properties (region-beginning) (region-end)))
(buf (get-buffer-create "*gptel*")))
;; Prepare the buffer and insert the query immediately
(with-current-buffer buf
(goto-char (point-max))
(unless (derived-mode-p 'gptel-mode)
(funcall gptel-default-mode)
(gptel-mode 1))
(insert "\n\n--- Code to Explain ---\n\n" code "\n\n### Explanation Request Sent...\n"))
(display-buffer buf)
;; Send the request
(gptel-request
(format "Explain this code concisely in 2-3 sentences:\n\n%s" code)
:system "You are an expert programmer explaining code."
:callback (lambda (response info)
(with-current-buffer (get-buffer "*gptel*")
(goto-char (point-max))
(insert "\n### Code Explanation\n\n" response "\n\n"))
(message "Explanation received in *gptel* buffer."))))))
(defun my/gptel-proofread-region ()
"Proofread and improve selected text."
(interactive)
(when (use-region-p)
(gptel-rewrite-region (region-beginning) (region-end)
"Fix spelling, grammar, and improve clarity. Keep the same tone and style.")))
(defun my--get-region-as-path-tag (path-fn)
"Helper to get path tag using PATH-FN to determine the file path."
(let* ((filename (funcall path-fn))
(start-line (line-number-at-pos (if (use-region-p) (region-beginning) (point))))
(tag (if (use-region-p)
(let* ((end-pos (1- (region-end)))
(end-line (line-number-at-pos (max (region-beginning) end-pos))))
(format "@%s:%d-%d" filename start-line end-line))
(format "@%s:%d" filename start-line))))
tag))
(defun my/copy-region-as-path-tag ()
"Copy the current region as a relative path tag: @path/to/file.ext:start-end"
(interactive)
(let ((tag (my--get-region-as-path-tag
(lambda () (file-relative-name (buffer-file-name) (doom-project-root))))))
(kill-new tag)
(message "Copied: %s" tag)))
(defun my/copy-region-as-path-tag-absolute ()
"Copy the current region as an absolute path tag: @/abs/path/to/file.ext:start-end"
(interactive)
(let ((tag (my--get-region-as-path-tag #'buffer-file-name)))
(kill-new tag)
(message "Copied: %s" tag)))
;; Add keybindings for helper functions
(defvar my-ai-helper-map (make-sparse-keymap)
"Keymap for my AI helper functions.")
(global-set-key (kbd "C-c a") my-ai-helper-map)
(define-key my-ai-helper-map (kbd "e") #'my/gptel-quick-explain-region)
(define-key my-ai-helper-map (kbd "p") #'my/gptel-proofread-region)
(define-key my-ai-helper-map (kbd "t") #'my/copy-region-as-path-tag)
(define-key my-ai-helper-map (kbd "T") #'my/copy-region-as-path-tag-absolute)
(map! :leader
(:prefix "A"
:desc "Quick explain" "e" #'my/gptel-quick-explain-region
:desc "Proofread text" "p" #'my/gptel-proofread-region
:desc "Copy path tag (relative)" "t" #'my/copy-region-as-path-tag
:desc "Copy path tag (absolute)" "T" #'my/copy-region-as-path-tag-absolute))