Files
confi-emacs-actual/configs/init-ai.el

208 lines
8.0 KiB
EmacsLisp

;;; init-ai.el --- Configuración de inteligencias artificales -*- lexical-binding: t -*-
;; Author: kj <webmaster@outcontrol.net>
;; URL: https://git.kj2.me/kj/confi-emacs-actual
;;; Commentary:
;; Configuración para Inteligencia artifical en Emacs.
;;; Code:
;; Cliente LLM (ollama, chatgpt, gemini, etc.)
(use-package gptel
:defer nil
:config
(when (getenv "GEMINI_KEY")
(gptel-make-gemini "Gemini"
:key (getenv "GEMINI_KEY")
:stream t))
(gptel-make-openai "llama.cpp"
:stream t
:protocol "http"
:host "127.0.0.1:1945"
:models '("Qwen3.8-27B"
"gemma-4-12B"
"gemma-4-E2B"
"gemma-4-E4B"
"gptoss-20b"))
(gptel-make-ollama "Ollama"
:host "localhost:11434"
:stream t
:request-params '(:think :json-false)
:models '("gemma4:31b-cloud"
"glm-5.1:cloud"
"kimi-k2.6:cloud"
"gpt-oss:20b-cloud"
"minimax-m2.5:cloud"
"qwen3-coder-next:cloud"))
;; Backend opencode-go: suscripción OpenCode Go vía endpoint OpenAI-compatible
(defun kj-opencode-go-key ()
"Return the OpenCode Go API key from opencode's auth file."
(let ((auth (with-temp-buffer
(insert-file-contents "~/.local/share/opencode/auth.json")
(json-parse-buffer :object-type 'alist))))
(alist-get 'key (alist-get 'opencode-go auth))))
(gptel-make-openai "OpenCode Go"
:host "opencode.ai"
:endpoint "/zen/go/v1/chat/completions"
:key #'kj-opencode-go-key
:stream t
:models '(minimax-m3 minimax-m2.7 minimax-m2.5
kimi-k3 kimi-k2.7-code kimi-k2.6 longcat-2.0 kimi-k2.5
glm-5.3-flash glm-5.3 glm-5.2 glm-5.1 glm-5
deepseek-v4-pro deepseek-v4-flash deepseek-v4-flash-vision-exp
qwen3.8-max qwen3.7-max qwen3.7-plus qwen3.6-plus qwen3.5-plus
mimo-v2-pro mimo-v2-omni mimo-v2.5-pro mimo-v2.5
hy3 hy3-preview
gpt-5.6-luna grok-4.5 grok-4.6
muse-spark-1.2-contributor))
;; Modelos gratuitos de OpenCode Go, servidos vía el endpoint /zen/v1
(gptel-make-openai "OpenCode Free"
:host "opencode.ai"
:endpoint "/zen/v1/chat/completions"
:key #'kj-opencode-go-key
:stream t
:models '(big-pickle deepseek-v4-flash-free muse-spark-1.2-contributor-free
mimo-v2.5-free hy3-free nemotron-3-ultra-free
nemotron-3.5-lightning-free laguna-s-2.1-free))
;; Backend y modelo por defecto
(setq gptel-backend (gptel-get-backend "OpenCode Go")
gptel-model 'mimo-v2.5)
(setq gptel-default-mode 'markdown-mode
gptel-prompt-prefix-alist
'((markdown-mode . "# User\n\n")
(org-mode . "* User\n\n")
(text-mode . "# User\n\n"))
gptel-response-prefix-alist
'((markdown-mode . "# AI\n\n")
(org-mode . "* AI\n\n")
(text-mode . "# AI\n\n"))
gptel-directives
'((default . "You are a large language model living in Emacs and a helpful assistant. Respond concisely in Spanish."))
)
(add-hook 'gptel-post-response-functions 'gptel-end-of-response)
;; El "thinking" de los modelos se muestra en los buffers gptel.
;; `ignore' (valor por defecto de gptel) lo muestra pero NO lo reenvía
;; al modelo en turnos posteriores (recomendado: evita llenar el
;; contexto, ver NEWS de gptel). Usar `t' si además se quiere
;; reenviarlo y `nil' para deshabilitarlo (útil para respuestas mas rápidas).
(setopt gptel-include-reasoning 'ignore)
(defun gptel-switch+model ()
"Switch to gptel backend and model in a single completion prompt."
(interactive)
(let (choices)
(dolist (pair gptel--known-backends)
(let* ((backend-name (car pair))
(backend (cdr pair))
(models
(and (fboundp 'gptel-backend-models)
(gptel-backend-models backend))))
(when models
(dolist (model models)
(push (cons ; (format "%s:%s" backend-name model)
(format "%s → %s"
(propertize backend-name 'face 'font-lock-keyword-face)
(propertize (symbol-name model) 'face 'font-lock-function-name-face))
(cons backend-name model))
choices)))))
(let* ((choice
(completing-read "Model: " (mapcar #'car choices) nil t))
(sel (cdr (assoc choice choices))))
(setq gptel-backend (cdr (assoc (car sel) gptel--known-backends))
gptel-model (cdr sel))
(message "gptel set to %s:%s" (car sel) (cdr sel)))))
)
;; (use-package copilot
;; :hook (prog-mode . copilot-mode)
;; :bind (:map copilot-completion-map
;; ("C-g" . 'copilot-clear-overlay)
;; ("C-<return>" . 'copilot-accept-completion)
;; ("S-<return>" . 'copilot-accept-completion-by-word)))
(use-package gptel-magit
:ensure t
:hook (magit-mode . gptel-magit-install)
:config
;; gptel-magit no sabe manejar el contenido de "reasoning": gptel entrega
;; el thinking al callback como (reasoning . TEXTO) y gptel-magit lo
;; inserta como si fuera la respuesta final, fallando con
;; Wrong type argument: char-or-string-p, (reasoning . "...")
;; (https://github.com/ragnard/gptel-magit/issues/8). Esto ocurre
;; aunque `gptel-include-reasoning' sea nil, porque gptel entrega las
;; celdas (reasoning . ...) al callback de forma incondicional (solo
;; controla si se insertan en el buffer gptel y si se reenvían).
;; En vez de deshabilitar el thinking globalmente, sanitizamos la
;; respuesta en el callback: se descartan las celdas de reasoning y solo
;; se deja pasar la respuesta final (string). Así el thinking sigue
;; activo en las sesiones gptel normales.
(defun kj-gptel-magit--string-response-callback (callback)
"Return a callback that forwards only string responses to CALLBACK.
Discards the (reasoning . TEXT) cells through which gptel delivers the
thinking content to the callback (see `gptel-request')."
(lambda (response &rest args)
(when (stringp response)
(apply callback response args))))
(defun kj-gptel-magit--sanitize-callback (orig-fn &rest args)
"Around advice for `gptel-magit--request'.
Wraps the :callback argument of ARGS so that responses with reasoning
content do not break gptel-magit."
(let ((pos (cl-position :callback args)))
(when (and pos (functionp (nth (1+ pos) args)))
(setf (nth (1+ pos) args)
(kj-gptel-magit--string-response-callback
(nth (1+ pos) args)))))
(apply orig-fn args))
(advice-add 'gptel-magit--request :around
#'kj-gptel-magit--sanitize-callback))
(use-package gptel-autocomplete
:defer nil
:ensure (:host github :repo "JDNdeveloper/gptel-autocomplete")
:bind (("M-<return>" . gptel-complete)
:map gptel-autocomplete-completion-map
("C-<return>" . gptel-accept-completion)))
(use-package macher
:ensure (:host github :repo "kmontag/macher")
:custom
(macher-action-buffer-ui 'org))
;; agent-shell: interfaz nativa de Emacs para interactuar con agentes ACP
(use-package shell-maker)
(use-package acp)
(use-package agent-shell
:after (shell-maker acp)
:hook (elpaca-after-init . (lambda () (defalias 'opencode #'agent-shell-opencode-start-agent)))
:config
(setq agent-shell-opencode-authentication
(agent-shell-opencode-make-authentication :none t))
(setq agent-shell-opencode-default-model-id "opencode-go/mimo-v2.5")
;; Header compacto
(setq agent-shell-header-style 'text)
;; Restaurar sesiones con texto completo
(setq agent-shell-session-restore-verbosity 'full)
;; Desactivar guardado de transcripciones y prompt al cerrar
(setq agent-shell-transcript-file-path-function nil)
(setq shell-maker-prompt-before-killing-buffer nil))
(provide 'init-ai)
;;; init-ai.el ends here