From 46ac5dac5325255dea7a93687a4d23d211c21ec0 Mon Sep 17 00:00:00 2001 From: yangdx Date: Fri, 17 Oct 2025 16:24:01 +0800 Subject: [PATCH 1/6] Improve API description formatting and add ReDoc link --- lightrag/api/lightrag_server.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lightrag/api/lightrag_server.py b/lightrag/api/lightrag_server.py index a558dae3..383c5ef3 100644 --- a/lightrag/api/lightrag_server.py +++ b/lightrag/api/lightrag_server.py @@ -345,14 +345,17 @@ def create_app(args): finalize_share_data() # Initialize FastAPI + base_description = ( + "Providing API for LightRAG core, Web UI and Ollama Model Emulation" + ) + swagger_description = ( + base_description + + (" (With authentication)" if api_key else "") + + "\n\n[View ReDoc documentation](/redoc)" + ) app_kwargs = { "title": "LightRAG Server API", - "description": ( - "Providing API for LightRAG core, Web UI and Ollama Model Emulation" - + "(With authentication)" - if api_key - else "" - ), + "description": swagger_description, "version": __api_version__, "openapi_url": "/openapi.json", # Explicitly set OpenAPI schema URL "docs_url": "/docs", # Explicitly set docs URL From f5558240649028195bc33c3abe3a0865f3dece82 Mon Sep 17 00:00:00 2001 From: yangdx Date: Fri, 17 Oct 2025 18:43:45 +0800 Subject: [PATCH 2/6] Fix tuple delimiter corruption handling in regex patterns --- lightrag/utils.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lightrag/utils.py b/lightrag/utils.py index 83a3c394..94f1ff27 100644 --- a/lightrag/utils.py +++ b/lightrag/utils.py @@ -2611,9 +2611,9 @@ def fix_tuple_delimiter_corruption( record, ) - # Fix: -> <|#|>, <|#|Y> -> <|#|>, -> <|#|>, <||#||> -> <|#|>, <||#> -> <|#|> (one extra characters outside pipes) + # Fix: -> <|#|>, <|#|Y> -> <|#|>, -> <|#|>, <||#||> -> <|#|> (one extra characters outside pipes) record = re.sub( - rf"<.?\|{escaped_delimiter_core}\|*?>", + rf"<.?\|{escaped_delimiter_core}\|.?>", tuple_delimiter, record, ) @@ -2633,7 +2633,6 @@ def fix_tuple_delimiter_corruption( ) # Fix: <|#| -> <|#|>, <|#|| -> <|#|> (missing closing >) - record = re.sub( rf"<\|{escaped_delimiter_core}\|+(?!>)", tuple_delimiter, @@ -2647,6 +2646,13 @@ def fix_tuple_delimiter_corruption( record, ) + # Fix: <||#> -> <|#|> (double pipe at start, missing pipe at end) + record = re.sub( + rf"<\|+{escaped_delimiter_core}>", + tuple_delimiter, + record, + ) + # Fix: <|| -> <|#|> record = re.sub( r"<\|\|(?!>)", From 4c3ab584738364542bb895287abc49011ace3b23 Mon Sep 17 00:00:00 2001 From: yangdx Date: Fri, 17 Oct 2025 19:10:36 +0800 Subject: [PATCH 3/6] Improve AsyncSelect layout and text overflow handling - Add responsive width container - Improve text truncation with tooltips --- .../src/components/graph/GraphLabels.tsx | 86 +++++++++++-------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/lightrag_webui/src/components/graph/GraphLabels.tsx b/lightrag_webui/src/components/graph/GraphLabels.tsx index 7ec2b6f5..ca2d1691 100644 --- a/lightrag_webui/src/components/graph/GraphLabels.tsx +++ b/lightrag_webui/src/components/graph/GraphLabels.tsx @@ -175,49 +175,59 @@ const GraphLabels = () => { > - - key={selectKey} // Force re-render when data changes - className="min-w-[300px]" - triggerClassName="max-h-8" - searchInputClassName="max-h-8" - triggerTooltip={t('graphPanel.graphLabels.selectTooltip')} - fetcher={fetchData} - renderOption={(item) =>
{item}
} - getOptionValue={(item) => item} - getDisplayValue={(item) =>
{item}
} - notFound={
{t('graphPanel.graphLabels.noLabels')}
} - ariaLabel={t('graphPanel.graphLabels.label')} - placeholder={t('graphPanel.graphLabels.placeholder')} - searchPlaceholder={t('graphPanel.graphLabels.placeholder')} - noResultsMessage={t('graphPanel.graphLabels.noLabels')} - value={label !== null ? label : '*'} - onChange={(newLabel) => { - const currentLabel = useSettingsStore.getState().queryLabel; +
+ + key={selectKey} // Force re-render when data changes + className="min-w-[300px]" + triggerClassName="max-h-8 w-full overflow-hidden" + searchInputClassName="max-h-8" + triggerTooltip={t('graphPanel.graphLabels.selectTooltip')} + fetcher={fetchData} + renderOption={(item) => ( +
+ {item} +
+ )} + getOptionValue={(item) => item} + getDisplayValue={(item) => ( +
+ {item} +
+ )} + notFound={
{t('graphPanel.graphLabels.noLabels')}
} + ariaLabel={t('graphPanel.graphLabels.label')} + placeholder={t('graphPanel.graphLabels.placeholder')} + searchPlaceholder={t('graphPanel.graphLabels.placeholder')} + noResultsMessage={t('graphPanel.graphLabels.noLabels')} + value={label !== null ? label : '*'} + onChange={(newLabel) => { + const currentLabel = useSettingsStore.getState().queryLabel; - // select the last item means query all - if (newLabel === '...') { - newLabel = '*'; - } + // select the last item means query all + if (newLabel === '...') { + newLabel = '*'; + } - // Handle reselecting the same label - if (newLabel === currentLabel && newLabel !== '*') { - newLabel = '*'; - } + // Handle reselecting the same label + if (newLabel === currentLabel && newLabel !== '*') { + newLabel = '*'; + } - // Add selected label to search history (except for special cases) - if (newLabel && newLabel !== '*' && newLabel !== '...' && newLabel.trim() !== '') { - SearchHistoryManager.addToHistory(newLabel); - } + // Add selected label to search history (except for special cases) + if (newLabel && newLabel !== '*' && newLabel !== '...' && newLabel.trim() !== '') { + SearchHistoryManager.addToHistory(newLabel); + } - // Reset graphDataFetchAttempted flag to ensure data fetch is triggered - useGraphStore.getState().setGraphDataFetchAttempted(false); + // Reset graphDataFetchAttempted flag to ensure data fetch is triggered + useGraphStore.getState().setGraphDataFetchAttempted(false); - // Update the label to trigger data loading - useSettingsStore.getState().setQueryLabel(newLabel); - }} - clearable={false} // Prevent clearing value on reselect - debounceTime={500} - /> + // Update the label to trigger data loading + useSettingsStore.getState().setQueryLabel(newLabel); + }} + clearable={false} // Prevent clearing value on reselect + debounceTime={500} + /> +
) } From 04d23671474430bdeb4af8d61f593f942ed9f186 Mon Sep 17 00:00:00 2001 From: yangdx Date: Fri, 17 Oct 2025 20:36:15 +0800 Subject: [PATCH 4/6] Fix redoc access problem in front-end dev mode - Add /redoc endpoint to proxy config - Remove root path from API endpoints - Add .env.development to git reopo - Update sample environment files - Refine .gitignore patterns for env files --- .gitignore | 5 +++-- lightrag_webui/.env.development | 4 ++++ lightrag_webui/env.development.smaple | 4 +++- lightrag_webui/env.local.sample | 2 +- lightrag_webui/vite.config.ts | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 lightrag_webui/.env.development diff --git a/.gitignore b/.gitignore index 25650406..29e275b7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,10 @@ __pycache__/ # Virtual Environment .venv/ -env/ venv/ -*.env* + +# Enviroment Variable Files +.env # Build / Distribution dist/ diff --git a/lightrag_webui/.env.development b/lightrag_webui/.env.development new file mode 100644 index 00000000..5a6ac93f --- /dev/null +++ b/lightrag_webui/.env.development @@ -0,0 +1,4 @@ +# Development environment configuration +VITE_BACKEND_URL=http://localhost:9621 +VITE_API_PROXY=true +VITE_API_ENDPOINTS=/api,/documents,/graphs,/graph,/health,/query,/docs,/redoc,/openapi.json,/login,/auth-status diff --git a/lightrag_webui/env.development.smaple b/lightrag_webui/env.development.smaple index 080cf95f..5a6ac93f 100644 --- a/lightrag_webui/env.development.smaple +++ b/lightrag_webui/env.development.smaple @@ -1,2 +1,4 @@ # Development environment configuration -VITE_BACKEND_URL=/api +VITE_BACKEND_URL=http://localhost:9621 +VITE_API_PROXY=true +VITE_API_ENDPOINTS=/api,/documents,/graphs,/graph,/health,/query,/docs,/redoc,/openapi.json,/login,/auth-status diff --git a/lightrag_webui/env.local.sample b/lightrag_webui/env.local.sample index 0cd53ad5..d8920437 100644 --- a/lightrag_webui/env.local.sample +++ b/lightrag_webui/env.local.sample @@ -1,3 +1,3 @@ VITE_BACKEND_URL=http://localhost:9621 VITE_API_PROXY=true -VITE_API_ENDPOINTS=/,/api,/documents,/graphs,/graph,/health,/query,/docs,/openapi.json,/login,/auth-status +VITE_API_ENDPOINTS=/api,/documents,/graphs,/graph,/health,/query,/docs,/redoc,/openapi.json,/login,/auth-status diff --git a/lightrag_webui/vite.config.ts b/lightrag_webui/vite.config.ts index 2957b743..5cb52992 100644 --- a/lightrag_webui/vite.config.ts +++ b/lightrag_webui/vite.config.ts @@ -40,7 +40,7 @@ export default defineConfig({ changeOrigin: true, rewrite: endpoint === '/api' ? (path) => path.replace(/^\/api/, '') : - endpoint === '/docs' || endpoint === '/openapi.json' ? + endpoint === '/docs' || endpoint === '/redoc' || endpoint === '/openapi.json' ? (path) => path : undefined } ]) From dab1c358346e43ab4bd4e8c0a0dae1b43782dece Mon Sep 17 00:00:00 2001 From: yangdx Date: Fri, 17 Oct 2025 21:17:01 +0800 Subject: [PATCH 5/6] Optimize chat performance by reducing animations in inactive tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Add isTabActive prop to ChatMessage • Disable spinner in inactive tabs • Reduce opacity for inactive content • Hide loading indicator when inactive • Pass tab state from RetrievalTesting --- .../src/components/retrieval/ChatMessage.tsx | 23 +++++++++++++++---- .../src/features/RetrievalTesting.tsx | 6 ++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lightrag_webui/src/components/retrieval/ChatMessage.tsx b/lightrag_webui/src/components/retrieval/ChatMessage.tsx index b70e7bf0..7570f503 100644 --- a/lightrag_webui/src/components/retrieval/ChatMessage.tsx +++ b/lightrag_webui/src/components/retrieval/ChatMessage.tsx @@ -45,7 +45,13 @@ export type MessageWithError = Message & { } // Restore original component definition and export -export const ChatMessage = ({ message }: { message: MessageWithError }) => { // Remove isComplete prop +export const ChatMessage = ({ + message, + isTabActive = true +}: { + message: MessageWithError + isTabActive?: boolean +}) => { const { t } = useTranslation() const { theme } = useTheme() const [katexPlugin, setKatexPlugin] = useState<((options?: KaTeXOptions) => any) | null>(null) @@ -148,8 +154,13 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { // } rounded-lg px-4 py-2`} > {/* Thinking process display - only for assistant messages */} + {/* Always render to prevent layout shift when switching tabs */} {message.role === 'assistant' && (isThinking || thinkingTime !== null) && ( -
+
{ @@ -161,7 +172,8 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { // > {isThinking ? ( <> - + {/* Only show spinner animation in active tab to save resources */} + {isTabActive && } {t('retrievePanel.chatMessage.thinking')} ) : ( @@ -247,11 +259,12 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { //
)} - {(() => { + {/* Loading indicator - only show in active tab */} + {isTabActive && (() => { // More comprehensive loading state check const hasVisibleContent = finalDisplayContent && finalDisplayContent.trim() !== ''; const isLoadingState = !hasVisibleContent && !isThinking && !thinkingTime; - return isLoadingState && ; + return isLoadingState && })()}
) diff --git a/lightrag_webui/src/features/RetrievalTesting.tsx b/lightrag_webui/src/features/RetrievalTesting.tsx index 0f3e0869..3fe8ec13 100644 --- a/lightrag_webui/src/features/RetrievalTesting.tsx +++ b/lightrag_webui/src/features/RetrievalTesting.tsx @@ -103,6 +103,10 @@ const parseCOTContent = (content: string) => { export default function RetrievalTesting() { const { t } = useTranslation() + // Get current tab to determine if this tab is active (for performance optimization) + const currentTab = useSettingsStore.use.currentTab() + const isRetrievalTabActive = currentTab === 'retrieval' + const [messages, setMessages] = useState(() => { try { const history = useSettingsStore.getState().retrievalHistory || [] @@ -716,7 +720,7 @@ export default function RetrievalTesting() { )} - + {message.role === 'assistant' && (