mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-06 21:29:12 +00:00
Pass query params to chat API in POST body instead of URL query string
Closes #899, #678
This commit is contained in:
@@ -103,7 +103,7 @@
|
|||||||
let conversationID = chatBody.dataset.conversationId;
|
let conversationID = chatBody.dataset.conversationId;
|
||||||
let hostURL = await window.hostURLAPI.getURL();
|
let hostURL = await window.hostURLAPI.getURL();
|
||||||
const khojToken = await window.tokenAPI.getToken();
|
const khojToken = await window.tokenAPI.getToken();
|
||||||
const headers = { 'Authorization': `Bearer ${khojToken}` };
|
const headers = { 'Authorization': `Bearer ${khojToken}`, 'Content-Type': 'application/json' };
|
||||||
|
|
||||||
if (!conversationID) {
|
if (!conversationID) {
|
||||||
let response = await fetch(`${hostURL}/api/chat/sessions`, { method: "POST", headers });
|
let response = await fetch(`${hostURL}/api/chat/sessions`, { method: "POST", headers });
|
||||||
@@ -149,12 +149,22 @@
|
|||||||
document.getElementById("send-button").style.display = "none";
|
document.getElementById("send-button").style.display = "none";
|
||||||
|
|
||||||
// Call Khoj chat API
|
// Call Khoj chat API
|
||||||
let chatApi = `${hostURL}/api/chat?q=${encodeURIComponent(query)}&conversation_id=${conversationID}&stream=true&client=desktop`;
|
const chatApi = `${hostURL}/api/chat?client=desktop`;
|
||||||
chatApi += (!!region && !!city && !!countryName && !!timezone)
|
const chatApiBody = {
|
||||||
? `®ion=${region}&city=${city}&country=${countryName}&timezone=${timezone}`
|
q: query,
|
||||||
: '';
|
conversation_id: parseInt(conversationID),
|
||||||
|
stream: true,
|
||||||
|
...(!!city && { city: city }),
|
||||||
|
...(!!region && { region: region }),
|
||||||
|
...(!!countryName && { country: countryName }),
|
||||||
|
...(!!timezone && { timezone: timezone }),
|
||||||
|
};
|
||||||
|
|
||||||
const response = await fetch(chatApi, { method: 'POST', headers });
|
const response = await fetch(chatApi, {
|
||||||
|
method: "POST",
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(chatApiBody),
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!response.ok) throw new Error(response.statusText);
|
if (!response.ok) throw new Error(response.statusText);
|
||||||
|
|||||||
@@ -675,14 +675,15 @@ Optionally apply CALLBACK with JSON parsed response and CBARGS."
|
|||||||
(json-parse-buffer :object-type 'alist))))
|
(json-parse-buffer :object-type 'alist))))
|
||||||
('file-error (message "Chat exception: [%s]" ex))))))
|
('file-error (message "Chat exception: [%s]" ex))))))
|
||||||
|
|
||||||
(defun khoj--call-api-async (path &optional method params callback &rest cbargs)
|
(defun khoj--call-api-async (path &optional method params body callback &rest cbargs)
|
||||||
"Async call to API at PATH with METHOD and query PARAMS as kv assoc list.
|
"Async call to API at PATH with specified METHOD, query PARAMS and request BODY.
|
||||||
Optionally apply CALLBACK with JSON parsed response and CBARGS."
|
Optionally apply CALLBACK with JSON parsed response and CBARGS."
|
||||||
(let* ((url-request-method (or method "GET"))
|
(let* ((url-request-method (or method "GET"))
|
||||||
(url-request-extra-headers `(("Authorization" . ,(format "Bearer %s" khoj-api-key))))
|
(url-request-extra-headers `(("Authorization" . ,(format "Bearer %s" khoj-api-key)) ("Content-Type" . "application/json")))
|
||||||
(param-string (if params (url-build-query-string params) ""))
|
(url-request-data (if body (json-encode body) nil))
|
||||||
|
(param-string (url-build-query-string (append params '((client "emacs")))))
|
||||||
(cbargs (if (and (listp cbargs) (listp (car cbargs))) (car cbargs) cbargs)) ; normalize cbargs to (a b) from ((a b)) if required
|
(cbargs (if (and (listp cbargs) (listp (car cbargs))) (car cbargs) cbargs)) ; normalize cbargs to (a b) from ((a b)) if required
|
||||||
(query-url (format "%s%s?%s&client=emacs" khoj-server-url path param-string)))
|
(query-url (format "%s%s?%s" khoj-server-url path param-string)))
|
||||||
(url-retrieve query-url
|
(url-retrieve query-url
|
||||||
(lambda (status)
|
(lambda (status)
|
||||||
(if (plist-get status :error)
|
(if (plist-get status :error)
|
||||||
@@ -710,6 +711,7 @@ Filter out first similar result if IS-FIND-SIMILAR set."
|
|||||||
(khoj--call-api-async path
|
(khoj--call-api-async path
|
||||||
"GET"
|
"GET"
|
||||||
params
|
params
|
||||||
|
nil
|
||||||
'khoj--render-search-results
|
'khoj--render-search-results
|
||||||
content-type query buffer-name is-find-similar)))
|
content-type query buffer-name is-find-similar)))
|
||||||
|
|
||||||
@@ -875,10 +877,11 @@ Filter out first similar result if IS-FIND-SIMILAR set."
|
|||||||
(defun khoj--query-chat-api (query session-id callback &rest cbargs)
|
(defun khoj--query-chat-api (query session-id callback &rest cbargs)
|
||||||
"Send QUERY for SESSION-ID to Khoj Chat API.
|
"Send QUERY for SESSION-ID to Khoj Chat API.
|
||||||
Call CALLBACK func with response and CBARGS."
|
Call CALLBACK func with response and CBARGS."
|
||||||
(let ((params `(("q" ,query) ("n" ,khoj-results-count))))
|
(let ((params `(("q" . ,query) ("n" . ,khoj-results-count))))
|
||||||
(when session-id (push `("conversation_id" ,session-id) params))
|
(when session-id (push `("conversation_id" . ,session-id) params))
|
||||||
(khoj--call-api-async "/api/chat"
|
(khoj--call-api-async "/api/chat"
|
||||||
"POST"
|
"POST"
|
||||||
|
nil
|
||||||
params
|
params
|
||||||
callback cbargs)))
|
callback cbargs)))
|
||||||
|
|
||||||
|
|||||||
@@ -1050,9 +1050,19 @@ export class KhojChatView extends KhojPaneView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get chat response from Khoj backend
|
// Get chat response from Khoj backend
|
||||||
let encodedQuery = encodeURIComponent(query);
|
const chatUrl = `${this.setting.khojUrl}/api/chat?client=obsidian`;
|
||||||
let chatUrl = `${this.setting.khojUrl}/api/chat?q=${encodedQuery}&conversation_id=${conversationId}&n=${this.setting.resultsCount}&stream=true&client=obsidian`;
|
const body = {
|
||||||
if (!!this.location) chatUrl += `®ion=${this.location.region}&city=${this.location.city}&country=${this.location.countryName}&timezone=${this.location.timezone}`;
|
q: query,
|
||||||
|
n: this.setting.resultsCount,
|
||||||
|
stream: true,
|
||||||
|
...(!!conversationId && { conversation_id: parseInt(conversationId) }),
|
||||||
|
...(!!this.location && {
|
||||||
|
city: this.location.city,
|
||||||
|
region: this.location.region,
|
||||||
|
country: this.location.countryName,
|
||||||
|
timezone: this.location.timezone,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
let newResponseEl = this.createKhojResponseDiv();
|
let newResponseEl = this.createKhojResponseDiv();
|
||||||
let newResponseTextEl = newResponseEl.createDiv();
|
let newResponseTextEl = newResponseEl.createDiv();
|
||||||
@@ -1079,6 +1089,7 @@ export class KhojChatView extends KhojPaneView {
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Authorization": `Bearer ${this.setting.khojApiKey}`,
|
"Authorization": `Bearer ${this.setting.khojApiKey}`,
|
||||||
},
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -232,17 +232,26 @@ export default function Chat() {
|
|||||||
async function chat() {
|
async function chat() {
|
||||||
localStorage.removeItem("message");
|
localStorage.removeItem("message");
|
||||||
if (!queryToProcess || !conversationId) return;
|
if (!queryToProcess || !conversationId) return;
|
||||||
let chatAPI = `/api/chat?q=${encodeURIComponent(queryToProcess)}&conversation_id=${conversationId}&stream=true&client=web`;
|
const chatAPI = "/api/chat?client=web";
|
||||||
if (locationData) {
|
const chatAPIBody = {
|
||||||
chatAPI += `®ion=${locationData.region}&country=${locationData.country}&city=${locationData.city}&timezone=${locationData.timezone}`;
|
q: queryToProcess,
|
||||||
}
|
conversation_id: parseInt(conversationId),
|
||||||
|
stream: true,
|
||||||
|
...(locationData && {
|
||||||
|
region: locationData.region,
|
||||||
|
country: locationData.country,
|
||||||
|
city: locationData.city,
|
||||||
|
timezone: locationData.timezone,
|
||||||
|
}),
|
||||||
|
...(image64 && { image: image64 }),
|
||||||
|
};
|
||||||
|
|
||||||
const response = await fetch(chatAPI, {
|
const response = await fetch(chatAPI, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: image64 ? JSON.stringify({ image: image64 }) : undefined,
|
body: JSON.stringify(chatAPIBody),
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -222,17 +222,26 @@ export default function SharedChat() {
|
|||||||
|
|
||||||
async function chat() {
|
async function chat() {
|
||||||
if (!queryToProcess || !conversationId) return;
|
if (!queryToProcess || !conversationId) return;
|
||||||
let chatAPI = `/api/chat?q=${encodeURIComponent(queryToProcess)}&conversation_id=${conversationId}&stream=true&client=web`;
|
const chatAPI = "/api/chat?client=web";
|
||||||
if (locationData) {
|
const chatAPIBody = {
|
||||||
chatAPI += `®ion=${locationData.region}&country=${locationData.country}&city=${locationData.city}&timezone=${locationData.timezone}`;
|
q: queryToProcess,
|
||||||
}
|
conversation_id: parseInt(conversationId),
|
||||||
|
stream: true,
|
||||||
|
...(locationData && {
|
||||||
|
region: locationData.region,
|
||||||
|
country: locationData.country,
|
||||||
|
city: locationData.city,
|
||||||
|
timezone: locationData.timezone,
|
||||||
|
}),
|
||||||
|
...(image64 && { image: image64 }),
|
||||||
|
};
|
||||||
|
|
||||||
const response = await fetch(chatAPI, {
|
const response = await fetch(chatAPI, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: image64 ? JSON.stringify({ image: image64 }) : undefined,
|
body: JSON.stringify(chatAPIBody),
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -520,8 +520,18 @@ async def set_conversation_title(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ImageUploadObject(BaseModel):
|
class ChatRequestBody(BaseModel):
|
||||||
image: str
|
q: str
|
||||||
|
n: Optional[int] = 7
|
||||||
|
d: Optional[float] = None
|
||||||
|
stream: Optional[bool] = False
|
||||||
|
title: Optional[str] = None
|
||||||
|
conversation_id: Optional[int] = None
|
||||||
|
city: Optional[str] = None
|
||||||
|
region: Optional[str] = None
|
||||||
|
country: Optional[str] = None
|
||||||
|
timezone: Optional[str] = None
|
||||||
|
image: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
@api_chat.post("")
|
@api_chat.post("")
|
||||||
@@ -529,17 +539,7 @@ class ImageUploadObject(BaseModel):
|
|||||||
async def chat(
|
async def chat(
|
||||||
request: Request,
|
request: Request,
|
||||||
common: CommonQueryParams,
|
common: CommonQueryParams,
|
||||||
q: str,
|
body: ChatRequestBody,
|
||||||
n: int = 7,
|
|
||||||
d: float = None,
|
|
||||||
stream: Optional[bool] = False,
|
|
||||||
title: Optional[str] = None,
|
|
||||||
conversation_id: Optional[int] = None,
|
|
||||||
city: Optional[str] = None,
|
|
||||||
region: Optional[str] = None,
|
|
||||||
country: Optional[str] = None,
|
|
||||||
timezone: Optional[str] = None,
|
|
||||||
image: Optional[ImageUploadObject] = None,
|
|
||||||
rate_limiter_per_minute=Depends(
|
rate_limiter_per_minute=Depends(
|
||||||
ApiUserRateLimiter(requests=60, subscribed_requests=60, window=60, slug="chat_minute")
|
ApiUserRateLimiter(requests=60, subscribed_requests=60, window=60, slug="chat_minute")
|
||||||
),
|
),
|
||||||
@@ -547,7 +547,20 @@ async def chat(
|
|||||||
ApiUserRateLimiter(requests=600, subscribed_requests=600, window=60 * 60 * 24, slug="chat_day")
|
ApiUserRateLimiter(requests=600, subscribed_requests=600, window=60 * 60 * 24, slug="chat_day")
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
async def event_generator(q: str, image: ImageUploadObject):
|
# Access the parameters from the body
|
||||||
|
q = body.q
|
||||||
|
n = body.n
|
||||||
|
d = body.d
|
||||||
|
stream = body.stream
|
||||||
|
title = body.title
|
||||||
|
conversation_id = body.conversation_id
|
||||||
|
city = body.city
|
||||||
|
region = body.region
|
||||||
|
country = body.country
|
||||||
|
timezone = body.timezone
|
||||||
|
image = body.image
|
||||||
|
|
||||||
|
async def event_generator(q: str, image: str):
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
ttft = None
|
ttft = None
|
||||||
chat_metadata: dict = {}
|
chat_metadata: dict = {}
|
||||||
@@ -560,7 +573,7 @@ async def chat(
|
|||||||
|
|
||||||
uploaded_image_url = None
|
uploaded_image_url = None
|
||||||
if image:
|
if image:
|
||||||
decoded_string = unquote(image.image)
|
decoded_string = unquote(image)
|
||||||
base64_data = decoded_string.split(",", 1)[1]
|
base64_data = decoded_string.split(",", 1)[1]
|
||||||
image_bytes = base64.b64decode(base64_data)
|
image_bytes = base64.b64decode(base64_data)
|
||||||
webp_image_bytes = convert_image_to_webp(image_bytes)
|
webp_image_bytes = convert_image_to_webp(image_bytes)
|
||||||
|
|||||||
Reference in New Issue
Block a user