3434(require 'cl-lib )
3535
3636(require 'openai )
37+ (require 'let-alist )
3738(require 'ht )
3839
3940(defgroup chatgpt nil
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
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