Log clients calling API endpoints on Khoj server

- Make API endpoints on Khoj server accept `client` as request parameter
  - Khoj API endpoints: /chat, /search, /update
- Make Khoj clients set `client` request param when calling the API endpoints on the Khoj server
  - Khoj clients: Emacs, Obsidian and Web
- Also log khoj server_version running to telemetry server
This commit is contained in:
Debanjum
2023-06-09 18:36:49 +05:30
committed by GitHub
10 changed files with 27 additions and 22 deletions

View File

@@ -634,7 +634,7 @@ CONFIG is json obtained from Khoj config API."
(buffer-string))) (buffer-string)))
;; Update index on khoj server after configuration update ;; Update index on khoj server after configuration update
(let ((khoj--server-ready? nil)) (let ((khoj--server-ready? nil))
(url-retrieve (format "%s/api/update?t=org" khoj-server-url) #'identity))) (url-retrieve (format "%s/api/update?t=org&client=emacs" khoj-server-url) #'identity)))
(defun khoj--get-enabled-content-types () (defun khoj--get-enabled-content-types ()
"Get content types enabled for search from API." "Get content types enabled for search from API."
@@ -651,7 +651,7 @@ CONFIG is json obtained from Khoj config API."
Use QUERY, CONTENT-TYPE and (optional) RERANK as query params" Use QUERY, CONTENT-TYPE and (optional) RERANK as query params"
(let ((rerank (or rerank "false")) (let ((rerank (or rerank "false"))
(encoded-query (url-hexify-string query))) (encoded-query (url-hexify-string query)))
(format "%s/api/search?q=%s&t=%s&r=%s&n=%s" khoj-server-url encoded-query content-type rerank khoj-results-count))) (format "%s/api/search?q=%s&t=%s&r=%s&n=%s&client=emacs" khoj-server-url encoded-query content-type rerank khoj-results-count)))
(defun khoj--query-search-api-and-render-results (query-url content-type query buffer-name) (defun khoj--query-search-api-and-render-results (query-url content-type query buffer-name)
"Query Khoj Search with QUERY-URL. "Query Khoj Search with QUERY-URL.
@@ -788,7 +788,7 @@ Render results in BUFFER-NAME using QUERY, CONTENT-TYPE."
"Send QUERY to Khoj Chat API." "Send QUERY to Khoj Chat API."
(let* ((url-request-method "GET") (let* ((url-request-method "GET")
(encoded-query (url-hexify-string query)) (encoded-query (url-hexify-string query))
(query-url (format "%s/api/chat?q=%s" khoj-server-url encoded-query))) (query-url (format "%s/api/chat?q=%s&client=emacs" khoj-server-url encoded-query)))
(with-temp-buffer (with-temp-buffer
(condition-case ex (condition-case ex
(progn (progn
@@ -1031,7 +1031,7 @@ Paragraph only starts at first text after blank line."
(let* ((force-update (if (member "--force-update" args) "true" "false")) (let* ((force-update (if (member "--force-update" args) "true" "false"))
;; set content type to: specified > last used > based on current buffer > default type ;; set content type to: specified > last used > based on current buffer > default type
(content-type (or (transient-arg-value "--content-type=" args) (khoj--buffer-name-to-content-type (buffer-name)))) (content-type (or (transient-arg-value "--content-type=" args) (khoj--buffer-name-to-content-type (buffer-name))))
(update-url (format "%s/api/update?t=%s&force=%s" khoj-server-url content-type force-update)) (update-url (format "%s/api/update?t=%s&force=%s&client=emacs" khoj-server-url content-type force-update))
(url-request-method "GET")) (url-request-method "GET"))
(progn (progn
(setq khoj--content-type content-type) (setq khoj--content-type content-type)

View File

@@ -35,7 +35,7 @@ export class KhojChatModal extends Modal {
contentEl.createDiv({ attr: { id: "khoj-chat-body", class: "khoj-chat-body" } }); contentEl.createDiv({ attr: { id: "khoj-chat-body", class: "khoj-chat-body" } });
// Get conversation history from Khoj backend // Get conversation history from Khoj backend
let chatUrl = `${this.setting.khojUrl}/api/chat?`; let chatUrl = `${this.setting.khojUrl}/api/chat?client=obsidian`;
let response = await request(chatUrl); let response = await request(chatUrl);
let chatLogs = JSON.parse(response).response; let chatLogs = JSON.parse(response).response;
chatLogs.forEach((chatLog: any) => { chatLogs.forEach((chatLog: any) => {
@@ -120,7 +120,7 @@ export class KhojChatModal extends Modal {
// Get chat response from Khoj backend // Get chat response from Khoj backend
let encodedQuery = encodeURIComponent(query); let encodedQuery = encodeURIComponent(query);
let chatUrl = `${this.setting.khojUrl}/api/chat?q=${encodedQuery}`; let chatUrl = `${this.setting.khojUrl}/api/chat?q=${encodedQuery}&client=obsidian`;
let response = await request(chatUrl); let response = await request(chatUrl);
let data = JSON.parse(response); let data = JSON.parse(response);

View File

@@ -89,7 +89,7 @@ export class KhojSearchModal extends SuggestModal<SearchResult> {
async getSuggestions(query: string): Promise<SearchResult[]> { async getSuggestions(query: string): Promise<SearchResult[]> {
// Query Khoj backend for search results // Query Khoj backend for search results
let encodedQuery = encodeURIComponent(query); let encodedQuery = encodeURIComponent(query);
let searchUrl = `${this.setting.khojUrl}/api/search?q=${encodedQuery}&n=${this.setting.resultsCount}&r=${this.rerank}`; let searchUrl = `${this.setting.khojUrl}/api/search?q=${encodedQuery}&n=${this.setting.resultsCount}&r=${this.rerank}&client=obsidian`;
// Get search results for markdown and pdf files // Get search results for markdown and pdf files
let mdResponse = await request(`${searchUrl}&t=markdown`); let mdResponse = await request(`${searchUrl}&t=markdown`);

View File

@@ -107,8 +107,8 @@ export class KhojSettingTab extends PluginSettingTab {
}, 300); }, 300);
this.plugin.registerInterval(progress_indicator); this.plugin.registerInterval(progress_indicator);
await request(`${this.plugin.settings.khojUrl}/api/update?t=markdown&force=true`); await request(`${this.plugin.settings.khojUrl}/api/update?t=markdown&force=true&client=obsidian`);
await request(`${this.plugin.settings.khojUrl}/api/update?t=pdf&force=true`); await request(`${this.plugin.settings.khojUrl}/api/update?t=pdf&force=true&client=obsidian`);
new Notice('✅ Updated Khoj index.'); new Notice('✅ Updated Khoj index.');
// Reset button once index is updated // Reset button once index is updated

View File

@@ -46,7 +46,7 @@ regenerateButton.addEventListener("click", (event) => {
event.preventDefault(); event.preventDefault();
regenerateButton.style.cursor = "progress"; regenerateButton.style.cursor = "progress";
regenerateButton.disabled = true; regenerateButton.disabled = true;
fetch("/api/update?force=true") fetch("/api/update?force=true&client=web")
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
regenerateButton.style.cursor = "pointer"; regenerateButton.style.cursor = "pointer";

View File

@@ -62,7 +62,7 @@
document.getElementById("chat-input").value = ""; document.getElementById("chat-input").value = "";
// Generate backend API URL to execute query // Generate backend API URL to execute query
let url = `/api/chat?q=${encodeURIComponent(query)}`; let url = `/api/chat?q=${encodeURIComponent(query)}&client=web`;
// Call specified Khoj API // Call specified Khoj API
fetch(url) fetch(url)
@@ -82,7 +82,7 @@
} }
window.onload = function () { window.onload = function () {
fetch('/api/chat') fetch('/api/chat?client=web')
.then(response => response.json()) .then(response => response.json())
.then(data => data.response) .then(data => data.response)
.then(chat_logs => { .then(chat_logs => {

View File

@@ -90,8 +90,8 @@
// Generate Backend API URL to execute Search // Generate Backend API URL to execute Search
url = type === "image" url = type === "image"
? `/api/search?q=${encodeURIComponent(query)}&t=${type}&n=${results_count}` ? `/api/search?q=${encodeURIComponent(query)}&t=${type}&n=${results_count}&client=web`
: `/api/search?q=${encodeURIComponent(query)}&t=${type}&n=${results_count}&r=${rerank}`; : `/api/search?q=${encodeURIComponent(query)}&t=${type}&n=${results_count}&r=${rerank}&client=web`;
// Execute Search and Render Results // Execute Search and Render Results
fetch(url) fetch(url)
@@ -107,7 +107,7 @@
function updateIndex() { function updateIndex() {
type = document.getElementById("type").value; type = document.getElementById("type").value;
fetch(`/api/update?t=${type}`) fetch(`/api/update?t=${type}&client=web`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
console.log(data); console.log(data);

View File

@@ -70,6 +70,7 @@ def search(
r: Optional[bool] = False, r: Optional[bool] = False,
score_threshold: Optional[Union[float, None]] = None, score_threshold: Optional[Union[float, None]] = None,
dedupe: Optional[bool] = True, dedupe: Optional[bool] = True,
client: Optional[str] = None,
): ):
results: List[SearchResponse] = [] results: List[SearchResponse] = []
if q is None or q == "": if q is None or q == "":
@@ -181,14 +182,16 @@ def search(
# Only log telemetry if query is new and not a continuation of previous query # Only log telemetry if query is new and not a continuation of previous query
if state.previous_query is None or state.previous_query not in user_query: if state.previous_query is None or state.previous_query not in user_query:
state.telemetry += [log_telemetry(telemetry_type="api", api="search", app_config=state.config.app)] state.telemetry += [
log_telemetry(telemetry_type="api", api="search", client=client, app_config=state.config.app)
]
state.previous_query = user_query state.previous_query = user_query
return results return results
@api.get("/update") @api.get("/update")
def update(t: Optional[SearchType] = None, force: Optional[bool] = False): def update(t: Optional[SearchType] = None, force: Optional[bool] = False, client: Optional[str] = None):
try: try:
state.search_index_lock.acquire() state.search_index_lock.acquire()
state.model = configure_search(state.model, state.config, regenerate=force, t=t) state.model = configure_search(state.model, state.config, regenerate=force, t=t)
@@ -207,13 +210,13 @@ def update(t: Optional[SearchType] = None, force: Optional[bool] = False):
else: else:
logger.info("📬 Processor reconfigured via API") logger.info("📬 Processor reconfigured via API")
state.telemetry += [log_telemetry(telemetry_type="api", api="update", app_config=state.config.app)] state.telemetry += [log_telemetry(telemetry_type="api", api="update", client=client, app_config=state.config.app)]
return {"status": "ok", "message": "khoj reloaded"} return {"status": "ok", "message": "khoj reloaded"}
@api.get("/chat") @api.get("/chat")
def chat(q: Optional[str] = None): def chat(q: Optional[str] = None, client: Optional[str] = None):
if ( if (
state.processor_config is None state.processor_config is None
or state.processor_config.conversation is None or state.processor_config.conversation is None
@@ -277,6 +280,6 @@ def chat(q: Optional[str] = None):
conversation_log=meta_log.get("chat", []), conversation_log=meta_log.get("chat", []),
) )
state.telemetry += [log_telemetry(telemetry_type="api", api="chat", app_config=state.config.app)] state.telemetry += [log_telemetry(telemetry_type="api", api="chat", client=client, app_config=state.config.app)]
return {"status": status, "response": gpt_response, "context": compiled_references} return {"status": status, "response": gpt_response, "context": compiled_references}

View File

@@ -3,11 +3,11 @@ from __future__ import annotations # to avoid quoting type hints
from collections import OrderedDict from collections import OrderedDict
import datetime import datetime
from importlib import import_module from importlib import import_module
from importlib.metadata import version
import logging import logging
from os import path from os import path
from pathlib import Path from pathlib import Path
import platform import platform
import requests
import sys import sys
from time import perf_counter from time import perf_counter
import torch import torch
@@ -184,6 +184,7 @@ def log_telemetry(telemetry_type: str, api: str = None, client: str = None, app_
request_body = { request_body = {
"telemetry_type": telemetry_type, "telemetry_type": telemetry_type,
"server_id": get_server_id(), "server_id": get_server_id(),
"server_version": version("khoj-assistant"),
"os": platform.system(), "os": platform.system(),
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
} }

View File

@@ -53,7 +53,7 @@ def v1_telemetry(telemetry_data: List[Dict[str, str]]):
# Log telemetry data # Log telemetry data
for item in telemetry_data: for item in telemetry_data:
cur.execute( cur.execute(
"INSERT INTO usage (time, type, server_id, os, api, client) VALUES (?, ?, ?, ?, ?, ?)", "INSERT INTO usage (time, type, server_id, os, api, client, server_version) VALUES (?, ?, ?, ?, ?, ?, ?)",
( (
item["timestamp"], item["timestamp"],
item["telemetry_type"], item["telemetry_type"],
@@ -61,6 +61,7 @@ def v1_telemetry(telemetry_data: List[Dict[str, str]]):
item["os"], item["os"],
item.get("api"), item.get("api"),
item.get("client"), item.get("client"),
item.get("server_version", None),
), ),
) )
# Commit the changes # Commit the changes