Fix and Improve Chat UI in Web, Desktop apps (#655)

### Improvements to Chat UI on Web, Desktop apps
- Improve styling of chat session side panel
- Improve styling of chat message bubble in Desktop, Web app
- Add frosted, minimal chat UI to background of Login screen
- Improve PWA install experience of Khoj

### Fixes to Chat UI on Web, Desktop apps
- Fix creating new chat sessions from the Desktop app
- Only show 3 starter questions even when consecutive chat sessions created

### Other Improvements
- Update Khoj cloud trial period to a fortnight instead of a week
- Document using venv to handle dependency conflict on khoj pip install

Resolves #276
This commit is contained in:
Debanjum
2024-02-23 19:27:02 +05:30
committed by GitHub
19 changed files with 153 additions and 71 deletions

View File

@@ -15,7 +15,7 @@ import TabItem from '@theme/TabItem';
```
## Setup
These are the general setup instructions for Khoj.
These are the general setup instructions for self-hosted Khoj.
- Make sure [python](https://realpython.com/installing-python/) and [pip](https://pip.pypa.io/en/stable/installation/) are installed on your machine
- Check the [Khoj Emacs docs](/clients/emacs#setup) to setup Khoj with Emacs<br />
@@ -23,7 +23,7 @@ These are the general setup instructions for Khoj.
- Check the [Khoj Obsidian docs](/clients/obsidian#setup) to setup Khoj with Obsidian<br />
Its simpler as it can skip the *configure* step below.
For Installation, you can either use Docker or install Khoj locally.
For Installation, you can either use Docker or install the Khoj server locally.
### Installation Option 1 (Docker)
@@ -267,6 +267,18 @@ You can head to http://localhost:42110 to use the web interface. You can also us
## Troubleshoot
#### Dependency conflict when trying to install Khoj python package with pip
- **Reason**: When conflicting dependency versions are required by Khoj vs other python packages installed on your system
- **Fix**: Install Khoj in a python virtual environment using [venv](https://docs.python.org/3/library/venv.html) or [pipx](https://pypa.github.io/pipx) to avoid this dependency conflicts
- **Process**:
1. Install [pipx](https://pypa.github.io/pipx/#install-pipx)
2. Use `pipx` to install Khoj to avoid dependency conflicts with other python packages.
```shell
pipx install khoj-assistant
```
3. Now start `khoj` using the standard steps described earlier
#### Install fails while building Tokenizer dependency
- **Details**: `pip install khoj-assistant` fails while building the `tokenizers` dependency. Complains about Rust.
- **Fix**: Install Rust to build the tokenizers package. For example on Mac run:

View File

@@ -5,7 +5,7 @@
<title>Khoj - About</title>
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
<link rel="manifest" href="/static/khoj_chat.webmanifest">
<link rel="manifest" href="/static/khoj.webmanifest">
<link rel="stylesheet" href="./assets/khoj.css">
</head>
<script type="text/javascript" src="./utils.js"></script>

View File

@@ -1,6 +1,6 @@
/* Amber Light scheme (Default) */
/* Can be forced with data-theme="light" */
@import url('https://fonts.googleapis.com/css2?family=Tajawal&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;500;700&display=swap');
[data-theme="light"],
:root:not([data-theme="dark"]) {

View File

@@ -5,7 +5,7 @@
<title>Khoj - Chat</title>
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
<link rel="manifest" href="/static/khoj_chat.webmanifest">
<link rel="manifest" href="/static/khoj.webmanifest">
<link rel="stylesheet" href="./assets/khoj.css">
</head>
<script type="text/javascript" src="./assets/markdown-it.min.js"></script>
@@ -104,7 +104,7 @@
linkElement.classList.add("inline-chat-link");
linkElement.classList.add("reference-link");
linkElement.setAttribute('title', title);
linkElement.innerHTML = title;
linkElement.textContent = title;
let referenceButton = document.createElement('button');
referenceButton.innerHTML = linkElement.outerHTML;
@@ -133,7 +133,6 @@
let message_time = formatDate(dt ?? new Date());
let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You";
let formattedMessage = formatHTMLMessage(message, raw);
let chatBody = document.getElementById("chat-body");
// Create a new div for the chat message
let chatMessage = document.createElement('div');
@@ -152,6 +151,7 @@
}
// Append chat message div to chat body
let chatBody = document.getElementById("chat-body");
chatBody.appendChild(chatMessage);
// Scroll to bottom of chat-body element
@@ -285,9 +285,12 @@
// Render markdown
newHTML = raw ? newHTML : md.render(newHTML);
// Get any elements with a class that starts with "language"
// Set rendered markdown to HTML DOM element
let element = document.createElement('div');
element.innerHTML = newHTML;
element.className = "chat-message-text-response";
// Get any elements with a class that starts with "language"
let codeBlockElements = element.querySelectorAll('[class^="language-"]');
// For each element, add a parent div with the class "programmatic-output"
codeBlockElements.forEach((codeElement) => {
@@ -341,22 +344,20 @@
let chat_body = document.getElementById("chat-body");
let conversationID = chat_body.dataset.conversationId;
let hostURL = await window.hostURLAPI.getURL();
const khojToken = await window.tokenAPI.getToken();
const headers = { 'Authorization': `Bearer ${khojToken}` };
if (!conversationID) {
let response = await fetch(`${hostURL}/api/chat/sessions`, { method: "POST" });
let response = await fetch(`${hostURL}/api/chat/sessions`, { method: "POST", headers });
let data = await response.json();
conversationID = data.conversation_id;
chat_body.dataset.conversationId = conversationID;
await refreshChatSessionsPanel();
}
// Generate backend API URL to execute query
let url = `${hostURL}/api/chat?q=${encodeURIComponent(query)}&n=${resultsCount}&client=web&stream=true&conversation_id=${conversationID}&region=${region}&city=${city}&country=${countryName}`;
const khojToken = await window.tokenAPI.getToken();
const headers = { 'Authorization': `Bearer ${khojToken}` };
let chatApi = `${hostURL}/api/chat?q=${encodeURIComponent(query)}&n=${resultsCount}&client=web&stream=true&conversation_id=${conversationID}&region=${region}&city=${city}&country=${countryName}`;
let new_response = document.createElement("div");
new_response.classList.add("chat-message", "khoj");
@@ -379,8 +380,8 @@
let chatInput = document.getElementById("chat-input");
chatInput.classList.remove("option-enabled");
// Call specified Khoj API
let response = await fetch(url, { headers });
// Call Khoj chat API
let response = await fetch(chatApi, { headers });
let rawResponse = "";
const contentType = response.headers.get("content-type");
@@ -540,6 +541,7 @@
chatInput.value = chatInput.value.trimStart();
let questionStarterSuggestions = document.getElementById("question-starters");
questionStarterSuggestions.innerHTML = "";
questionStarterSuggestions.style.display = "none";
if (chatInput.value.startsWith("/") && chatInput.value.split(" ").length === 1) {
@@ -591,6 +593,7 @@
const headers = { 'Authorization': `Bearer ${khojToken}` };
let chatBody = document.getElementById("chat-body");
chatBody.innerHTML = "";
let conversationId = chatBody.dataset.conversationId;
let chatHistoryUrl = `/api/chat/history?client=desktop`;
if (conversationId) {
@@ -671,11 +674,11 @@
fetch(`${hostURL}/api/chat/starters?client=desktop`, { headers })
.then(response => response.json())
.then(data => {
// Render chat options, if any
// Render conversation starters, if any
if (data.length > 0) {
let questionStarterSuggestions = document.getElementById("question-starters");
for (let index in data) {
let questionStarter = data[index];
questionStarterSuggestions.innerHTML = "";
data.forEach((questionStarter) => {
let questionStarterButton = document.createElement('button');
questionStarterButton.innerHTML = questionStarter;
questionStarterButton.classList.add("question-starter");
@@ -685,7 +688,7 @@
chat();
});
questionStarterSuggestions.appendChild(questionStarterButton);
}
});
questionStarterSuggestions.style.display = "grid";
}
})
@@ -785,7 +788,7 @@
let conversationButton = document.createElement('div');
let incomingConversationId = conversation["conversation_id"];
const conversationTitle = conversation["slug"] || `New conversation 🌱`;
conversationButton.innerHTML = conversationTitle;
conversationButton.textContent = conversationTitle;
conversationButton.classList.add("conversation-button");
if (incomingConversationId == conversationId) {
conversationButton.classList.add("selected-conversation");
@@ -883,7 +886,7 @@
fetch(`${hostURL}${editURL}` , { method: "PATCH" })
.then(response => response.ok ? response.json() : Promise.reject(response))
.then(data => {
conversationButton.innerHTML = newTitle;
conversationButton.textContent = newTitle;
})
.catch(err => {
return;
@@ -1014,6 +1017,7 @@
document.getElementById('side-panel').classList.toggle('collapsed');
document.getElementById('new-conversation').classList.toggle('collapsed');
document.getElementById('existing-conversations').classList.toggle('collapsed');
document.getElementById('side-panel-collapse').style.transform = document.getElementById('side-panel').classList.contains('collapsed') ? 'rotate(0deg)' : 'rotate(180deg)';
document.getElementById('chat-section-wrapper').classList.toggle('mobile-friendly');
}
@@ -1058,7 +1062,7 @@
id="collapse-side-panel-button"
onclick="handleCollapseSidePanel()"
>
<svg class="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg id="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.82054 20.7313C8.21107 21.1218 8.84423 21.1218 9.23476 20.7313L15.8792 14.0868C17.0505 12.9155 17.0508 11.0167 15.88 9.84497L9.3097 3.26958C8.91918 2.87905 8.28601 2.87905 7.89549 3.26958C7.50497 3.6601 7.50497 4.29327 7.89549 4.68379L14.4675 11.2558C14.8581 11.6464 14.8581 12.2795 14.4675 12.67L7.82054 19.317C7.43002 19.7076 7.43002 20.3407 7.82054 20.7313Z" fill="#0F0F0F"/>
</svg>
</button>
@@ -1161,6 +1165,7 @@
}
#side-panel {
width: 250px;
padding: 10px;
background: var(--background-color);
border-radius: 5px;
@@ -1168,11 +1173,11 @@
overflow-y: scroll;
text-align: left;
transition: width 0.3s ease-in-out;
width: 250px;
}
div#side-panel.collapsed {
width: 1px;
width: 0;
padding: 0;
display: block;
overflow: hidden;
}
@@ -1227,6 +1232,7 @@
display: inline-block;
max-width: 80%;
text-align: left;
white-space: pre-line;
}
/* color chat bubble by khoj blue */
.chat-message-text.khoj {
@@ -1234,6 +1240,15 @@
background: var(--primary);
margin-left: auto;
}
.chat-message-text ol,
.chat-message-text ul {
white-space: normal;
margin: 0;
}
.chat-message-text-response {
margin-bottom: -16px;
}
/* Spinner symbol when the chat message is loading */
.spinner {
border: 4px solid #f3f3f3;
@@ -1350,7 +1365,7 @@
font-size: large;
}
svg.side-panel-collapse {
svg#side-panel-collapse {
width: 30px;
height: 30px;
}
@@ -1604,7 +1619,7 @@
padding: 0;
}
svg.side-panel-collapse {
svg#side-panel-collapse {
width: 24px;
height: 24px;
}

View File

@@ -219,7 +219,7 @@ def subscription_to_state(subscription: Subscription) -> str:
return SubscriptionState.INVALID.value
elif subscription.type == Subscription.Type.TRIAL:
# Trial subscription is valid for 7 days
if datetime.now(tz=timezone.utc) - subscription.created_at > timedelta(days=7):
if datetime.now(tz=timezone.utc) - subscription.created_at > timedelta(days=14):
return SubscriptionState.EXPIRED.value
return SubscriptionState.TRIAL.value
@@ -573,7 +573,7 @@ class ConversationAdapters:
return await SpeechToTextModelOptions.objects.filter().afirst()
@staticmethod
async def aget_conversation_starters(user: KhojUser):
async def aget_conversation_starters(user: KhojUser, max_results=3):
all_questions = []
if await ReflectiveQuestion.objects.filter(user=user).aexists():
all_questions = await sync_to_async(ReflectiveQuestion.objects.filter(user=user).values_list)(
@@ -584,7 +584,6 @@ class ConversationAdapters:
"question", flat=True
)
max_results = 3
all_questions = await sync_to_async(list)(all_questions) # type: ignore
if len(all_questions) < max_results:
return all_questions

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,6 +1,6 @@
/* Amber Light scheme (Default) */
/* Can be forced with data-theme="light" */
@import url('https://fonts.googleapis.com/css2?family=Tajawal&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;500;700&display=swap');
[data-theme="light"],
:root:not([data-theme="dark"]) {
@@ -9,6 +9,7 @@
--primary-focus: rgba(255, 179, 0, 0.125);
--primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #f5f4f3;
--frosted-background-color: rgba(245, 244, 243, 0.75);
--main-text-color: #475569;
--summer-sun: #fcc50b;
--water: #44b9da;
@@ -26,6 +27,7 @@
--primary-focus: rgba(255, 179, 0, 0.25);
--primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #f5f4f3;
--frosted-background-color: rgba(245, 244, 243, 0.75);
--main-text-color: #475569;
--summer-sun: #fcc50b;
--water: #44b9da;
@@ -42,6 +44,7 @@
--primary-focus: rgba(255, 179, 0, 0.25);
--primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #f5f4f3;
--frosted-background-color: rgba(245, 244, 243, 0.75);
--main-text-color: #475569;
--summer-sun: #fcc50b;
--water: #44b9da;

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

View File

@@ -5,7 +5,7 @@
<title>Khoj - Chat</title>
<link rel="icon" type="image/png" sizes="128x128" href="/static/assets/icons/favicon-128x128.png">
<link rel="manifest" href="/static/khoj_chat.webmanifest">
<link rel="manifest" href="/static/khoj.webmanifest">
<link rel="stylesheet" href="/static/assets/khoj.css">
</head>
<script type="text/javascript" src="/static/assets/utils.js"></script>
@@ -116,7 +116,7 @@ To get started, just start typing below. You can also type / to see a list of co
linkElement.classList.add("inline-chat-link");
linkElement.classList.add("reference-link");
linkElement.setAttribute('title', title);
linkElement.innerHTML = title;
linkElement.textContent = title;
let referenceButton = document.createElement('button');
referenceButton.innerHTML = linkElement.outerHTML;
@@ -145,7 +145,6 @@ To get started, just start typing below. You can also type / to see a list of co
let message_time = formatDate(dt ?? new Date());
let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You";
let formattedMessage = formatHTMLMessage(message, raw);
let chatBody = document.getElementById("chat-body");
// Create a new div for the chat message
let chatMessage = document.createElement('div');
@@ -164,6 +163,7 @@ To get started, just start typing below. You can also type / to see a list of co
}
// Append chat message div to chat body
let chatBody = document.getElementById("chat-body");
chatBody.appendChild(chatMessage);
// Scroll to bottom of chat-body element
@@ -297,9 +297,12 @@ To get started, just start typing below. You can also type / to see a list of co
// Render markdown
newHTML = raw ? newHTML : md.render(newHTML);
// Get any elements with a class that starts with "language"
// Set rendered markdown to HTML DOM element
let element = document.createElement('div');
element.innerHTML = newHTML;
element.className = "chat-message-text-response";
// Get any elements with a class that starts with "language"
let codeBlockElements = element.querySelectorAll('[class^="language-"]');
// For each element, add a parent div with the class "programmatic-output"
codeBlockElements.forEach((codeElement) => {
@@ -520,6 +523,7 @@ To get started, just start typing below. You can also type / to see a list of co
chatInput.value = chatInput.value.trimStart();
let questionStarterSuggestions = document.getElementById("question-starters");
questionStarterSuggestions.innerHTML = "";
questionStarterSuggestions.style.display = "none";
if (chatInput.value.startsWith("/") && chatInput.value.split(" ").length === 1) {
@@ -565,6 +569,7 @@ To get started, just start typing below. You can also type / to see a list of co
function loadChat() {
let chatBody = document.getElementById("chat-body");
chatBody.innerHTML = "";
let conversationId = chatBody.dataset.conversationId;
let chatHistoryUrl = `/api/chat/history?client=web`;
if (conversationId) {
@@ -647,8 +652,8 @@ To get started, just start typing below. You can also type / to see a list of co
// Render chat options, if any
if (data.length > 0) {
let questionStarterSuggestions = document.getElementById("question-starters");
for (let index in data) {
let questionStarter = data[index];
questionStarterSuggestions.innerHTML = "";
data.forEach((questionStarter) => {
let questionStarterButton = document.createElement('button');
questionStarterButton.innerHTML = questionStarter;
questionStarterButton.classList.add("question-starter");
@@ -658,7 +663,7 @@ To get started, just start typing below. You can also type / to see a list of co
chat();
});
questionStarterSuggestions.appendChild(questionStarterButton);
}
});
questionStarterSuggestions.style.display = "grid";
}
})
@@ -713,7 +718,7 @@ To get started, just start typing below. You can also type / to see a list of co
let conversationButton = document.createElement('div');
let incomingConversationId = conversation["conversation_id"];
const conversationTitle = conversation["slug"] || `New conversation 🌱`;
conversationButton.innerHTML = conversationTitle;
conversationButton.textContent = conversationTitle;
conversationButton.classList.add("conversation-button");
if (incomingConversationId == conversationId) {
conversationButton.classList.add("selected-conversation");
@@ -789,7 +794,7 @@ To get started, just start typing below. You can also type / to see a list of co
fetch(editURL , { method: "PATCH" })
.then(response => response.ok ? response.json() : Promise.reject(response))
.then(data => {
conversationButton.innerHTML = newTitle;
conversationButton.textContent = newTitle;
})
.catch(err => {
return;
@@ -966,6 +971,7 @@ To get started, just start typing below. You can also type / to see a list of co
document.getElementById('side-panel').classList.toggle('collapsed');
document.getElementById('new-conversation').classList.toggle('collapsed');
document.getElementById('existing-conversations').classList.toggle('collapsed');
document.getElementById('side-panel-collapse').style.transform = document.getElementById('side-panel').classList.contains('collapsed') ? 'rotate(0deg)' : 'rotate(180deg)';
document.getElementById('chat-section-wrapper').classList.toggle('mobile-friendly');
}
@@ -1001,7 +1007,7 @@ To get started, just start typing below. You can also type / to see a list of co
id="collapse-side-panel-button"
onclick="handleCollapseSidePanel()"
>
<svg class="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg id="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.82054 20.7313C8.21107 21.1218 8.84423 21.1218 9.23476 20.7313L15.8792 14.0868C17.0505 12.9155 17.0508 11.0167 15.88 9.84497L9.3097 3.26958C8.91918 2.87905 8.28601 2.87905 7.89549 3.26958C7.50497 3.6601 7.50497 4.29327 7.89549 4.68379L14.4675 11.2558C14.8581 11.6464 14.8581 12.2795 14.4675 12.67L7.82054 19.317C7.43002 19.7076 7.43002 20.3407 7.82054 20.7313Z" fill="#0F0F0F"/>
</svg>
</button>
@@ -1226,6 +1232,7 @@ To get started, just start typing below. You can also type / to see a list of co
}
#side-panel {
width: 250px;
padding: 10px;
background: var(--background-color);
border-radius: 5px;
@@ -1233,11 +1240,11 @@ To get started, just start typing below. You can also type / to see a list of co
overflow-y: scroll;
text-align: left;
transition: width 0.3s ease-in-out;
width: 250px;
}
div#side-panel.collapsed {
width: 1px;
width: 0;
padding: 0;
display: block;
overflow: hidden;
}
@@ -1291,6 +1298,7 @@ To get started, just start typing below. You can also type / to see a list of co
display: inline-block;
max-width: 80%;
text-align: left;
white-space: pre-line;
}
/* color chat bubble by khoj blue */
.chat-message-text.khoj {
@@ -1298,6 +1306,15 @@ To get started, just start typing below. You can also type / to see a list of co
background: var(--primary);
margin-left: auto;
}
.chat-message-text ol,
.chat-message-text ul {
white-space: normal;
margin: 0;
}
.chat-message-text-response {
margin-bottom: -16px;
}
/* Spinner symbol when the chat message is loading */
.spinner {
border: 4px solid #f3f3f3;
@@ -1412,7 +1429,7 @@ To get started, just start typing below. You can also type / to see a list of co
font-size: large;
}
svg.side-panel-collapse {
svg#side-panel-collapse {
width: 30px;
height: 30px;
}
@@ -1551,7 +1568,7 @@ To get started, just start typing below. You can also type / to see a list of co
padding: 0;
}
svg.side-panel-collapse {
svg#side-panel-collapse {
width: 24px;
height: 24px;
}

View File

@@ -1,16 +1,51 @@
{
"name": "Khoj",
"short_name": "Khoj",
"description": "An AI search assistant for your digital brain",
"display": "standalone",
"start_url": "/",
"description": "The open, personal AI for your digital brain. You can ask Khoj to draft a message, paint your imagination, find information on the internet and even answer questions from your documents.",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"icons": [
{
"src": "/static/assets/icons/favicon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/static/assets/icons/favicon-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"start_url": "/"
"screenshots": [
{
"src": "/static/assets/samples/phone-remember-plan-sample.png",
"sizes": "419x900",
"type": "image/png",
"form_factor": "narrow",
"label": "Remember and Plan"
},
{
"src": "/static/assets/samples/phone-browse-draw-sample.png",
"sizes": "419x900",
"type": "image/png",
"form_factor": "narrow",
"label": "Browse and Draw"
},
{
"src": "/static/assets/samples/desktop-remember-plan-sample.png",
"sizes": "1260x742",
"type": "image/png",
"form_factor": "wide",
"label": "Remember and Plan"
},
{
"src": "/static/assets/samples/desktop-browse-draw-sample.png",
"sizes": "1260x742",
"type": "image/png",
"form_factor": "wide",
"label": "Browse and Draw"
}
]
}

View File

@@ -1,16 +0,0 @@
{
"name": "Khoj Chat",
"short_name": "Khoj Chat",
"description": "An AI personal assistant for your digital brain",
"icons": [
{
"src": "/static/assets/icons/favicon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"start_url": "/chat"
}

View File

@@ -65,13 +65,24 @@
padding: 0px;
margin: 0px;
height: 100%;
background: var(--background-color);
background: url('/static/assets/samples/desktop-plain-chat-sample.png') no-repeat center center fixed;
background-size: contain;
color: var(--main-text-color);
font-family: var(--font-family);
font-size: 20px;
font-weight: 300;
line-height: 1.5em;
}
body::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--frosted-background-color);
backdrop-filter: blur(10px);
}
body > * {
padding: 10px;
margin: 10px;
@@ -100,9 +111,9 @@
grid-template-rows: 1fr auto auto 1fr;
gap: 32px;
min-height: 300px;
background: var(--background-color);
margin-left: 25%;
margin-right: 25%;
z-index: 1;
}
div.g_id_signin {
@@ -118,9 +129,14 @@
}
@media only screen and (max-width: 700px) {
body{
background: url('/static/assets/samples/phone-plain-chat-sample.png') no-repeat center center fixed;
background-size: contain;
}
div#login-modal {
margin-left: 10%;
margin-right: 10%;
z-index: 1;
}
}

View File

@@ -122,6 +122,7 @@ def save_to_conversation_log(
conversation_id=conversation_id,
user_message=q,
)
logger.info(f'Saved Conversation Turn\nYou ({user.username}): "{q}"\n\nKhoj: "{chat_response}"')
def generate_chatml_messages_with_context(

View File

@@ -430,14 +430,14 @@ class ApiUserRateLimiter:
if subscribed and count_requests >= self.subscribed_requests:
raise HTTPException(status_code=429, detail="Slow down! Too Many Requests")
if not subscribed and count_requests >= self.requests:
if self.subscribed_requests == self.requests:
if self.requests >= self.subscribed_requests:
raise HTTPException(
status_code=429,
detail="Slow down! Too Many Requests",
)
raise HTTPException(
status_code=429,
detail="We're glad you're enjoying Khoj! You've exceeded your usage limit for today. Come back tomorrow or subscribe to increase your rate limit via [your settings](https://app.khoj.dev/config).",
detail="We're glad you're enjoying Khoj! You've exceeded your usage limit for today. Come back tomorrow or subscribe to increase your usage limit via [your settings](https://app.khoj.dev/config).",
)
# Add the current request to the cache
@@ -476,7 +476,7 @@ class ConversationCommandRateLimiter:
if not subscribed and count_requests >= self.trial_rate_limit:
raise HTTPException(
status_code=429,
detail=f"We're glad you're enjoying Khoj! You've exceeded your `/{conversation_command.value}` command usage limit for today. You can increase your rate limit via [your settings](https://app.khoj.dev/config).",
detail=f"We're glad you're enjoying Khoj! You've exceeded your `/{conversation_command.value}` command usage limit for today. Subscribe to increase your usage limit via [your settings](https://app.khoj.dev/config).",
)
await UserRequests.objects.acreate(user=user, slug=command_slug)
return