From df9c5ff2639578c64cdc3e9cba10b534c3749531 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Tue, 11 Jun 2024 11:11:07 +0530 Subject: [PATCH] Show online references used for chat response as footnotes in Emacs Previously online references used weren't being shown --- src/interface/emacs/khoj.el | 71 ++++++++++++++++++----- src/interface/emacs/tests/khoj-tests.el | 77 ++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 16 deletions(-) diff --git a/src/interface/emacs/khoj.el b/src/interface/emacs/khoj.el index db5fd147..639aade4 100644 --- a/src/interface/emacs/khoj.el +++ b/src/interface/emacs/khoj.el @@ -849,16 +849,55 @@ RECEIVE-DATE is the message receive date." "Create `org-mode' footnotes with REFERENCE." (setq khoj--reference-count (1+ khoj--reference-count)) (let ((compiled-reference (cdr (assoc 'compiled reference)))) - (cons - (propertize (format "^{ [fn:%x]}" khoj--reference-count) 'help-echo compiled-reference) - (thread-last - compiled-reference - ;; remove filename top heading line from reference - ;; prevents actual reference heading in next line jumping out of references footnote section - (replace-regexp-in-string "^\* .*\n" "") - ;; remove multiple, consecutive empty lines from reference - (replace-regexp-in-string "\n\n" "\n") - (format "\n[fn:%x] %s" khoj--reference-count))))) + (cons + (propertize (format "^{ [fn:%x]}" khoj--reference-count) 'help-echo compiled-reference) + (thread-last + compiled-reference + ;; remove filename top heading line from reference + ;; prevents actual reference heading in next line jumping out of references footnote section + (replace-regexp-in-string "^\* .*\n" "") + ;; remove multiple, consecutive empty lines from reference + (replace-regexp-in-string "\n\n" "\n") + (format "\n[fn:%x] %s" khoj--reference-count))))) + +(defun khoj--generate-online-reference (reference) + (setq khoj--reference-count (1+ khoj--reference-count)) + (let ((link (cdr (assoc 'link reference))) + (title (cdr (assoc 'title reference))) + (description (cdr (assoc 'description reference)))) + (cons + (propertize (format "^{ [fn:%x]}" khoj--reference-count) 'help-echo (format "%s\n%s" link description)) + (thread-last + description + ;; remove multiple, consecutive empty lines from reference + (replace-regexp-in-string "\n\n" "\n") + (format "\n[fn:%x] [[%s][%s]]\n%s\n" khoj--reference-count link title))))) + +(defun khoj--extract-online-references (result-types searches) + "Extract link, title, and description of specified RESULT-TYPES from SEARCHES." + (let ((result '())) + (-map + (lambda (search) + (let ((search-q (car search)) + (search-results (cdr search))) + (-map-when + ;; filter search results by specified result types + (lambda (search-result) (member (car search-result) result-types)) + ;; extract link, title, and description from search results + (lambda (search-result) + (-map + (lambda (entry) + (let ((link (cdr (or (assoc 'link entry) (assoc 'descriptionLink entry)))) + (title (cdr (or (assoc 'title entry) '(title . ,link)))) + (description (cdr (or (assoc 'snippet entry) (assoc 'description entry))))) + (setq result (append result `(((title . ,title) (link . ,link) (description . ,description) (search . ,search-q))))))) + ;; wrap search results in a list if it is not already a list + (if (or (equal 'knowledgeGraph (car search-result)) (equal 'webpages (car search-result))) + (list (cdr search-result)) + (cdr search-result)))) + search-results))) + searches) + result)) (defun khoj--render-chat-response (response buffer-name) (with-current-buffer (get-buffer buffer-name) @@ -878,10 +917,14 @@ RECEIVE-DATE is the message receive date." (let* ((message (cdr (or (assoc 'response json-response) (assoc 'message json-response)))) (sender (cdr (assoc 'by json-response))) (receive-date (cdr (assoc 'created json-response))) - (references (or (cdr (assoc 'context json-response)) '())) - (footnotes (mapcar #'khoj--generate-reference references)) - (footnote-links (mapcar #'car footnotes)) - (footnote-defs (mapcar #'cdr footnotes)) + (online-references (or (cdr (assoc 'onlineContext json-response)) '())) + (online-footnotes (-map #'khoj--generate-online-reference + (khoj--extract-online-references '(organic knowledgeGraph peopleAlsoAsk webpages) + online-references))) + (doc-references (or (cdr (assoc 'context json-response)) '())) + (doc-footnotes (mapcar #'khoj--generate-reference doc-references)) + (footnote-links (mapcar #'car (append doc-footnotes online-footnotes))) + (footnote-defs (mapcar #'cdr (append doc-footnotes online-footnotes))) (formatted-response (thread-first ;; concatenate khoj message and references from API diff --git a/src/interface/emacs/tests/khoj-tests.el b/src/interface/emacs/tests/khoj-tests.el index cd2a1f02..c1dfe90e 100644 --- a/src/interface/emacs/tests/khoj-tests.el +++ b/src/interface/emacs/tests/khoj-tests.el @@ -221,7 +221,7 @@ Rule everything\n") (equal (khoj--render-update-files-as-request-body (list upgrade-file act-file) "khoj") (format - "\n--khoj\r\n\ + "\n--khoj\r\n\ Content-Disposition: form-data; name=\"files\"; filename=\"%s\"\r\n\ Content-Type: text/org\r\n\r\n\ # Become God\n\ @@ -246,7 +246,7 @@ Rule everything\n\n\r\n\ (equal (khoj--render-delete-files-as-request-body (list upgrade-file act-file "/tmp/deleted-file.org") "khoj") (format - "\n--khoj\r\n\ + "\n--khoj\r\n\ Content-Disposition: form-data; name=\"files\"; filename=\"%s\"\r\n\ Content-Type: text/org\r\n\r\n\ \r @@ -262,6 +262,79 @@ Content-Type: text/org\r\n\r\n\ (delete-file upgrade-file) (delete-file act-file)))) +(ert-deftest khoj-tests--extract-online-references () + (let* (;; Arrange + (onlineContext '((Albert\ Einstein\ wife + (organic . [((link . "https://en.wikipedia.org/wiki/Mileva_Mari%C4%87") + (title . "Mileva Marić - Wikipedia") + (snippet . "Marić married Einstein in 1903 and they had three ...") + (position . 1) + (sitelinks . [((link . "https://en.wikipedia.org/wiki/Mileva_Mari%C4%87#Bi...") + (title . "Biography")) + ((link . "https://en.wikipedia.org/wiki/Mileva_Mari%C4%87#De...") + (title . "Debate over collaboration with..."))])) + ((link . "https://en.wikipedia.org/wiki/Elsa_Einstein") + (title . "Elsa Einstein - Wikipedia") + (snippet . "Elsa Einstein (18 January 1876 – 20 December 1936)...") + (position . 2)) + ((link . "https://www.amazon.com/Einsteins-Wife-Story-Mileva...") + (price . 25.86) + (title . "Einstein's Wife: The Real Story of Mileva Einstein...") + (rating . 3.8) + (snippet . "Albert Einstein's first wife, Mileva Einstein-Mari...") + (currency . "$") + (position . 3) + (ratingCount . 80))]) + (peopleAlsoAsk . [((link . "https://en.wikipedia.org/wiki/Mileva_Mari%C4%87") + (title . "Mileva Marić - Wikipedia") + (snippet . "Death. Mileva Marić suffered a severe stroke and d...") + (question . "What happened to Einstein's first wife?")) + ((link . "https://www.redalyc.org/journal/5117/511767145014/...") + (title . "The story of Mileva Marić: Did Einstein's first wi...") + (snippet . "The couple were married in 1903. Some claim that M...") + (question . "Did Albert Einstein's wife do all his work?")) + ((link . "https://en.wikipedia.org/wiki/Hans_Albert_Einstein") + (title . "Hans Albert Einstein - Wikipedia") + (snippet . "Klaus Martin Einstein (1932–1939), died of diphthe...") + (question . "What happened to Einstein's children?"))]) + (knowledgeGraph (type . "Theoretical physicist") + (title . "Albert Einstein") + (attributes (Born . "March 14, 1879, Ulm, Germany") + (Died . "April 18, 1955 (age 76 years), Princeton, NJ") + (Spouse . "Elsa Einstein (m. 1919–1936) and Mileva Marić (m. ...") + (Children . "Eduard Einstein, Hans Albert Einstein, and Lieserl...") + (Education . "University of Zurich (1905), ETH Zürich (1897–1900...") + (Nationality . "American, German, Hungarian, and more") + (Grandchildren . "Evelyn Einstein, Bernhard Caesar Einstein, Klaus M...")) + (description . "Albert Einstein was a German-born theoretical phys...") + (descriptionLink . "https://en.wikipedia.org/wiki/Albert_Einstein") + (descriptionSource . "Wikipedia"))) + (Prince\ Albert\ spouse + (webpages (link . "https://en.wikipedia.org/wiki/Prince_Albert_of_Sax...") (snippet . "Prince Albert of Saxe-Coburg and Gotha was the hus...")) + (organic . [((date . "Feb 4, 2024") + (link . "https://en.wikipedia.org/wiki/Prince_Albert_of_Sax...") + (title . "Prince Albert of Saxe-Coburg and Gotha - Wikipedia") + (snippet . "Prince Albert of Saxe-Coburg and Gotha was the hus...") + (position . 1) + (sitelinks . [((link . "https://en.wikipedia.org/wiki/Prince_consort") + (title . "Prince consort")) + ((link . "https://en.wikipedia.org/wiki/Nellie_Clifden") + (title . "Nellie Clifden"))]))]) + (answerBox (title . "Prince Albert of Saxe-Coburg and Gotha / Spouse") + (answer . "Queen Victoria")) + (peopleAlsoAsk . [((link . "https://en.wikipedia.org/wiki/Albert_II,_Prince_of...") + (title . "Albert II, Prince of Monaco - Wikipedia") + (snippet . "In July 2011, Prince Albert married South African ...") + (question . "How many children does Prince Albert of Monaco hav..."))])))) + ;; Act + (result (khoj--extract-online-references + '(organic knowledgeGraph peopleAlsoAsk webpages) + onlineContext))) + ;; Assert + (progn + (should (equal (length result) 10)) + result))) + (provide 'khoj-tests) ;;; khoj-tests.el ends here