Skip to content

Commit 04187e2

Browse files
committed
basic
1 parent f550c82 commit 04187e2

File tree

1 file changed

+119
-25
lines changed

1 file changed

+119
-25
lines changed

chatgpt.el

Lines changed: 119 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
(require 'cl-lib)
3535

3636
(require 'openai)
37+
(require 'let-alist)
3738
(require 'ht)
3839

3940
(defgroup chatgpt nil
@@ -42,6 +43,11 @@
4243
:group 'comm
4344
:link '(url-link :tag "Repository" "https://github.com/emacs-openai/chatgpt"))
4445

46+
(defcustom chatgpt-model "gpt-3.5-turbo"
47+
"Model to use for chat."
48+
:type 'string
49+
:group 'chatgpt)
50+
4551
(defcustom chatgpt-max-tokens 4000
4652
"The maximum number of tokens to generate in the completion."
4753
:type 'integer
@@ -67,6 +73,12 @@
6773
(defvar-local chatgpt-requesting-p nil
6874
"Non-nil when requesting; waiting for the response.")
6975

76+
(defvar-local chatgpt-data (ht-create)
77+
"Store other information other than messages.")
78+
79+
(defvar-local chatgpt--display-pointer 0
80+
"Display pointer.")
81+
7082
;;
7183
;;; Util
7284

@@ -84,13 +96,23 @@ Display buffer from BUFFER-OR-NAME."
8496
openai-user))
8597

8698
;;
87-
;;; Core
99+
;;; Instances
100+
101+
(defmacro chatgpt-with-instance (instance &rest body)
102+
"Execute BODY within instance."
103+
(declare (indent 1))
104+
`(when-let ((buffer (and ,instance
105+
(get-buffer (cdr ,instance)))))
106+
(with-current-buffer buffer
107+
(let ((inhibit-read-only t))
108+
,@body))))
88109

89110
(defun chatgpt--live-instances ()
90111
"Return a list of live instances."
91112
(let ((live-instances))
92113
(ht-map (lambda (index buffer)
93-
(when (get-buffer buffer)
114+
(when (and (get-buffer buffer)
115+
(buffer-live-p buffer))
94116
(push buffer live-instances)))
95117
chatgpt-instances)
96118
(reverse live-instances)))
@@ -109,35 +131,101 @@ Display buffer from BUFFER-OR-NAME."
109131
(let ((target))
110132
(cl-some (lambda (index)
111133
(let ((buffer (ht-get chatgpt-instances index)))
112-
(unless (get-buffer buffer) ; if buffer is killed
134+
(when (or (not (get-buffer buffer))
135+
(not (buffer-live-p buffer))) ; if buffer is killed
113136
(setq target index)
114137
t)))
115138
(ht-keys chatgpt-instances))
116139
(unless target ; No killed instance?
117140
(setq target (ht-size chatgpt-instances))) ; Create a new one!
118141
target))
119142

120-
(defun chatgpt--display-message (instance message)
121-
""
122-
(with-current-buffer (get-buffer (cdr instance))
123-
(let ((inhibit-read-only t))
143+
(defun chatgpt-restart ()
144+
"Restart session."
145+
(interactive)
146+
(let* ((instance chatgpt-instances)
147+
(index (car instance))
148+
(buffer (cdr instance))
149+
(old-name))
150+
;; If buffer is alive, kill it!
151+
(chatgpt-with-instance instance
152+
(setq old-name (buffer-name))
153+
(kill-this-buffer))
154+
;; `old-name' will remain `nil' if buffer is not killed or invalid!
155+
(when old-name
156+
(chatgpt-register-instance index old-name))))
157+
158+
;;
159+
;;; Core
160+
161+
(defun chatgpt--add-tokens (data)
162+
"Record all tokens information from DATA."
163+
(let-alist data
164+
(let-alist .usage
165+
;; First we get the current tokens,
166+
(let ((prompt-tokens (ht-get chatgpt-data 'prompt_tokens 0))
167+
(completion-tokens (ht-get chatgpt-data 'completion_tokens 0))
168+
(total-tokens (ht-get chatgpt-data 'total_tokens 0)))
169+
;; Then we add it up!
170+
(ht-set chatgpt-data 'prompt_tokens (+ prompt-tokens .prompt_tokens))
171+
(ht-set chatgpt-data 'completion_tokens (+ completion-tokens .completion_tokens))
172+
(ht-set chatgpt-data 'total_tokens (+ total-tokens .total_tokens))))))
173+
174+
(defun chatgpt--add-message (role content)
175+
"Add a message to history.
176+
177+
The data is consist of ROLE and CONTENT."
178+
(setq chatgpt-chat-history
179+
(vconcat (or chatgpt-chat-history '[])
180+
`[((role . ,role)
181+
(content . ,(string-trim content)))])))
182+
183+
(defun chatgpt--add-response-messages (data)
184+
"Add the message to history from DATA, and return the message itself."
185+
(let-alist data
186+
(mapc (lambda (choice)
187+
(let-alist choice
188+
(let-alist .message
189+
(chatgpt--add-message .role .content))))
190+
.choices)))
191+
192+
;;
193+
;;; Display
124194

125-
)))
195+
(defun chatgpt--display-messages ()
196+
"Display all messages to latest response."
197+
(while (< chatgpt--display-pointer (length chatgpt-chat-history))
198+
(let ((message (elt chatgpt-chat-history chatgpt--display-pointer)))
199+
(let-alist message
200+
(insert .role ": " .content)
201+
(insert "\n\n")))
202+
(cl-incf chatgpt--display-pointer)))
126203

127204
(defun chatgpt-type-response ()
128-
""
205+
"Type response to OpenAI."
129206
(interactive)
130-
(let ((response (read-string "Type response: "))
131-
(instance chatgpt-instance))
132-
(setq chatgpt-requesting-p t)
133-
(openai-chat response
134-
(lambda (data)
135-
(let ((message ; TODO: ..
136-
))
137-
(chatgpt--display-response instance message)))
138-
:max-tokens chatgpt-max-tokens
139-
:temperature chatgpt-temperature
140-
:user (chatgpt--get-user))))
207+
(cond
208+
(chatgpt-requesting-p
209+
(message "[BUSY] Waiting for OpanAI to response..."))
210+
(t
211+
(let ((response (read-string "Type response: "))
212+
(user (chatgpt--get-user))
213+
(instance chatgpt-instance))
214+
(chatgpt--add-message user response) ; add user's response
215+
(chatgpt-with-instance instance
216+
(chatgpt--display-messages)) ; display it
217+
(setq chatgpt-requesting-p t)
218+
(openai-chat chatgpt-chat-history
219+
(lambda (data)
220+
(chatgpt-with-instance instance
221+
(setq chatgpt-requesting-p nil)
222+
(chatgpt--add-tokens data)
223+
(chatgpt--add-response-messages data)
224+
(chatgpt--display-messages)))
225+
:model chatgpt-model
226+
:max-tokens chatgpt-max-tokens
227+
:temperature chatgpt-temperature
228+
:user user)))))
141229

142230
;;
143231
;;; Entry
@@ -155,6 +243,15 @@ Display buffer from BUFFER-OR-NAME."
155243
\\<chatgpt-mode-map>"
156244
(setq-local buffer-read-only t))
157245

246+
(defun chatgpt-register-instance (index buffer-or-name)
247+
"Register BUFFER-OR-NAME with INDEX as an instance.
248+
249+
Caution, this will overwrite the existing instance!"
250+
(ht-set chatgpt-instances index (get-buffer-create buffer-or-name))
251+
(with-current-buffer buffer-or-name
252+
(chatgpt-mode)
253+
(setq chatgpt-instance (cons index (current-buffer)))))
254+
158255
;;;###autoload
159256
(defun chatgpt-new ()
160257
"Run a new instance of ChatGPT."
@@ -163,10 +260,7 @@ Display buffer from BUFFER-OR-NAME."
163260
(new-buffer-name (format chatgpt-buffer-name-format new-index)))
164261
(when (get-buffer new-buffer-name)
165262
(user-error "Internal Error: creating instance that already exists"))
166-
(ht-set chatgpt-instances new-index (get-buffer-create new-buffer-name))
167-
(with-current-buffer new-buffer-name
168-
(setq chatgpt-instance (cons new-index (current-buffer)))
169-
(chatgpt-mode 1))
263+
(chatgpt-register-instance new-index new-buffer-name)
170264
(chatgpt--pop-to-buffer new-buffer-name)))
171265

172266
;;;###autoload
@@ -178,7 +272,7 @@ Display buffer from BUFFER-OR-NAME."
178272
(cond (shown-instances
179273
(chatgpt--pop-to-buffer (nth 0 shown-instances)))
180274
(live-instances
181-
(chatgpt--pop-to-buffer (nth 0 shown-instances)))
275+
(chatgpt--pop-to-buffer (nth 0 live-instances)))
182276
(t
183277
(chatgpt-new)))))
184278

0 commit comments

Comments
 (0)