2026-03-23 15:56:17 +08:00
;;; ai/gptel.el -*- lexical-binding: t; -*-
2025-10-19 21:54:29 +08:00
2025-10-19 22:16:22 +08:00
;;; ============================================================================
;;; gptel Configuration
;;; ============================================================================
;; Core gptel setup
( use-package! gptel
2025-11-22 13:52:03 +08:00
:defer t
2025-10-19 22:16:22 +08:00
:config
;; Default backend: OpenRouter
2026-03-23 15:34:41 +08:00
( setq gptel-model 'google/gemini-3-flash-preview
2025-10-19 22:16:22 +08:00
gptel-backend
( gptel-make-openai "OpenRouter"
:host "openrouter.ai"
:endpoint "/api/v1/chat/completions"
:stream t
:key "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"
2026-03-23 15:34:41 +08:00
:models ' ( google/gemini-3-flash-preview
2025-11-22 20:13:14 +08:00
x-ai/grok-4.1-fast
x-ai/grok-code-fast-1
google/gemini-3-pro-preview
2025-10-19 22:16:22 +08:00
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 )
2025-11-07 01:26:43 +08:00
;; Keybindings - using SPC A for AI (uppercase to avoid conflicts)
2025-10-19 22:16:22 +08:00
( map! :leader
2025-11-07 01:26:43 +08:00
( :prefix ( "A" . "AI/gptel" )
2025-10-19 22:16:22 +08:00
: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
2026-03-21 12:23:07 +08:00
: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." )
2025-10-19 22:16:22 +08:00
;; Code explainer preset
( gptel-make-preset 'explain
2026-03-21 12:23:07 +08:00
:description "Explain code to beginners"
:system "Explain this code to a novice programmer. Use simple language and break down complex concepts." )
2025-10-19 22:16:22 +08:00
;; Refactoring preset
( gptel-make-preset 'refactor
2026-03-21 12:23:07 +08:00
: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." )
2025-10-19 22:16:22 +08:00
;; Quick answers preset
( gptel-make-preset 'quick
2026-03-21 12:23:07 +08:00
:description "Quick, concise answers"
2026-03-23 15:34:41 +08:00
:model 'google/gemini-3-flash-preview
2026-03-21 12:23:07 +08:00
:system "Provide quick, concise answers. Be direct and to the point." )
2025-10-19 22:16:22 +08:00
;; Documentation writer preset
( gptel-make-preset 'docs
2026-03-21 12:23:07 +08:00
:description "Documentation writer"
:system "Write clear, comprehensive documentation. Include examples and explain edge cases." ))
2025-10-19 22:16:22 +08:00
;;; ============================================================================
;;; gptel-quick - Quick lookups and explanations
;;; ============================================================================
( use-package! gptel-quick
:after gptel
2025-11-22 13:52:03 +08:00
:defer t
2025-10-19 22:16:22 +08:00
:config
;; Include context from gptel-add if available
( setq gptel-quick-use-context t )
( map! :leader
2025-11-07 01:26:43 +08:00
( :prefix "A"
2025-10-19 22:16:22 +08:00
:desc "Quick lookup" "q" #' gptel-quick )))
;;; ============================================================================
;;; gptel-extensions - Extra utility functions
;;; ============================================================================
( use-package! gptel-extensions
:after gptel
2025-11-22 13:52:03 +08:00
:defer t
2025-10-19 22:16:22 +08:00
:config
( map! :leader
2025-11-07 01:26:43 +08:00
( :prefix "A"
2025-10-19 22:16:22 +08:00
: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
2025-11-22 13:52:03 +08:00
:defer t
2025-11-07 01:26:43 +08:00
:commands ( gptel-complete gptel-accept-completion )
2025-10-19 22:16:22 +08:00
:config
2025-11-07 01:26:43 +08:00
;; Configure context size
( setq gptel-autocomplete-before-context-lines 100
gptel-autocomplete-after-context-lines 20
gptel-autocomplete-temperature 0.1 )
2025-10-19 22:16:22 +08:00
2025-11-07 01:26:43 +08:00
;; Keybindings for autocomplete
( map! :leader
( :prefix "A"
:desc "Complete at point" "C" #' gptel-complete
:desc "Accept completion" "a" #' gptel-accept-completion ))
2025-10-19 22:16:22 +08:00
2025-11-07 01:26:43 +08:00
;; 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 ))
2025-10-19 22:16:22 +08:00
( defun my/gptel-quick-explain-region ()
2026-03-23 15:44:02 +08:00
"Quickly explain selected code region in the gptel buffer."
2025-10-19 22:16:22 +08:00
( interactive )
( when ( use-region-p )
2026-03-23 15:44:02 +08:00
( 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."
2026-03-21 12:23:07 +08:00
:callback ( lambda ( response info )
2026-03-23 15:44:02 +08:00
( 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." ))))))
2025-10-19 22:16:22 +08:00
( defun my/gptel-proofread-region ()
"Proofread and improve selected text."
( interactive )
( when ( use-region-p )
( gptel-rewrite-region ( region-beginning ) ( region-end )
2025-11-22 20:13:14 +08:00
"Fix spelling, grammar, and improve clarity. Keep the same tone and style." )))
2025-10-19 22:16:22 +08:00
2026-03-23 15:39:10 +08:00
( 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 ))
2026-03-23 15:36:58 +08:00
( defun my/copy-region-as-path-tag ()
2026-03-23 15:39:10 +08:00
"Copy the current region as a relative path tag: @path/to/file.ext:start-end"
2026-03-23 15:36:58 +08:00
( interactive )
2026-03-23 15:39:10 +08:00
( 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 )))
2026-03-23 15:36:58 +08:00
2025-10-19 22:16:22 +08:00
;; Add keybindings for helper functions
2026-03-23 15:40:44 +08:00
( 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 )
2025-10-19 22:16:22 +08:00
( map! :leader
2025-11-07 01:26:43 +08:00
( :prefix "A"
2025-10-19 22:16:22 +08:00
:desc "Quick explain" "e" #' my/gptel-quick-explain-region
2026-03-23 15:36:58 +08:00
:desc "Proofread text" "p" #' my/gptel-proofread-region
2026-03-23 15:39:10 +08:00
: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 ))