Skip to content

Commit 9077a8b

Browse files
committed
gptel: Extend save/restore state to support tools (#963)
- Extend saved chat state to include gptel tools. Active tool names are written to and read from Org properties (in Org mode) and file-local variables (elsewhere). - Additionally, handle "deletions" correctly. That is, remove existing Org properties and file-local variables for settings that are set to nil. For some settings (like the backend/model) a nil value does not make sense and is ignored. The system message could be nil and this edge case is not yet handled. Other persisted settings are handled correctly. * gptel-org.el (gptel-org--send-with-props) (gptel-org--entry-properties, gptel-org--restore-state) (gptel-org-set-properties): Handle the GPTEL_TOOLS property. * gptel.el (gptel--tool-names, gptel--backend-name) (gptel--restore-state, gptel--save-state): Use a new variable `gptel--tool-names' to persist gptel tool names for the chat session.
1 parent fcf7c9f commit 9077a8b

File tree

2 files changed

+69
-26
lines changed

2 files changed

+69
-26
lines changed

gptel-org.el

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,13 @@ parameters.
424424
ARGS are the original function call arguments."
425425
(if (derived-mode-p 'org-mode)
426426
(pcase-let ((`(,gptel--system-message ,gptel-backend ,gptel-model
427-
,gptel-temperature ,gptel-max-tokens)
427+
,gptel-temperature ,gptel-max-tokens
428+
,gptel--num-messages-to-send ,gptel-tools)
428429
(seq-mapn (lambda (a b) (or a b))
429430
(gptel-org--entry-properties)
430431
(list gptel--system-message gptel-backend gptel-model
431-
gptel-temperature gptel-max-tokens))))
432+
gptel-temperature gptel-max-tokens
433+
gptel--num-messages-to-send gptel-tools))))
432434
(apply send-fun args))
433435
(apply send-fun args)))
434436

@@ -445,12 +447,12 @@ ARGS are the original function call arguments."
445447
(defun gptel-org--entry-properties (&optional pt)
446448
"Find gptel configuration properties stored at PT."
447449
(pcase-let
448-
((`(,system ,backend ,model ,temperature ,tokens ,num)
450+
((`(,system ,backend ,model ,temperature ,tokens ,num ,tools)
449451
(mapcar
450452
(lambda (prop) (org-entry-get (or pt (point)) prop 'selective))
451453
'("GPTEL_SYSTEM" "GPTEL_BACKEND" "GPTEL_MODEL"
452454
"GPTEL_TEMPERATURE" "GPTEL_MAX_TOKENS"
453-
"GPTEL_NUM_MESSAGES_TO_SEND"))))
455+
"GPTEL_NUM_MESSAGES_TO_SEND" "GPTEL_TOOLS"))))
454456
(when system
455457
(setq system (string-replace "\\n" "\n" system)))
456458
(when backend
@@ -461,7 +463,16 @@ ARGS are the original function call arguments."
461463
(setq temperature (gptel--to-number temperature)))
462464
(when tokens (setq tokens (gptel--to-number tokens)))
463465
(when num (setq num (gptel--to-number num)))
464-
(list system backend model temperature tokens num)))
466+
(when tools
467+
(setq tools (cl-loop
468+
for tname in (split-string tools)
469+
for tool = (with-demoted-errors "gptel: %S"
470+
(gptel-get-tool tname))
471+
if tool collect tool else do
472+
(display-warning
473+
'(gptel org tools)
474+
(format "Tool %s not found, ignoring" tname)))))
475+
(list system backend model temperature tokens num tools)))
465476

