kotaemon/libs/ktem/ktem/main.py
Tuan Anh Nguyen Dang (Tadashi_Cin) 3bd3830b8d
feat: sso login, demo mode & new mindmap support (#644) bump:minor
* fix: update .env.example

* feat: add SSO login

* fix: update flowsetting

* fix: add requirement

* fix: refine UI

* fix: update group id-based operation

* fix: improve citation logics

* fix: UI enhancement

* fix: user_id to string in models

* fix: improve chat suggestion UI and flow

* fix: improve group id handling

* fix: improve chat suggestion

* fix: secure download for single file

* fix: file limiting in docstore

* fix: improve chat suggestion logics & language conform

* feat: add markmap and select text to highlight function

* fix: update Dockerfile

* fix: user id auto generate

* fix: default user id

* feat: add demo mode

* fix: update flowsetting

* fix: revise default params for demo

* feat: sso_app alternative

* feat: sso login demo

* feat: demo specific customization

* feat: add login using API key

* fix: disable key-based login

* fix: optimize duplicate upload

* fix: gradio routing

* fix: disable arm build for demo

* fix: revise full-text search js logic

* feat: add rate limit

* fix: update Dockerfile with new launch script

* fix: update Dockerfile

* fix: update Dockerignore

* fix: update ratelimit logic

* fix: user_id in user management page

* fix: rename conv logic

* feat: update demo hint

* fix: minor fix

* fix: highlight on long PDF load

* feat: add HF paper list

* fix: update HF papers load logic

* feat: fly config

* fix: update fly config

* fix: update paper list pull api

* fix: minor update root routing

* fix: minor update root routing

* fix: simplify login flow & paper list UI

* feat: add paper recommendation

* fix: update Dockerfile

* fix: update Dockerfile

* fix: update default model

* feat: add long context Ollama through LCOllama

* feat: espose Gradio share to env

* fix: revert customized changes

* fix: list group at app load

* fix: relocate share conv button

* fix: update launch script

* fix: update Docker CI

* feat: add Ollama model selection at first setup

* docs: update README
2025-02-02 15:19:48 +07:00

211 lines
7.7 KiB
Python

import gradio as gr
from decouple import config
from ktem.app import BaseApp
from ktem.pages.chat import ChatPage
from ktem.pages.help import HelpPage
from ktem.pages.resources import ResourcesTab
from ktem.pages.settings import SettingsPage
from ktem.pages.setup import SetupPage
from theflow.settings import settings as flowsettings
KH_DEMO_MODE = getattr(flowsettings, "KH_DEMO_MODE", False)
KH_SSO_ENABLED = getattr(flowsettings, "KH_SSO_ENABLED", False)
KH_ENABLE_FIRST_SETUP = getattr(flowsettings, "KH_ENABLE_FIRST_SETUP", False)
KH_APP_DATA_EXISTS = getattr(flowsettings, "KH_APP_DATA_EXISTS", True)
# override first setup setting
if config("KH_FIRST_SETUP", default=False, cast=bool):
KH_APP_DATA_EXISTS = False
def toggle_first_setup_visibility():
global KH_APP_DATA_EXISTS
is_first_setup = not KH_DEMO_MODE and not KH_APP_DATA_EXISTS
KH_APP_DATA_EXISTS = True
return gr.update(visible=is_first_setup), gr.update(visible=not is_first_setup)
class App(BaseApp):
"""The main app of Kotaemon
The main application contains app-level information:
- setting state
- user id
App life-cycle:
- Render
- Declare public events
- Subscribe public events
- Register events
"""
def ui(self):
"""Render the UI"""
self._tabs = {}
with gr.Tabs() as self.tabs:
if self.f_user_management:
from ktem.pages.login import LoginPage
with gr.Tab(
"Welcome", elem_id="login-tab", id="login-tab"
) as self._tabs["login-tab"]:
self.login_page = LoginPage(self)
with gr.Tab(
"Chat",
elem_id="chat-tab",
id="chat-tab",
visible=not self.f_user_management,
) as self._tabs["chat-tab"]:
self.chat_page = ChatPage(self)
if len(self.index_manager.indices) == 1:
for index in self.index_manager.indices:
with gr.Tab(
f"{index.name}",
elem_id="indices-tab",
elem_classes=[
"fill-main-area-height",
"scrollable",
"indices-tab",
],
id="indices-tab",
visible=not self.f_user_management and not KH_DEMO_MODE,
) as self._tabs[f"{index.id}-tab"]:
page = index.get_index_page_ui()
setattr(self, f"_index_{index.id}", page)
elif len(self.index_manager.indices) > 1:
with gr.Tab(
"Files",
elem_id="indices-tab",
elem_classes=["fill-main-area-height", "scrollable", "indices-tab"],
id="indices-tab",
visible=not self.f_user_management and not KH_DEMO_MODE,
) as self._tabs["indices-tab"]:
for index in self.index_manager.indices:
with gr.Tab(
index.name,
elem_id=f"{index.id}-tab",
) as self._tabs[f"{index.id}-tab"]:
page = index.get_index_page_ui()
setattr(self, f"_index_{index.id}", page)
if not KH_DEMO_MODE:
if not KH_SSO_ENABLED:
with gr.Tab(
"Resources",
elem_id="resources-tab",
id="resources-tab",
visible=not self.f_user_management,
elem_classes=["fill-main-area-height", "scrollable"],
) as self._tabs["resources-tab"]:
self.resources_page = ResourcesTab(self)
with gr.Tab(
"Settings",
elem_id="settings-tab",
id="settings-tab",
visible=not self.f_user_management,
elem_classes=["fill-main-area-height", "scrollable"],
) as self._tabs["settings-tab"]:
self.settings_page = SettingsPage(self)
with gr.Tab(
"Help",
elem_id="help-tab",
id="help-tab",
visible=not self.f_user_management,
elem_classes=["fill-main-area-height", "scrollable"],
) as self._tabs["help-tab"]:
self.help_page = HelpPage(self)
if KH_ENABLE_FIRST_SETUP:
with gr.Column(visible=False) as self.setup_page_wrapper:
self.setup_page = SetupPage(self)
def on_subscribe_public_events(self):
if self.f_user_management:
from ktem.db.engine import engine
from ktem.db.models import User
from sqlmodel import Session, select
def toggle_login_visibility(user_id):
if not user_id:
return list(
(
gr.update(visible=True)
if k == "login-tab"
else gr.update(visible=False)
)
for k in self._tabs.keys()
) + [gr.update(selected="login-tab")]
with Session(engine) as session:
user = session.exec(select(User).where(User.id == user_id)).first()
if user is None:
return list(
(
gr.update(visible=True)
if k == "login-tab"
else gr.update(visible=False)
)
for k in self._tabs.keys()
)
is_admin = user.admin
tabs_update = []
for k in self._tabs.keys():
if k == "login-tab":
tabs_update.append(gr.update(visible=False))
elif k == "resources-tab":
tabs_update.append(gr.update(visible=is_admin))
else:
tabs_update.append(gr.update(visible=True))
tabs_update.append(gr.update(selected="chat-tab"))
return tabs_update
self.subscribe_event(
name="onSignIn",
definition={
"fn": toggle_login_visibility,
"inputs": [self.user_id],
"outputs": list(self._tabs.values()) + [self.tabs],
"show_progress": "hidden",
},
)
self.subscribe_event(
name="onSignOut",
definition={
"fn": toggle_login_visibility,
"inputs": [self.user_id],
"outputs": list(self._tabs.values()) + [self.tabs],
"show_progress": "hidden",
},
)
if KH_ENABLE_FIRST_SETUP:
self.subscribe_event(
name="onFirstSetupComplete",
definition={
"fn": toggle_first_setup_visibility,
"inputs": [],
"outputs": [self.setup_page_wrapper, self.tabs],
"show_progress": "hidden",
},
)
def _on_app_created(self):
"""Called when the app is created"""
if KH_ENABLE_FIRST_SETUP:
self.app.load(
toggle_first_setup_visibility,
inputs=[],
outputs=[self.setup_page_wrapper, self.tabs],
)