mirror of
https://github.com/perstarkse/minne.git
synced 2026-04-20 16:01:22 +02:00
uniform template for references, mvp chat interface
This commit is contained in:
@@ -2242,6 +2242,18 @@
|
|||||||
.bottom-0 {
|
.bottom-0 {
|
||||||
bottom: calc(var(--spacing) * 0);
|
bottom: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
|
.bottom-0\.5 {
|
||||||
|
bottom: calc(var(--spacing) * 0.5);
|
||||||
|
}
|
||||||
|
.bottom-1 {
|
||||||
|
bottom: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
|
.bottom-2 {
|
||||||
|
bottom: calc(var(--spacing) * 2);
|
||||||
|
}
|
||||||
|
.bottom-4 {
|
||||||
|
bottom: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
.\!left-3 {
|
.\!left-3 {
|
||||||
left: calc(var(--spacing) * 3) !important;
|
left: calc(var(--spacing) * 3) !important;
|
||||||
}
|
}
|
||||||
@@ -3761,6 +3773,9 @@
|
|||||||
color: var(--color-base-content);
|
color: var(--color-base-content);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
.\!mb-0 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 0) !important;
|
||||||
|
}
|
||||||
.mb-2 {
|
.mb-2 {
|
||||||
margin-bottom: calc(var(--spacing) * 2);
|
margin-bottom: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -4144,9 +4159,57 @@
|
|||||||
.h-32 {
|
.h-32 {
|
||||||
height: calc(var(--spacing) * 32);
|
height: calc(var(--spacing) * 32);
|
||||||
}
|
}
|
||||||
|
.h-100 {
|
||||||
|
height: calc(var(--spacing) * 100);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-48px\)\] {
|
||||||
|
height: calc(100vh - 48px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-52px\)\] {
|
||||||
|
height: calc(100vh - 52px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-54px\)\] {
|
||||||
|
height: calc(100vh - 54px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-60px\)\] {
|
||||||
|
height: calc(100vh - 60px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-65px\)\] {
|
||||||
|
height: calc(100vh - 65px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-80px\)\] {
|
||||||
|
height: calc(100vh - 80px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-120px\)\] {
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-140px\)\] {
|
||||||
|
height: calc(100vh - 140px);
|
||||||
|
}
|
||||||
.h-\[calc\(100vh-160px\)\] {
|
.h-\[calc\(100vh-160px\)\] {
|
||||||
height: calc(100vh - 160px);
|
height: calc(100vh - 160px);
|
||||||
}
|
}
|
||||||
|
.h-\[calc\(100vh-170px\)\] {
|
||||||
|
height: calc(100vh - 170px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-175px\)\] {
|
||||||
|
height: calc(100vh - 175px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-180px\)\] {
|
||||||
|
height: calc(100vh - 180px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-200px\)\] {
|
||||||
|
height: calc(100vh - 200px);
|
||||||
|
}
|
||||||
|
.h-\[calc\(100vh-240px\)\] {
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
}
|
||||||
|
.h-full {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.max-h-full {
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
.min-h-\[100dvh\] {
|
.min-h-\[100dvh\] {
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
}
|
}
|
||||||
@@ -4389,12 +4452,18 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.overflow-auto {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
.overflow-clip {
|
.overflow-clip {
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
}
|
}
|
||||||
.overflow-hidden {
|
.overflow-hidden {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.overflow-visible {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
.overflow-x-auto {
|
.overflow-x-auto {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
@@ -4593,12 +4662,18 @@
|
|||||||
.pr-12 {
|
.pr-12 {
|
||||||
padding-right: calc(var(--spacing) * 12);
|
padding-right: calc(var(--spacing) * 12);
|
||||||
}
|
}
|
||||||
|
.\!pb-0 {
|
||||||
|
padding-bottom: calc(var(--spacing) * 0) !important;
|
||||||
|
}
|
||||||
.pb-0 {
|
.pb-0 {
|
||||||
padding-bottom: calc(var(--spacing) * 0);
|
padding-bottom: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
.pb-10 {
|
.pb-10 {
|
||||||
padding-bottom: calc(var(--spacing) * 10);
|
padding-bottom: calc(var(--spacing) * 10);
|
||||||
}
|
}
|
||||||
|
.pb-24 {
|
||||||
|
padding-bottom: calc(var(--spacing) * 24);
|
||||||
|
}
|
||||||
.pb-32 {
|
.pb-32 {
|
||||||
padding-bottom: calc(var(--spacing) * 32);
|
padding-bottom: calc(var(--spacing) * 32);
|
||||||
}
|
}
|
||||||
@@ -4970,11 +5045,21 @@
|
|||||||
padding-inline: calc(var(--spacing) * 0);
|
padding-inline: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sm\:pb-0 {
|
||||||
|
@media (width >= 40rem) {
|
||||||
|
padding-bottom: calc(var(--spacing) * 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
.sm\:pb-4 {
|
.sm\:pb-4 {
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
padding-bottom: calc(var(--spacing) * 4);
|
padding-bottom: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sm\:pb-32 {
|
||||||
|
@media (width >= 40rem) {
|
||||||
|
padding-bottom: calc(var(--spacing) * 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
.sm\:text-6xl {
|
.sm\:text-6xl {
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
font-size: var(--text-6xl);
|
font-size: var(--text-6xl);
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
|
||||||
systems.url = "github:nix-systems/default";
|
|
||||||
devenv.url = "github:cachix/devenv";
|
|
||||||
devenv.inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
nixConfig = {
|
|
||||||
extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=";
|
|
||||||
extra-substituters = "https://devenv.cachix.org";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = {
|
|
||||||
self,
|
|
||||||
nixpkgs,
|
|
||||||
devenv,
|
|
||||||
systems,
|
|
||||||
...
|
|
||||||
} @ inputs: let
|
|
||||||
forEachSystem = nixpkgs.lib.genAttrs (import systems);
|
|
||||||
in {
|
|
||||||
packages = forEachSystem (system: {
|
|
||||||
devenv-up = self.devShells.${system}.default.config.procfileScript;
|
|
||||||
});
|
|
||||||
|
|
||||||
devShells =
|
|
||||||
forEachSystem
|
|
||||||
(system: let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
in {
|
|
||||||
default = devenv.lib.mkShell {
|
|
||||||
inherit inputs pkgs;
|
|
||||||
modules = [
|
|
||||||
{
|
|
||||||
# https://devenv.sh/reference/options/
|
|
||||||
enterShell = ''
|
|
||||||
echo "Welcome to zettle_db project"
|
|
||||||
echo "----------------------------"
|
|
||||||
echo "run devenv up -d to start and monitor services"
|
|
||||||
'';
|
|
||||||
|
|
||||||
packages = [
|
|
||||||
pkgs.vscode-langservers-extracted
|
|
||||||
pkgs.openssl
|
|
||||||
];
|
|
||||||
|
|
||||||
languages.rust.enable = true;
|
|
||||||
|
|
||||||
processes = {
|
|
||||||
surreal_db.exec = "docker run --rm --pull always -p 8000:8000 --user $(id -u) -v $(pwd)/database:/database surrealdb/surrealdb:latest-dev start rocksdb:/database/database.db --user root_user --pass root_password";
|
|
||||||
# tailwind_css.exec = "npx tailwindcss --input src/server/assets/input.css --output src/server/assets/style.css -w";
|
|
||||||
};
|
|
||||||
|
|
||||||
services = {
|
|
||||||
rabbitmq = {
|
|
||||||
enable = true;
|
|
||||||
# plugins = ["tracing"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
env = {
|
|
||||||
SMTP_RELAYER = "relay_address";
|
|
||||||
SMTP_USERNAME = "relay_user";
|
|
||||||
SMTP_PASSWORD = "relay_pass";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -139,7 +139,7 @@ pub async fn get_response_stream(
|
|||||||
// 5. Create channel for collecting complete response
|
// 5. Create channel for collecting complete response
|
||||||
let (tx, mut rx) = channel::<String>(1000);
|
let (tx, mut rx) = channel::<String>(1000);
|
||||||
let tx_clone = tx.clone();
|
let tx_clone = tx.clone();
|
||||||
let (tx_final, mut rx_final) = channel::<Vec<String>>(1);
|
let (tx_final, mut rx_final) = channel::<Message>(1);
|
||||||
|
|
||||||
// 6. Set up the collection task for DB storage
|
// 6. Set up the collection task for DB storage
|
||||||
let db_client = state.surreal_db_client.clone();
|
let db_client = state.surreal_db_client.clone();
|
||||||
@@ -160,8 +160,6 @@ pub async fn get_response_stream(
|
|||||||
.map(|r| r.reference)
|
.map(|r| r.reference)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let _ = tx_final.send(references.clone()).await;
|
|
||||||
|
|
||||||
let ai_message = Message::new(
|
let ai_message = Message::new(
|
||||||
user_message.conversation_id,
|
user_message.conversation_id,
|
||||||
MessageRole::AI,
|
MessageRole::AI,
|
||||||
@@ -169,6 +167,8 @@ pub async fn get_response_stream(
|
|||||||
Some(references),
|
Some(references),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let _ = tx_final.send(ai_message.clone()).await;
|
||||||
|
|
||||||
match store_item(&db_client, ai_message).await {
|
match store_item(&db_client, ai_message).await {
|
||||||
Ok(_) => info!("Successfully stored AI message with references"),
|
Ok(_) => info!("Successfully stored AI message with references"),
|
||||||
Err(e) => error!("Failed to store AI message: {:?}", e),
|
Err(e) => error!("Failed to store AI message: {:?}", e),
|
||||||
@@ -181,7 +181,7 @@ pub async fn get_response_stream(
|
|||||||
user_message.conversation_id,
|
user_message.conversation_id,
|
||||||
MessageRole::AI,
|
MessageRole::AI,
|
||||||
full_json,
|
full_json,
|
||||||
Some(vec![]),
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = store_item(&db_client, ai_message).await;
|
let _ = store_item(&db_client, ai_message).await;
|
||||||
@@ -234,31 +234,27 @@ pub async fn get_response_stream(
|
|||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.chain(stream::once(async move {
|
.chain(stream::once(async move {
|
||||||
if let Some(references) = rx_final.recv().await {
|
if let Some(message) = rx_final.recv().await {
|
||||||
// Don't send any event if references is empty
|
// Don't send any event if references is empty
|
||||||
if references.is_empty() {
|
if message.references.as_ref().is_some_and(|x| x.is_empty()) {
|
||||||
return Ok(Event::default().event("empty")); // This event won't be sent
|
return Ok(Event::default().event("empty")); // This event won't be sent
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare data for template
|
// Prepare data for template
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct ReferenceData {
|
struct ReferenceData {
|
||||||
references: Vec<String>,
|
message: Message,
|
||||||
user_message_id: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render template with references
|
// Render template with references
|
||||||
match render_template(
|
match render_template(
|
||||||
"chat/reference_list.html",
|
"chat/reference_list.html",
|
||||||
ReferenceData {
|
ReferenceData { message },
|
||||||
references,
|
|
||||||
user_message_id: user_message.id,
|
|
||||||
},
|
|
||||||
state.templates.clone(),
|
state.templates.clone(),
|
||||||
) {
|
) {
|
||||||
Ok(html) => {
|
Ok(html) => {
|
||||||
// Extract the String from Html<String>
|
// Extract the String from Html<String>
|
||||||
let html_string = html.0; // Convert Html<String> to String
|
let html_string = html.0;
|
||||||
|
|
||||||
// Return the rendered HTML
|
// Return the rendered HTML
|
||||||
Ok(Event::default().event("references").data(html_string))
|
Ok(Event::default().event("references").data(html_string))
|
||||||
|
|||||||
@@ -34,21 +34,4 @@ impl Message {
|
|||||||
references,
|
references,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_ai_message(
|
|
||||||
conversation_id: String,
|
|
||||||
id: String,
|
|
||||||
content: String,
|
|
||||||
references: Option<Vec<String>>,
|
|
||||||
) -> Self {
|
|
||||||
let now = Utc::now();
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
role: MessageRole::AI,
|
|
||||||
content,
|
|
||||||
references,
|
|
||||||
conversation_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{% extends 'body_base.html' %}
|
{% extends 'body_base.html' %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div class="drawer xl:drawer-open">
|
<div class="drawer xl:drawer-open h-[calc(100vh-65px)] overflow-auto">
|
||||||
<input id="my-drawer-2" type="checkbox" class="drawer-toggle" />
|
<input id="my-drawer-2" type="checkbox" class="drawer-toggle" />
|
||||||
|
|
||||||
<!-- Drawer Content -->
|
<!-- Drawer Content -->
|
||||||
<div class="drawer-content flex justify-center">
|
<div class="drawer-content flex justify-center ">
|
||||||
<main class="flex justify-center grow mt-2 sm:mt-4 gap-6 mb-10 max-w-3xl w-full absolute left-0 right-0 mx-auto">
|
<main class="flex justify-center grow mt-2 sm:mt-4 gap-6 mb-10 max-w-3xl w-full absolute left-0 right-0 mx-auto">
|
||||||
<div class="relative w-full">
|
<div class="relative w-full">
|
||||||
{% include "chat/history.html" %}
|
{% include "chat/history.html" %}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="drawer-side z-50">
|
<div class="drawer-side z-50">
|
||||||
<label for="my-drawer-2" aria-label="close sidebar" class="drawer-overlay"></label>
|
<label for="my-drawer-2" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
<ul class="menu bg-base-200 text-base-content min-h-full w-72">
|
<ul class="menu bg-base-200 text-base-content w-72">
|
||||||
<!-- Sidebar content here -->
|
<!-- Sidebar content here -->
|
||||||
<li class="mt-4 cursor-pointer "><a href="/chat" hx-boost="true" class="flex justify-between">Create new
|
<li class="mt-4 cursor-pointer "><a href="/chat" hx-boost="true" class="flex justify-between">Create new
|
||||||
chat<span>{% include
|
chat<span>{% include
|
||||||
@@ -11,6 +11,5 @@
|
|||||||
<li><a href="/chat/{{conversation.id}}" hx-boost="true">{{conversation.title}} - {{conversation.created_at}}</a>
|
<li><a href="/chat/{{conversation.id}}" hx-boost="true">{{conversation.title}} - {{conversation.created_at}}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{{conversation_archive}}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<div id="chat_container" class="pl-3 overflow-y-auto h-[calc(100vh-160px)] pb-32">
|
<div id="chat_container" class="pl-3 overflow-y-auto h-[calc(100vh-175px)] hide-scrollbar">
|
||||||
{% for message in history %}
|
{% for message in history %}
|
||||||
{% if message.role == "AI" %}
|
{% if message.role == "AI" %}
|
||||||
<div class="chat chat-start">
|
<div class="chat chat-start">
|
||||||
@@ -31,11 +31,24 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also scroll when page loads
|
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener('load', function () {
|
||||||
const chatContainer = document.getElementById('chat_container');
|
const chatContainer = document.getElementById('chat_container');
|
||||||
if (chatContainer) {
|
if (chatContainer) {
|
||||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
/* Hide scrollbar but keep functionality */
|
||||||
|
.hide-scrollbar {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
/* IE and Edge */
|
||||||
|
scrollbar-width: none;
|
||||||
|
/* Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
/* Chrome, Safari and Opera */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="fixed w-full mx-auto max-w-3xl p-4 pb-0 sm:pb-4 left-0 right-0 bottom-0 bg-base-100 z-10">
|
<div class="fixed w-full mx-auto max-w-3xl p-0 pb-0 sm:pb-4 left-0 right-0 bottom-0 bg-base-100 z-10">
|
||||||
<form hx-post="{% if conversation %} /chat/{{conversation.id}} {% else %} /chat {% endif %}"
|
<form hx-post="{% if conversation %} /chat/{{conversation.id}} {% else %} /chat {% endif %}"
|
||||||
hx-target="#chat_container" hx-swap="beforeend" class="relative flex gap-2" id="chat-form">
|
hx-target="#chat_container" hx-swap="beforeend" class="relative flex gap-2" id="chat-form">
|
||||||
<textarea autofocus required name="content" placeholder="Type your message..." rows="2"
|
<textarea autofocus required name="content" placeholder="Type your message..." rows="2"
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<div class="relative my-2">
|
<div class="relative my-2">
|
||||||
<button id="references-toggle-{{user_message_id}}"
|
<button id="references-toggle-{{message.id}}"
|
||||||
class="text-xs text-blue-500 hover:text-blue-700 hover:underline focus:outline-none flex items-center">
|
class="text-xs text-blue-500 hover:text-blue-700 hover:underline focus:outline-none flex items-center">
|
||||||
References
|
References
|
||||||
{% include "icons/chevron_icon.html" %}
|
{% include "icons/chevron_icon.html" %}
|
||||||
</button>
|
</button>
|
||||||
<div id="references-content-{{user_message_id}}" class="hidden max-w-full mt-1">
|
<div id="references-content-{{message.id}}" class="hidden max-w-full mt-1">
|
||||||
<div class="flex flex-wrap gap-1">
|
<div class="flex flex-wrap gap-1">
|
||||||
{% for reference in references %}
|
{% for reference in message.references %}
|
||||||
<div class="reference-badge-container" data-reference="{{reference}}" data-message-id="{{user_message_id}}"
|
<div class="reference-badge-container" data-reference="{{reference}}" data-message-id="{{message.id}}"
|
||||||
data-index="{{loop.index}}">
|
data-index="{{loop.index}}">
|
||||||
<span class="badge badge-xs badge-neutral truncate max-w-[20ch] overflow-hidden text-left block cursor-pointer">
|
<span class="badge badge-xs badge-neutral truncate max-w-[20ch] overflow-hidden text-left block cursor-pointer">
|
||||||
{{reference}}
|
{{reference}}
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('references-toggle-{{user_message_id}}').addEventListener('click', function () {
|
document.getElementById('references-toggle-{{message.id}}').addEventListener('click', function () {
|
||||||
const content = document.getElementById('references-content-{{user_message_id}}');
|
const content = document.getElementById('references-content-{{message.id}}');
|
||||||
const icon = document.getElementById('toggle-icon');
|
const icon = document.getElementById('toggle-icon');
|
||||||
content.classList.toggle('hidden');
|
content.classList.toggle('hidden');
|
||||||
icon.classList.toggle('rotate-180');
|
icon.classList.toggle('rotate-180');
|
||||||
|
|||||||
11
todo.md
11
todo.md
@@ -1,17 +1,20 @@
|
|||||||
\[\] chat functionality
|
\[\] archive ingressed webpage
|
||||||
|
\[x\] chat styling overhaul
|
||||||
|
\[\] configs primarily get envs
|
||||||
\[\] filtering on categories
|
\[\] filtering on categories
|
||||||
\[\] link to ingressed urls or archives
|
\[\] link to ingressed urls or archives
|
||||||
\[\] archive ingressed webpage
|
\[\] three js graph explorer
|
||||||
\[\] configs primarily get envs
|
\[\] three js vector explorer
|
||||||
\[\] on updates of knowledgeentity create new embeddings
|
|
||||||
\[x\] add user_id to ingress objects
|
\[x\] add user_id to ingress objects
|
||||||
\[x\] admin controls re registration
|
\[x\] admin controls re registration
|
||||||
|
\[x\] chat functionality
|
||||||
\[x\] gdpr
|
\[x\] gdpr
|
||||||
\[x\] html ingression
|
\[x\] html ingression
|
||||||
\[x\] hx-redirect
|
\[x\] hx-redirect
|
||||||
\[x\] ios shortcut generation
|
\[x\] ios shortcut generation
|
||||||
\[x\] job queue
|
\[x\] job queue
|
||||||
\[x\] macro for pagedata?
|
\[x\] macro for pagedata?
|
||||||
|
\[x\] on updates of knowledgeentity create new embeddings
|
||||||
\[x\] redirects
|
\[x\] redirects
|
||||||
\[x\] restrict retrieval to users own objects
|
\[x\] restrict retrieval to users own objects
|
||||||
\[x\] smoothie_dom test
|
\[x\] smoothie_dom test
|
||||||
|
|||||||
Reference in New Issue
Block a user