466477
(defun gptel-org--restore-state ()
467478
"Restore gptel state for Org buffers when turning on `gptel-mode'."
@@ -472,7 +483,7 @@ ARGS are the original function call arguments."
472483
(progn
473484
(when-let* ((bounds (org-entry-get (point-min) "GPTEL_BOUNDS")))
474485
(gptel--restore-props (read bounds)))
475-
(pcase-let ((`(,system ,backend ,model ,temperature ,tokens ,num)
486+
(pcase-let ((`(,system ,backend ,model ,temperature ,tokens ,num ,tools)
476487
(gptel-org--entry-properties (point-min))))
477488
(when system (setq-local gptel--system-message system))
478489
(if backend (setq-local gptel-backend backend)
@@ -486,7 +497,8 @@ ARGS are the original function call arguments."
486497
(when model (setq-local gptel-model model))
487498
(when temperature (setq-local gptel-temperature temperature))
488499
(when tokens (setq-local gptel-max-tokens tokens))
489-
(when num (setq-local gptel--num-messages-to-send num))))
500+
(when num (setq-local gptel--num-messages-to-send num))
501+
(when tools (setq-local gptel-tools tools))))
490502
(:success (message "gptel chat restored."))
491503
(error (message "Could not restore gptel state, sorry! Error: %s" status)))
492504
(set-buffer-modified-p modified))))
@@ -503,20 +515,27 @@ non-nil (default), display a message afterwards."
503515
(interactive (list (point) t))
504516
(org-entry-put pt "GPTEL_MODEL" (gptel--model-name gptel-model))
505517
(org-entry-put pt "GPTEL_BACKEND" (gptel-backend-name gptel-backend))
506-
(unless (equal (default-value 'gptel-temperature) gptel-temperature)
507-
(org-entry-put pt "GPTEL_TEMPERATURE"
508-
(number-to-string gptel-temperature)))
509-
(when (natnump gptel--num-messages-to-send)
510-
(org-entry-put pt "GPTEL_NUM_MESSAGES_TO_SEND"
511-
(number-to-string gptel--num-messages-to-send)))
512-
(org-entry-put pt "GPTEL_SYSTEM"
518+
(org-entry-put pt "GPTEL_SYSTEM" ;TODO: Handle nil case correctly
513519
(and-let* ((msg (car-safe
514520
(gptel--parse-directive
515521
gptel--system-message))))
516522
(string-replace "\n" "\\n" msg)))
517-
(when gptel-max-tokens
518-
(org-entry-put
519-
pt "GPTEL_MAX_TOKENS" (number-to-string gptel-max-tokens)))
523+
(if gptel-tools
524+
(org-entry-put
525+
pt "GPTEL_TOOLS" (mapconcat #'gptel-tool-name gptel-tools " "))
526+
(org-entry-delete pt "GPTEL_TOOLS"))
527+
(if (equal (default-value 'gptel-temperature) gptel-temperature)
528+
(org-entry-delete pt "GPTEL_TEMPERATURE")
529+
(org-entry-put pt "GPTEL_TEMPERATURE"
530+
(number-to-string gptel-temperature)))
531+
(if (natnump gptel--num-messages-to-send)
532+
(org-entry-put pt "GPTEL_NUM_MESSAGES_TO_SEND"
533+
(number-to-string gptel--num-messages-to-send))
534+
(org-entry-delete pt "GPTEL_NUM_MESSAGES_TO_SEND"))
535+
(if gptel-max-tokens
536+
(org-entry-put
537+
pt "GPTEL_MAX_TOKENS" (number-to-string gptel-max-tokens))
538+
(org-entry-delete pt "GPTEL_MAX_TOKENS"))
520539
(when msg
521540
(message "Added gptel configuration to current headline.")))
522541

gptel.el

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,13 @@ This opens up advanced options in `gptel-menu'.")
802802
(defvar gptel--num-messages-to-send nil)
803803
(put 'gptel--num-messages-to-send 'safe-local-variable #'always)
804804

805+
(defvar-local gptel--tool-names nil
806+
"Store to persist tool names to file across Emacs sessions.
807+
808+
Note: Changing this variable does not affect gptel\\='s behavior
809+
in any way.")
810+
(put 'gptel--backend-name 'safe-local-variable #'always)
811+
805812
(defcustom gptel-log-level nil
806813
"Logging level for gptel.
807814
@@ -1429,7 +1436,17 @@ applied before being re-persisted in the new structure."
14291436
"Could not activate gptel backend \"%s\"! "
14301437
"Switch backends with \\[universal-argument] \\[gptel-send]"
14311438
" before using gptel."))
1432-
gptel--backend-name))))))
1439+
gptel--backend-name)))
1440+
(when gptel--tool-names
1441+
(if-let* ((tools (cl-loop
1442+
for tname in gptel--tool-names
1443+
for tool = (with-demoted-errors "gptel: %S"
1444+
(gptel-get-tool tname))
1445+
if tool collect tool else do
1446+
(display-warning
1447+
'(gptel org tools)
1448+
(format "Tool %s not found, ignoring" tname)))))
1449+
(setq-local gptel-tools tools))))))
14331450

14341451
(defun gptel--save-state ()
14351452
"Write the gptel state to the buffer.
@@ -1448,18 +1465,25 @@ file."
14481465
(add-file-local-variable 'gptel-model gptel-model)
14491466
(add-file-local-variable 'gptel--backend-name
14501467
(gptel-backend-name gptel-backend))
1451-
(unless (equal (default-value 'gptel-temperature) gptel-temperature)
1452-
(add-file-local-variable 'gptel-temperature gptel-temperature))
14531468
(unless (equal (default-value 'gptel--system-message)
14541469
gptel--system-message)
14551470
(add-file-local-variable
1456-
'gptel--system-message
1471+
'gptel--system-message ;TODO: Handle nil case correctly
14571472
(car-safe (gptel--parse-directive gptel--system-message))))
1458-
(when gptel-max-tokens
1459-
(add-file-local-variable 'gptel-max-tokens gptel-max-tokens))
1460-
(when (natnump gptel--num-messages-to-send)
1461-
(add-file-local-variable 'gptel--num-messages-to-send
1462-
gptel--num-messages-to-send))
1473+
(if gptel-tools
1474+
(add-file-local-variable
1475+
'gptel--tool-names (mapcar #'gptel-tool-name gptel-tools))
1476+
(delete-file-local-variable 'gptel--tool-names))
1477+
(if (equal (default-value 'gptel-temperature) gptel-temperature)
1478+
(delete-file-local-variable 'gptel-temperature)
1479+
(add-file-local-variable 'gptel-temperature gptel-temperature))
1480+
(if gptel-max-tokens
1481+
(add-file-local-variable 'gptel-max-tokens gptel-max-tokens)
1482+
(delete-file-local-variable 'gptel-max-tokens))
1483+
(if (natnump gptel--num-messages-to-send)
1484+
(add-file-local-variable 'gptel--num-messages-to-send
1485+
gptel--num-messages-to-send)
1486+
(delete-file-local-variable 'gptel--num-messages-to-send))
14631487
(add-file-local-variable 'gptel--bounds (gptel--get-buffer-bounds)))))))
14641488

14651489

0 commit comments

Comments
 (0)