-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathconsult-web-chatgpt.el
139 lines (118 loc) · 5.59 KB
/
consult-web-chatgpt.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
;;; consult-web-chatgpt.el --- Consulting chatGPT -*- lexical-binding: t -*-
;; Copyright (C) 2024 Armin Darvish
;; Author: Armin Darvish
;; Maintainer: Armin Darvish
;; Created: 2024
;; Version: 0.1
;; Package-Requires: ((emacs "28.1") (consult "1.1"))
;; Homepage: https://github.com/armindarvish/consult-web
;; Keywords: convenience
;;; Commentary:
;;; Code:
(require 'consult-web)
(defun consult-web-dynamic--chatgpt-format-candidate (table &optional face &rest args)
"Returns a formatted string for candidates of `consult-web-chatgpt'.
TABLE is a hashtable from `consult-web--chatgpt-fetch-results'."
(let* ((pl (consult-web-hashtable-to-plist table))
(title (format "%s" (gethash :title table)))
(source (gethash :source table))
(source (if (stringp source) (propertize source 'face 'consult-web-source-face)))
(query (gethash :query table))
(model (gethash :model table))
(match-str (if (stringp query) (consult--split-escaped (car (consult--command-split query))) nil))
(title-str (consult-web--set-string-width title (floor (* (frame-width) 0.4))))
(title-str (propertize title-str 'face (or face 'consult-web-ai-source-face)))
(extra-args (consult-web-hashtable-to-plist table '(:title :url :search-url :query :source :model)))
(str (concat title-str (if model (propertize (format "\tmodel: %s" model) 'face 'consult-web-path-face)) (if source (concat "\t" source)) (if extra-args (format "\t%s" extra-args))))
(str (apply #'propertize str pl))
)
(if consult-web-highlight-matches
(cond
((listp match-str)
(mapcar (lambda (match) (setq str (consult-web--highlight-match match str t))) match-str))
((stringp match-str)
(setq str (consult-web--highlight-match match-str str t)))))
str))
(defvar consult-web-chatgpt-api-url "https://api.openai.com/v1/chat/completions")
(defcustom consult-web-openai-api-key nil
"Key for OpeAI API
See URL `https://openai.com/product' and URL `https://platform.openai.com/docs/introduction' for details"
:group 'consult-web
:type '(choice (const :tag "API Key" string)
(function :tag "Custom Function")))
(cl-defun consult-web--chatgpt-fetch-results (input &rest args &key model &allow-other-keys)
"Fetches chat response for INPUT from chatGPT."
(let* ((model (or model gptel-model))
(headers `(("Content-Type" . "application/json")
("Authorization" . ,(concat "Bearer " (consult-web-expand-variable-function consult-web-openai-api-key))))))
(funcall consult-web-retrieve-backend
consult-web-chatgpt-api-url
:type "POST"
:headers headers
:data (json-encode `((model . ,model)
(messages . [((role . "user")
(content . ,input))])))
:parser
(lambda ()
(goto-char (point-min))
(let* ((table (make-hash-table :test 'equal))
(response (json-parse-buffer))
(title (gethash "content" (gethash "message" (aref (gethash "choices" response) 0)))))
(puthash :url nil
table)
(puthash :title title
table)
(puthash :source "chatGPT"
table)
(puthash :model model
table)
(puthash :query input
table)
(list table)))
)))
(defun consult-web--chatgpt-response-preview (response &optional query)
"Returns a buffer with formatted RESPONSE from chatGPT"
(save-excursion
(let ((buff (get-buffer-create "*consult-web-chatgpt-response*")))
(with-current-buffer buff
(erase-buffer)
(if query (insert (format "# User:\n\n %s\n\n" query)))
(if response (insert (format "# chatGPT:\n\n %s\n\n" response)))
(if (featurep 'mardown-mode)
(require 'markdown-mode)
(markdown-mode)
)
(point-marker))
)))
(defun consult-web--chatgpt-preview (cand)
"Shows a preview buffer with chatGPT response from CAND"
(when-let ((buff (get-buffer "*consult-web-chatgpt-response*")))
(kill-buffer buff))
(when-let* ((query (cond ((listp cand)
(get-text-property 0 :query (car cand)))
(t
(get-text-property 0 :query cand))))
(response (cond ((listp cand)
(or (get-text-property 0 :title (car cand)) (car cand)))
(t
(or (get-text-property 0 :title cand) cand))))
(marker (consult-web--chatgpt-response-preview response query)))
(consult--jump marker)
))
(consult-web-define-source "chatGPT"
:narrow-char ?G
:face 'consult-web-ai-source-face
:request #'consult-web--chatgpt-fetch-results
:format #'consult-web-dynamic--chatgpt-format-candidate
:on-preview #'consult-web--chatgpt-preview
:on-return #'identity
:on-callback #'consult-web--chatgpt-preview
:preview-key consult-web-preview-key
:search-history 'consult-web--search-history
:selection-history 'consult-web--selection-history
:dynamic 'both
)
;;; provide `consult-web-chatgpt' module
(provide 'consult-web-chatgpt)
(add-to-list 'consult-web-sources-modules-to-load 'consult-web-chatgpt)
;;; consult-web-chatgpt.el ends here