Clean-up, add timestamps
This commit is contained in:
parent
dc40c13c12
commit
3677b889cd
3 changed files with 157 additions and 79 deletions
144
README.org
144
README.org
|
@ -40,7 +40,6 @@ Therefore (philosophy, I may change my mind later):
|
|||
- Tool names are documentation.
|
||||
- Argument names are documentation.
|
||||
- As few arguments per tool as possible.
|
||||
|
||||
** Installation:
|
||||
I only use Doom Emacs, so here's how I load:
|
||||
|
||||
|
@ -131,22 +130,18 @@ Use ~setq~ in your configuration, with a list of buffer names whose headings you
|
|||
#+end_src
|
||||
|
||||
**** result-limit
|
||||
It is with great disappointment that I admit to a terrible flaw of this package.
|
||||
|
||||
When in local use, with a large dataset, a lot of the commands which return entire org-mode entries... end up grabbing way, way too much text.
|
||||
|
||||
This can end up with Emacs freezing as it tries to parse immeasurable amounts of text (for fonts, formatting, etc) at the same time as the LLM is screeching in electric pain trying to actually process all that data.
|
||||
The stop-gap solution is to implement a hard limit on the amount of text a tool can return.
|
||||
|
||||
To that end, the stop-gap solution is to implement a hard limit on the amount of text a tool can return.
|
||||
|
||||
I don't think there's much reason to ever allow partial results, which leads me to this:
|
||||
I don't think there's much reason to ever allow partial results, which led me to this:
|
||||
|
||||
#+begin_src elisp :results none
|
||||
(defvar gptel-got-result-limit 40000)
|
||||
|
||||
(defun gptel-got--result-limit (result)
|
||||
(if (>= (length (format "%s" result)) gptel-got-result-limit)
|
||||
(format "Results over %s character. Stop. Analyze. Find a different solution, or use a more specific query." gptel-got-result-limit)
|
||||
(format "Results over %s characters. Stop. Analyze. Find a different solution, or use a more specific query." gptel-got-result-limit)
|
||||
result))
|
||||
|
||||
|
||||
|
@ -303,7 +298,7 @@ Opens a file into an inactive (background) buffer for processing.
|
|||
#+begin_src elisp
|
||||
(defun gptel-got--open-file-inactive (file)
|
||||
"Open FILE in a background buffer without modifying its contents."
|
||||
(find-file-noselect file)
|
||||
(find-file-noselect file)
|
||||
(set-buffer-modified-p nil))
|
||||
|
||||
(add-to-list 'gptel-got
|
||||
|
@ -475,9 +470,9 @@ QUERY can be any valid org-ql-select query."
|
|||
My journal is a single file, with a hierarchy like so:
|
||||
|
||||
#+begin_src org :tangle no
|
||||
,* [YYYY]
|
||||
,* [YYYY])
|
||||
,** [YYYY-MM]
|
||||
,*** [YYYY-MM-DD Day HH:MM])
|
||||
,*** [YYYY-MM-DD Day HH:MM]
|
||||
#+end_src
|
||||
|
||||
This tool /kinda sorta/ works. It has two main failure modes:
|
||||
|
@ -570,7 +565,7 @@ Retrieve the headings where the heading matches query. This is very much the sam
|
|||
(org-ql-select
|
||||
(get-buffer buf)
|
||||
`(heading ,query)
|
||||
:action #''gptel-got--heading)))
|
||||
:action #'gptel-got--heading)))
|
||||
(concat (gptel-got--result-limit result) "Results end here. Proceed with the next action."))
|
||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||
(message "Buffer '%s' does not exist." buf))))
|
||||
|
@ -887,55 +882,114 @@ This means that /every org-mode file I have/ is part of this search. If you're u
|
|||
:description "Returns entries matching regexp QUERY from all files. After using this, stop. Evaluate results for relevance, and proceed with completing user's request. The regexp *must* be in Emacs rx format!" ))
|
||||
#+end_src
|
||||
|
||||
*** End
|
||||
#+begin_src elisp
|
||||
(provide 'gptel-got)
|
||||
;;; gptel-got.el ends here
|
||||
#+end_src
|
||||
* gptel-got-qol (quality of life)
|
||||
:PROPERTIES:
|
||||
:header-args: :elisp :tangle gptel-got-qol.el
|
||||
:END:
|
||||
** Notes
|
||||
****** Utils
|
||||
These are functions/ hooks/ stuff that I'm building in an effort to improve my experience.
|
||||
** gptel-got-match-model
|
||||
******* gptel-got-match-model
|
||||
|
||||
Gets the model name from the current gptel-backend's API (should be generally openai-compatible).
|
||||
#+begin_src elisp
|
||||
#+begin_src elisp :results none
|
||||
(defun gptel-got--model ()
|
||||
"This function retrieves the model name from the GPTel backend.
|
||||
|
||||
It sends a GET request to the backend's models endpoint to fetch the
|
||||
available models. The function returns the model name (string) from the
|
||||
first entry in the backend's model list, which should be sufficient
|
||||
when ."
|
||||
|
||||
for local, single-model inference."
|
||||
(let ((result (gptel--url-retrieve (concat "http://" (gptel-backend-host gptel-backend) "/v1/models") :method "GET")))
|
||||
(plist-get (aref (plist-get result :models) 0) :model)))
|
||||
#+end_src
|
||||
(file-name-nondirectory(plist-get (aref (plist-get result :models) 0) :model))))
|
||||
|
||||
Determines which model in your gptel-backend matches the name of the model returned from the LLM API best, using Levenshtein.
|
||||
#+begin_src elisp
|
||||
(defun gptel-got-match-model ()
|
||||
(defun gptel-got--match-model ()
|
||||
"Finds the closest matching model name from the backend's model list.
|
||||
|
||||
This function uses string distance (e.g., Levenshtein distance) to compare
|
||||
the target model name (from `gptel-got--model') against all available
|
||||
models. It returns the best-matching model name (string) from the backend.
|
||||
|
||||
Intended for scenarios where the exact model name is unknown or contains
|
||||
typos. The function ensures the most similar model name is selected for
|
||||
operation by minimizing the string distance between names."
|
||||
This function uses string distance (i.e. Levenshtein distance) to compare
|
||||
the target model name (from `gptel-got--model') against all models defined
|
||||
for the backend. It returns the best-matching model name (string) from
|
||||
the backend."
|
||||
|
||||
(let ((got-model (gptel-got--model))
|
||||
(min most-positive-fixnum)
|
||||
(best-match nil))
|
||||
(cl-loop for model in (gptel-backend-models gptel-backend)
|
||||
do (let ((distance (string-distance got-model (symbol-name model)))
|
||||
(when (< distance min)
|
||||
(setq min distance)
|
||||
(setq best-match model))))
|
||||
best-match))
|
||||
do (let ((distance (string-distance got-model (symbol-name model))))
|
||||
(when (< distance min)
|
||||
(setq min distance)
|
||||
(setq best-match model))))
|
||||
best-match))
|
||||
#+end_src
|
||||
|
||||
*** Usage
|
||||
Currently none, have to hook this into =gptel-prompt-transform-functions=, and then have a model switching mechanism, possibly through presets, or direct. Both would be useful.
|
||||
The above works fine, as far as I can tell. Not perfect, but fine.
|
||||
|
||||
******** Example
|
||||
#+begin_src elisp :tangle no
|
||||
(gptel-got--model)
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
: Mistral-Small-3.2-24B-Instruct-2506-UD-Q4_K_XL.gguf
|
||||
|
||||
#+begin_src elisp :tangle no
|
||||
(gptel-got--match-model)
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
: Mistral
|
||||
|
||||
But this... just doesn't, and it's baffling.
|
||||
#+begin_src elisp :results none
|
||||
(defun gptel-got-auto-model ()
|
||||
"Automatically select a model matching models specified by `gptel-backend` by calling `gptel-got--match-model`."
|
||||
(setq gptel-model (gptel-got--match-model))
|
||||
|
||||
(unless (member #'gptel-got-auto-model gptel-prompt-transform-functions)
|
||||
(add-to-list 'gptel-prompt-transform-functions #'gptel-got-auto-model)))
|
||||
#+end_src
|
||||
|
||||
******* current_time
|
||||
I wanted to have a way for my interactions to be timestamped, so I could ask things like "what have I focused on over the last 8 hours."
|
||||
|
||||
This function (and variable) lets me do that.
|
||||
|
||||
To use, simply add this line to your buffer. The buffer won't change, but the current_time line will have a timestamp appended to it in the text sent to the inference engine.
|
||||
|
||||
#+begin_src org :tangle no
|
||||
#+current_time:
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp :results none
|
||||
(defvar gptel-got-timestamp-toggle t
|
||||
"If non-nil, enable insertion of current timestamp in prompts.")
|
||||
|
||||
(defun gptel-got--timestamp ()
|
||||
"Return current time in ISO 8601 format (YYYY-MM-DD HH:MM:SS)."
|
||||
(format-time-string "%Y-%m-%d %H:%M:%S"))
|
||||
|
||||
(defun gptel-got-timestamp-text ()
|
||||
"Insert current timestamp into buffer if gptel-got-timestamp-toggle is not NIL.
|
||||
Checks `gptel-got-timestamp-toggle`, then replaces #+current_time: placeholder
|
||||
with the current time and the formatted timestamp string."
|
||||
(when gptel-got-timestamp-toggle
|
||||
(goto-char (point-min))
|
||||
(perform-replace "\\(?:#\\+current_time:\\)"
|
||||
(concat "#+current_time: " (gptel-got--timestamp) "\n")
|
||||
nil t nil)))
|
||||
|
||||
(unless (member #'gptel-got-timestamp-text gptel-prompt-transform-functions)
|
||||
(add-to-list 'gptel-prompt-transform-functions #'gptel-got-timestamp-text))
|
||||
|
||||
#+end_src
|
||||
|
||||
|
||||
|
||||
#+begin_src elisp :tangle no
|
||||
gptel-prompt-transform-functions
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
| gptel-got-auto-model | gptel-got-timestamp-text | gptel--transform-add-context | gptel--transform-apply-preset |
|
||||
|
||||
|
||||
*** End
|
||||
#+begin_src elisp
|
||||
(provide 'gptel-got)
|
||||
;;; gptel-got.el ends here
|
||||
#+end_src
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
(defun gptel-got--model ()
|
||||
"This function retrieves the model name from the GPTel backend.
|
||||
|
||||
It sends a GET request to the backend's models endpoint to fetch the
|
||||
available models. The function returns the model name (string) from the
|
||||
first entry in the backend's model list, which should be sufficient
|
||||
when ."
|
||||
|
||||
(let ((result (gptel--url-retrieve (concat "http://" (gptel-backend-host gptel-backend) "/v1/models") :method "GET")))
|
||||
(plist-get (aref (plist-get result :models) 0) :model)))
|
||||
|
||||
(defun gptel-got-match-model ()
|
||||
"Finds the closest matching model name from the backend's model list.
|
||||
|
||||
This function uses string distance (e.g., Levenshtein distance) to compare
|
||||
the target model name (from `gptel-got--model') against all available
|
||||
models. It returns the best-matching model name (string) from the backend.
|
||||
|
||||
Intended for scenarios where the exact model name is unknown or contains
|
||||
typos. The function ensures the most similar model name is selected for
|
||||
operation by minimizing the string distance between names."
|
||||
|
||||
(let ((got-model (gptel-got--model))
|
||||
(min most-positive-fixnum)
|
||||
(best-match nil))
|
||||
(cl-loop for model in (gptel-backend-models gptel-backend)
|
||||
do (let ((distance (string-distance got-model (symbol-name model)))
|
||||
(when (< distance min)
|
||||
(setq min distance)
|
||||
(setq best-match model))))
|
||||
best-match))
|
61
gptel-got.el
61
gptel-got.el
|
@ -42,7 +42,7 @@
|
|||
|
||||
(defun gptel-got--result-limit (result)
|
||||
(if (>= (length (format "%s" result)) gptel-got-result-limit)
|
||||
(format "Results over %s character. Stop. Analyze. Find a different solution, or use a more specific query." gptel-got-result-limit)
|
||||
(format "Results over %s characters. Stop. Analyze. Find a different solution, or use a more specific query." gptel-got-result-limit)
|
||||
result))
|
||||
|
||||
(defun gptel-got--heading ()
|
||||
|
@ -107,7 +107,7 @@
|
|||
|
||||
(defun gptel-got--open-file-inactive (file)
|
||||
"Open FILE in a background buffer without modifying its contents."
|
||||
(find-file-noselect file)
|
||||
(find-file-noselect file)
|
||||
(set-buffer-modified-p nil))
|
||||
|
||||
(add-to-list 'gptel-got
|
||||
|
@ -265,7 +265,7 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
|||
(org-ql-select
|
||||
(get-buffer buf)
|
||||
`(heading ,query)
|
||||
:action #''gptel-got--heading)))
|
||||
:action #'gptel-got--heading)))
|
||||
(concat (gptel-got--result-limit result) "Results end here. Proceed with the next action."))
|
||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||
(message "Buffer '%s' does not exist." buf))))
|
||||
|
@ -419,5 +419,60 @@ After using this, stop. Evaluate the relevance of the results. Then continue."))
|
|||
:category "org-ql"
|
||||
:description "Returns entries (heading and body) matching QUERY from BUFFER. This may pull too many results, only use if other tools fail. After using this, stop. Evaluate results. If necessary, re-plan. Only then proceed."))
|
||||
|
||||
(defun gptel-got--model ()
|
||||
"This function retrieves the model name from the GPTel backend.
|
||||
|
||||
It sends a GET request to the backend's models endpoint to fetch the
|
||||
available models. The function returns the model name (string) from the
|
||||
first entry in the backend's model list, which should be sufficient
|
||||
for local, single-model inference."
|
||||
(let ((result (gptel--url-retrieve (concat "http://" (gptel-backend-host gptel-backend) "/v1/models") :method "GET")))
|
||||
(file-name-nondirectory(plist-get (aref (plist-get result :models) 0) :model))))
|
||||
|
||||
(defun gptel-got--match-model ()
|
||||
"Finds the closest matching model name from the backend's model list.
|
||||
|
||||
This function uses string distance (i.e. Levenshtein distance) to compare
|
||||
the target model name (from `gptel-got--model') against all models defined
|
||||
for the backend. It returns the best-matching model name (string) from
|
||||
the backend."
|
||||
|
||||
(let ((got-model (gptel-got--model))
|
||||
(min most-positive-fixnum)
|
||||
(best-match nil))
|
||||
(cl-loop for model in (gptel-backend-models gptel-backend)
|
||||
do (let ((distance (string-distance got-model (symbol-name model))))
|
||||
(when (< distance min)
|
||||
(setq min distance)
|
||||
(setq best-match model))))
|
||||
best-match))
|
||||
|
||||
(defun gptel-got-auto-model ()
|
||||
"Automatically select a model matching models specified by `gptel-backend` by calling `gptel-got--match-model`."
|
||||
(setq gptel-model (gptel-got--match-model))
|
||||
|
||||
(unless (member #'gptel-got-auto-model gptel-prompt-transform-functions)
|
||||
(add-to-list 'gptel-prompt-transform-functions #'gptel-got-auto-model)))
|
||||
|
||||
(defvar gptel-got-timestamp-toggle t
|
||||
"If non-nil, enable insertion of current timestamp in prompts.")
|
||||
|
||||
(defun gptel-got--timestamp ()
|
||||
"Return current time in ISO 8601 format (YYYY-MM-DD HH:MM:SS)."
|
||||
(format-time-string "%Y-%m-%d %H:%M:%S"))
|
||||
|
||||
(defun gptel-got-timestamp-text ()
|
||||
"Insert current timestamp into buffer if gptel-got-timestamp-toggle is not NIL.
|
||||
Checks `gptel-got-timestamp-toggle`, then replaces #+current_time: placeholder
|
||||
with the current time and the formatted timestamp string."
|
||||
(when gptel-got-timestamp-toggle
|
||||
(goto-char (point-min))
|
||||
(perform-replace "\\(?:#\\+current_time:\\)"
|
||||
(concat "#+current_time: " (gptel-got--timestamp) "\n")
|
||||
nil t nil)))
|
||||
|
||||
(unless (member #'gptel-got-timestamp-text gptel-prompt-transform-functions)
|
||||
(add-to-list 'gptel-prompt-transform-functions #'gptel-got-timestamp-text))
|
||||
|
||||
(provide 'gptel-got)
|
||||
;;; gptel-got.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue