mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-09 21:29:11 +00:00
Support configuration of multiple Github repositories in the settings interface
- Add cards to configure each of the Github repositories - Fix a bug in the API which caused all other settings to be wiped when updating one of the content types - Provide an error message to the user if they have a misconfiguration in their chat settings
This commit is contained in:
@@ -84,12 +84,21 @@
|
|||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
fetch('/api/chat?client=web')
|
fetch('/api/chat?client=web')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => data.response)
|
.then(data => {
|
||||||
.then(chat_logs => {
|
if (data.detail) {
|
||||||
|
// If the server returns a 500 error with detail, render it as a message.
|
||||||
|
renderMessage(data.detail + " You can configure Khoj chat in your <a class='inline-chat-link' href='/config'>settings</a>.", "khoj");
|
||||||
|
}
|
||||||
|
return data.response;
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
// Render conversation history, if any
|
// Render conversation history, if any
|
||||||
chat_logs.forEach(chat_log => {
|
response.forEach(chat_log => {
|
||||||
renderMessageWithReference(chat_log.message, chat_log.by, chat_log.context, new Date(chat_log.created));
|
renderMessageWithReference(chat_log.message, chat_log.by, chat_log.context, new Date(chat_log.created));
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set welcome message on load
|
// Set welcome message on load
|
||||||
@@ -235,6 +244,12 @@
|
|||||||
font-size: medium;
|
font-size: medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.inline-chat-link {
|
||||||
|
color: #475569;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px dotted #475569;
|
||||||
|
}
|
||||||
|
|
||||||
@media (pointer: coarse), (hover: none) {
|
@media (pointer: coarse), (hover: none) {
|
||||||
abbr[title] {
|
abbr[title] {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -16,31 +16,25 @@
|
|||||||
<input type="text" id="pat-token" name="pat" value="{{ current_config['pat_token'] }}">
|
<input type="text" id="pat-token" name="pat" value="{{ current_config['pat_token'] }}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<label for="repo-owner">Repository Owner</label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" id="repo-owner" name="repo_owner" value="{{ current_config['repo_owner'] }}">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<label for="repo-name">Repository Name</label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" id="repo-name" name="repo_name" value="{{ current_config['repo_name'] }}">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<label for="repo-branch">Repository Branch</label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" id="repo-branch" name="repo_branch" value="{{ current_config['repo_branch'] }}">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
<h4>Repositories</h4>
|
||||||
|
<div id="repositories" class="section-cards">
|
||||||
|
{% for repo in current_config['repos'] %}
|
||||||
|
<div class="card repo" id="repo-card-{{loop.index}}">
|
||||||
|
<label for="repo-owner">Repository Owner</label>
|
||||||
|
<input type="text" id="repo-owner-{{loop.index}}" name="repo_owner" value="{{ repo.owner }}">
|
||||||
|
<label for="repo-name">Repository Name</label>
|
||||||
|
<input type="text" id="repo-name-{{loop.index}}" name="repo_name" value="{{ repo.name}}">
|
||||||
|
<label for="repo-branch">Repository Branch</label>
|
||||||
|
<input type="text" id="repo-branch-{{loop.index}}" name="repo_branch" value="{{ repo.branch }}">
|
||||||
|
<button type="button"
|
||||||
|
class="remove-repo-button"
|
||||||
|
onclick="remove_repo({{loop.index}})"
|
||||||
|
id="remove-repo-button-{{loop.index}}">Remove Repository</button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<button type="button" id="add-repository-button">Add Repository</button>
|
||||||
<h4>You probably don't need to edit these.</h4>
|
<h4>You probably don't need to edit these.</h4>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
@@ -68,16 +62,86 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<style>
|
||||||
|
div.repo {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
grid-template-rows: none;
|
||||||
|
}
|
||||||
|
div#repositories {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
button.remove-repo-button {
|
||||||
|
background-color: gainsboro;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
const add_repo_button = document.getElementById("add-repository-button");
|
||||||
|
add_repo_button.addEventListener("click", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var repo = document.createElement("div");
|
||||||
|
repo.classList.add("card");
|
||||||
|
repo.classList.add("repo");
|
||||||
|
const id = Date.now();
|
||||||
|
repo.id = "repo-card-" + id;
|
||||||
|
repo.innerHTML = `
|
||||||
|
<label for="repo-owner">Repository Owner</label>
|
||||||
|
<input type="text" id="repo-owner" name="repo_owner">
|
||||||
|
<label for="repo-name">Repository Name</label>
|
||||||
|
<input type="text" id="repo-name" name="repo_name">
|
||||||
|
<label for="repo-branch">Repository Branch</label>
|
||||||
|
<input type="text" id="repo-branch" name="repo_branch">
|
||||||
|
<button type="button"
|
||||||
|
class="remove-repo-button"
|
||||||
|
onclick="remove_repo(${id})"
|
||||||
|
id="remove-repo-button-${id}">Remove Repository</button>
|
||||||
|
`;
|
||||||
|
document.getElementById("repositories").appendChild(repo);
|
||||||
|
})
|
||||||
|
|
||||||
|
function remove_repo(index) {
|
||||||
|
document.getElementById("repo-card-" + index).remove();
|
||||||
|
}
|
||||||
|
|
||||||
submit.addEventListener("click", function(event) {
|
submit.addEventListener("click", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
var compressed_jsonl = document.getElementById("compressed-jsonl").value;
|
const compressed_jsonl = document.getElementById("compressed-jsonl").value;
|
||||||
var embeddings_file = document.getElementById("embeddings-file").value;
|
const embeddings_file = document.getElementById("embeddings-file").value;
|
||||||
var pat_token = document.getElementById("pat-token").value;
|
const pat_token = document.getElementById("pat-token").value;
|
||||||
var repo_owner = document.getElementById("repo-owner").value;
|
|
||||||
var repo_name = document.getElementById("repo-name").value;
|
if (pat_token == "") {
|
||||||
var repo_branch = document.getElementById("repo-branch").value;
|
document.getElementById("success").innerHTML = "❌ Please enter a Personal Access Token.";
|
||||||
|
document.getElementById("success").style.display = "block";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var cards = document.getElementById("repositories").getElementsByClassName("repo");
|
||||||
|
var repos = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < cards.length; i++) {
|
||||||
|
var card = cards[i];
|
||||||
|
var owner = card.getElementsByTagName("input")[0].value;
|
||||||
|
var name = card.getElementsByTagName("input")[1].value;
|
||||||
|
var branch = card.getElementsByTagName("input")[2].value;
|
||||||
|
|
||||||
|
if (owner == "" || name == "" || branch == "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
repos.push({
|
||||||
|
"owner": owner,
|
||||||
|
"name": name,
|
||||||
|
"branch": branch,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repos.length == 0) {
|
||||||
|
document.getElementById("success").innerHTML = "❌ Please add at least one repository.";
|
||||||
|
document.getElementById("success").style.display = "block";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const csrfToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken'))?.split('=')[1];
|
const csrfToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken'))?.split('=')[1];
|
||||||
fetch('/api/config/data/content_type/github', {
|
fetch('/api/config/data/content_type/github', {
|
||||||
@@ -88,9 +152,7 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"pat_token": pat_token,
|
"pat_token": pat_token,
|
||||||
"repo_owner": repo_owner,
|
"repos": repos,
|
||||||
"repo_name": repo_name,
|
|
||||||
"repo_branch": repo_branch,
|
|
||||||
"compressed_jsonl": compressed_jsonl,
|
"compressed_jsonl": compressed_jsonl,
|
||||||
"embeddings_file": embeddings_file,
|
"embeddings_file": embeddings_file,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -80,7 +80,12 @@ async def set_content_config_github_data(updated_config: GithubContentConfig):
|
|||||||
if not state.config:
|
if not state.config:
|
||||||
state.config = FullConfig()
|
state.config = FullConfig()
|
||||||
state.config.search_type = SearchConfig.parse_obj(constants.default_config["search-type"])
|
state.config.search_type = SearchConfig.parse_obj(constants.default_config["search-type"])
|
||||||
state.config.content_type = ContentConfig(github=updated_config)
|
|
||||||
|
if not state.config.content_type:
|
||||||
|
state.config.content_type = ContentConfig(**{"github": updated_config})
|
||||||
|
else:
|
||||||
|
state.config.content_type.github = updated_config
|
||||||
|
|
||||||
try:
|
try:
|
||||||
save_config_to_file_updated_state()
|
save_config_to_file_updated_state()
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
@@ -93,7 +98,12 @@ async def set_content_config_data(content_type: str, updated_config: TextContent
|
|||||||
if not state.config:
|
if not state.config:
|
||||||
state.config = FullConfig()
|
state.config = FullConfig()
|
||||||
state.config.search_type = SearchConfig.parse_obj(constants.default_config["search-type"])
|
state.config.search_type = SearchConfig.parse_obj(constants.default_config["search-type"])
|
||||||
|
|
||||||
|
if not state.config.content_type:
|
||||||
state.config.content_type = ContentConfig(**{content_type: updated_config})
|
state.config.content_type = ContentConfig(**{content_type: updated_config})
|
||||||
|
else:
|
||||||
|
state.config.content_type[content_type] = updated_config
|
||||||
|
|
||||||
try:
|
try:
|
||||||
save_config_to_file_updated_state()
|
save_config_to_file_updated_state()
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
|
|||||||
Reference in New Issue
Block a user