diff --git a/README-zh.md b/README-zh.md index 94a11b61..d345562f 100644 --- a/README-zh.md +++ b/README-zh.md @@ -11,7 +11,6 @@ - [X] [2024.12.31]🎯📢LightRAG现在支持[通过文档ID删除](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#delete)。 - [X] [2024.11.25]🎯📢LightRAG现在支持无缝集成[自定义知识图谱](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#insert-custom-kg),使用户能够用自己的领域专业知识增强系统。 - [X] [2024.11.19]🎯📢LightRAG的综合指南现已在[LearnOpenCV](https://learnopencv.com/lightrag)上发布。非常感谢博客作者。 -- [X] [2024.11.12]🎯📢LightRAG现在支持[Oracle Database 23ai的所有存储类型(KV、向量和图)](https://github.com/HKUDS/LightRAG/blob/main/examples/lightrag_oracle_demo.py)。 - [X] [2024.11.11]🎯📢LightRAG现在支持[通过实体名称删除实体](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#delete)。 - [X] [2024.11.09]🎯📢推出[LightRAG Gui](https://lightrag-gui.streamlit.app),允许您插入、查询、可视化和下载LightRAG知识。 - [X] [2024.11.04]🎯📢现在您可以[使用Neo4J进行存储](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#using-neo4j-for-storage)。 @@ -1085,9 +1084,10 @@ rag.clear_cache(modes=["local"]) | **参数** | **类型** | **说明** | **默认值** | |--------------|----------|-----------------|-------------| | **working_dir** | `str` | 存储缓存的目录 | `lightrag_cache+timestamp` | -| **kv_storage** | `str` | 文档和文本块的存储类型。支持的类型:`JsonKVStorage`、`OracleKVStorage` | `JsonKVStorage` | -| **vector_storage** | `str` | 嵌入向量的存储类型。支持的类型:`NanoVectorDBStorage`、`OracleVectorDBStorage` | `NanoVectorDBStorage` | -| **graph_storage** | `str` | 图边和节点的存储类型。支持的类型:`NetworkXStorage`、`Neo4JStorage`、`OracleGraphStorage` | `NetworkXStorage` | +| **kv_storage** | `str` | Storage type for documents and text chunks. Supported types: `JsonKVStorage`,`PGKVStorage`,`RedisKVStorage`,`MongoKVStorage` | `JsonKVStorage` | +| **vector_storage** | `str` | Storage type for embedding vectors. Supported types: `NanoVectorDBStorage`,`PGVectorStorage`,`MilvusVectorDBStorage`,`ChromaVectorDBStorage`,`FaissVectorDBStorage`,`MongoVectorDBStorage`,`QdrantVectorDBStorage` | `NanoVectorDBStorage` | +| **graph_storage** | `str` | Storage type for graph edges and nodes. Supported types: `NetworkXStorage`,`Neo4JStorage`,`PGGraphStorage`,`AGEStorage` | `NetworkXStorage` | +| **doc_status_storage** | `str` | Storage type for documents process status. Supported types: `JsonDocStatusStorage`,`PGDocStatusStorage`,`MongoDocStatusStorage` | `JsonDocStatusStorage` | | **chunk_token_size** | `int` | 拆分文档时每个块的最大令牌大小 | `1200` | | **chunk_overlap_token_size** | `int` | 拆分文档时两个块之间的重叠令牌大小 | `100` | | **tiktoken_model_name** | `str` | 用于计算令牌数的Tiktoken编码器的模型名称 | `gpt-4o-mini` | diff --git a/README.md b/README.md index 0d04b015..e154c719 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,6 @@ - [X] [2024.12.31]🎯📢LightRAG now supports [deletion by document ID](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#delete). - [X] [2024.11.25]🎯📢LightRAG now supports seamless integration of [custom knowledge graphs](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#insert-custom-kg), empowering users to enhance the system with their own domain expertise. - [X] [2024.11.19]🎯📢A comprehensive guide to LightRAG is now available on [LearnOpenCV](https://learnopencv.com/lightrag). Many thanks to the blog author. -- [X] [2024.11.12]🎯📢LightRAG now supports [Oracle Database 23ai for all storage types (KV, vector, and graph)](https://github.com/HKUDS/LightRAG/blob/main/examples/lightrag_oracle_demo.py). - [X] [2024.11.11]🎯📢LightRAG now supports [deleting entities by their names](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#delete). - [X] [2024.11.09]🎯📢Introducing the [LightRAG Gui](https://lightrag-gui.streamlit.app), which allows you to insert, query, visualize, and download LightRAG knowledge. - [X] [2024.11.04]🎯📢You can now [use Neo4J for Storage](https://github.com/HKUDS/LightRAG?tab=readme-ov-file#using-neo4j-for-storage). @@ -1145,9 +1144,10 @@ Valid modes are: | **Parameter** | **Type** | **Explanation** | **Default** | |--------------|----------|-----------------|-------------| | **working_dir** | `str` | Directory where the cache will be stored | `lightrag_cache+timestamp` | -| **kv_storage** | `str` | Storage type for documents and text chunks. Supported types: `JsonKVStorage`, `OracleKVStorage` | `JsonKVStorage` | -| **vector_storage** | `str` | Storage type for embedding vectors. Supported types: `NanoVectorDBStorage`, `OracleVectorDBStorage` | `NanoVectorDBStorage` | -| **graph_storage** | `str` | Storage type for graph edges and nodes. Supported types: `NetworkXStorage`, `Neo4JStorage`, `OracleGraphStorage` | `NetworkXStorage` | +| **kv_storage** | `str` | Storage type for documents and text chunks. Supported types: `JsonKVStorage`,`PGKVStorage`,`RedisKVStorage`,`MongoKVStorage` | `JsonKVStorage` | +| **vector_storage** | `str` | Storage type for embedding vectors. Supported types: `NanoVectorDBStorage`,`PGVectorStorage`,`MilvusVectorDBStorage`,`ChromaVectorDBStorage`,`FaissVectorDBStorage`,`MongoVectorDBStorage`,`QdrantVectorDBStorage` | `NanoVectorDBStorage` | +| **graph_storage** | `str` | Storage type for graph edges and nodes. Supported types: `NetworkXStorage`,`Neo4JStorage`,`PGGraphStorage`,`AGEStorage` | `NetworkXStorage` | +| **doc_status_storage** | `str` | Storage type for documents process status. Supported types: `JsonDocStatusStorage`,`PGDocStatusStorage`,`MongoDocStatusStorage` | `JsonDocStatusStorage` | | **chunk_token_size** | `int` | Maximum token size per chunk when splitting documents | `1200` | | **chunk_overlap_token_size** | `int` | Overlap token size between two chunks when splitting documents | `100` | | **tiktoken_model_name** | `str` | Model name for the Tiktoken encoder used to calculate token numbers | `gpt-4o-mini` | diff --git a/config.ini.example b/config.ini.example index 3041611e..5ff7cfbb 100644 --- a/config.ini.example +++ b/config.ini.example @@ -13,23 +13,6 @@ uri=redis://localhost:6379/1 [qdrant] uri = http://localhost:16333 -[oracle] -dsn = localhost:1521/XEPDB1 -user = your_username -password = your_password -config_dir = /path/to/oracle/config -wallet_location = /path/to/wallet # 可选 -wallet_password = your_wallet_password # 可选 -workspace = default # 可选,默认为default - -[tidb] -host = localhost -port = 4000 -user = your_username -password = your_password -database = your_database -workspace = default # 可选,默认为default - [postgres] host = localhost port = 5432 diff --git a/env.example b/env.example index 20d80d43..d21bbef6 100644 --- a/env.example +++ b/env.example @@ -4,11 +4,9 @@ # HOST=0.0.0.0 # PORT=9621 # WORKERS=2 -### separating data from difference Lightrag instances -# NAMESPACE_PREFIX=lightrag -### Max nodes return from grap retrieval -# MAX_GRAPH_NODES=1000 # CORS_ORIGINS=http://localhost:3000,http://localhost:8080 +WEBUI_TITLE='Graph RAG Engine' +WEBUI_DESCRIPTION="Simple and Fast Graph Based RAG System" ### Optional SSL Configuration # SSL=true @@ -22,6 +20,9 @@ ### Ollama Emulating Model Tag # OLLAMA_EMULATING_MODEL_TAG=latest +### Max nodes return from grap retrieval +# MAX_GRAPH_NODES=1000 + ### Logging level # LOG_LEVEL=INFO # VERBOSE=False @@ -110,24 +111,14 @@ LIGHTRAG_VECTOR_STORAGE=NanoVectorDBStorage LIGHTRAG_GRAPH_STORAGE=NetworkXStorage LIGHTRAG_DOC_STATUS_STORAGE=JsonDocStatusStorage -### Oracle Database Configuration -ORACLE_DSN=localhost:1521/XEPDB1 -ORACLE_USER=your_username -ORACLE_PASSWORD='your_password' -ORACLE_CONFIG_DIR=/path/to/oracle/config -#ORACLE_WALLET_LOCATION=/path/to/wallet -#ORACLE_WALLET_PASSWORD='your_password' -### separating all data from difference Lightrag instances(deprecating, use NAMESPACE_PREFIX in future) -#ORACLE_WORKSPACE=default - -### TiDB Configuration -TIDB_HOST=localhost -TIDB_PORT=4000 -TIDB_USER=your_username -TIDB_PASSWORD='your_password' -TIDB_DATABASE=your_database -### separating all data from difference Lightrag instances(deprecating, use NAMESPACE_PREFIX in future) -#TIDB_WORKSPACE=default +### TiDB Configuration (Deprecated) +# TIDB_HOST=localhost +# TIDB_PORT=4000 +# TIDB_USER=your_username +# TIDB_PASSWORD='your_password' +# TIDB_DATABASE=your_database +### separating all data from difference Lightrag instances(deprecating) +# TIDB_WORKSPACE=default ### PostgreSQL Configuration POSTGRES_HOST=localhost @@ -135,8 +126,8 @@ POSTGRES_PORT=5432 POSTGRES_USER=your_username POSTGRES_PASSWORD='your_password' POSTGRES_DATABASE=your_database -### separating all data from difference Lightrag instances(deprecating, use NAMESPACE_PREFIX in future) -#POSTGRES_WORKSPACE=default +### separating all data from difference Lightrag instances(deprecating) +# POSTGRES_WORKSPACE=default ### Independent AGM Configuration(not for AMG embedded in PostreSQL) AGE_POSTGRES_DB= @@ -145,8 +136,8 @@ AGE_POSTGRES_PASSWORD= AGE_POSTGRES_HOST= # AGE_POSTGRES_PORT=8529 -### separating all data from difference Lightrag instances(deprecating, use NAMESPACE_PREFIX in future) # AGE Graph Name(apply to PostgreSQL and independent AGM) +### AGE_GRAPH_NAME is precated # AGE_GRAPH_NAME=lightrag ### Neo4j Configuration @@ -157,7 +148,7 @@ NEO4J_PASSWORD='your_password' ### MongoDB Configuration MONGO_URI=mongodb://root:root@localhost:27017/ MONGO_DATABASE=LightRAG -### separating all data from difference Lightrag instances(deprecating, use NAMESPACE_PREFIX in future) +### separating all data from difference Lightrag instances(deprecating) # MONGODB_GRAPH=false ### Milvus Configuration @@ -177,7 +168,9 @@ REDIS_URI=redis://localhost:6379 ### For JWT Auth # AUTH_ACCOUNTS='admin:admin123,user1:pass456' # TOKEN_SECRET=Your-Key-For-LightRAG-API-Server -# TOKEN_EXPIRE_HOURS=4 +# TOKEN_EXPIRE_HOURS=48 +# GUEST_TOKEN_EXPIRE_HOURS=24 +# JWT_ALGORITHM=HS256 ### API-Key to access LightRAG Server API # LIGHTRAG_API_KEY=your-secure-api-key-here diff --git a/examples/lightrag_api_ollama_demo.py b/examples/lightrag_api_ollama_demo.py deleted file mode 100644 index dad2a2e0..00000000 --- a/examples/lightrag_api_ollama_demo.py +++ /dev/null @@ -1,188 +0,0 @@ -from fastapi import FastAPI, HTTPException, File, UploadFile -from contextlib import asynccontextmanager -from pydantic import BaseModel -import os -from lightrag import LightRAG, QueryParam -from lightrag.llm.ollama import ollama_embed, ollama_model_complete -from lightrag.utils import EmbeddingFunc -from typing import Optional -import asyncio -import nest_asyncio -import aiofiles -from lightrag.kg.shared_storage import initialize_pipeline_status - -# Apply nest_asyncio to solve event loop issues -nest_asyncio.apply() - -DEFAULT_RAG_DIR = "index_default" - -DEFAULT_INPUT_FILE = "book.txt" -INPUT_FILE = os.environ.get("INPUT_FILE", f"{DEFAULT_INPUT_FILE}") -print(f"INPUT_FILE: {INPUT_FILE}") - -# Configure working directory -WORKING_DIR = os.environ.get("RAG_DIR", f"{DEFAULT_RAG_DIR}") -print(f"WORKING_DIR: {WORKING_DIR}") - - -if not os.path.exists(WORKING_DIR): - os.mkdir(WORKING_DIR) - - -async def init(): - rag = LightRAG( - working_dir=WORKING_DIR, - llm_model_func=ollama_model_complete, - llm_model_name="gemma2:9b", - llm_model_max_async=4, - llm_model_max_token_size=8192, - llm_model_kwargs={ - "host": "http://localhost:11434", - "options": {"num_ctx": 8192}, - }, - embedding_func=EmbeddingFunc( - embedding_dim=768, - max_token_size=8192, - func=lambda texts: ollama_embed( - texts, embed_model="nomic-embed-text", host="http://localhost:11434" - ), - ), - ) - - # Add initialization code - await rag.initialize_storages() - await initialize_pipeline_status() - - return rag - - -@asynccontextmanager -async def lifespan(app: FastAPI): - global rag - rag = await init() - print("done!") - yield - - -app = FastAPI( - title="LightRAG API", description="API for RAG operations", lifespan=lifespan -) - - -# Data models -class QueryRequest(BaseModel): - query: str - mode: str = "hybrid" - only_need_context: bool = False - - -class InsertRequest(BaseModel): - text: str - - -class Response(BaseModel): - status: str - data: Optional[str] = None - message: Optional[str] = None - - -# API routes -@app.post("/query", response_model=Response) -async def query_endpoint(request: QueryRequest): - try: - loop = asyncio.get_event_loop() - result = await loop.run_in_executor( - None, - lambda: rag.query( - request.query, - param=QueryParam( - mode=request.mode, only_need_context=request.only_need_context - ), - ), - ) - return Response(status="success", data=result) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -# insert by text -@app.post("/insert", response_model=Response) -async def insert_endpoint(request: InsertRequest): - try: - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, lambda: rag.insert(request.text)) - return Response(status="success", message="Text inserted successfully") - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -# insert by file in payload -@app.post("/insert_file", response_model=Response) -async def insert_file(file: UploadFile = File(...)): - try: - file_content = await file.read() - # Read file content - try: - content = file_content.decode("utf-8") - except UnicodeDecodeError: - # If UTF-8 decoding fails, try other encodings - content = file_content.decode("gbk") - # Insert file content - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, lambda: rag.insert(content)) - - return Response( - status="success", - message=f"File content from {file.filename} inserted successfully", - ) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -# insert by local default file -@app.post("/insert_default_file", response_model=Response) -@app.get("/insert_default_file", response_model=Response) -async def insert_default_file(): - try: - # Read file content from book.txt - async with aiofiles.open(INPUT_FILE, "r", encoding="utf-8") as file: - content = await file.read() - print(f"read input file {INPUT_FILE} successfully") - # Insert file content - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, lambda: rag.insert(content)) - - return Response( - status="success", - message=f"File content from {INPUT_FILE} inserted successfully", - ) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -@app.get("/health") -async def health_check(): - return {"status": "healthy"} - - -if __name__ == "__main__": - import uvicorn - - uvicorn.run(app, host="0.0.0.0", port=8020) - -# Usage example -# To run the server, use the following command in your terminal: -# python lightrag_api_openai_compatible_demo.py - -# Example requests: -# 1. Query: -# curl -X POST "http://127.0.0.1:8020/query" -H "Content-Type: application/json" -d '{"query": "your query here", "mode": "hybrid"}' - -# 2. Insert text: -# curl -X POST "http://127.0.0.1:8020/insert" -H "Content-Type: application/json" -d '{"text": "your text here"}' - -# 3. Insert file: -# curl -X POST "http://127.0.0.1:8020/insert_file" -H "Content-Type: multipart/form-data" -F "file=@path/to/your/file.txt" - -# 4. Health check: -# curl -X GET "http://127.0.0.1:8020/health" diff --git a/examples/lightrag_api_openai_compatible_demo.py b/examples/lightrag_api_openai_compatible_demo.py deleted file mode 100644 index 312be872..00000000 --- a/examples/lightrag_api_openai_compatible_demo.py +++ /dev/null @@ -1,204 +0,0 @@ -from fastapi import FastAPI, HTTPException, File, UploadFile -from contextlib import asynccontextmanager -from pydantic import BaseModel -import os -from lightrag import LightRAG, QueryParam -from lightrag.llm.openai import openai_complete_if_cache, openai_embed -from lightrag.utils import EmbeddingFunc -import numpy as np -from typing import Optional -import asyncio -import nest_asyncio -from lightrag.kg.shared_storage import initialize_pipeline_status - -# Apply nest_asyncio to solve event loop issues -nest_asyncio.apply() - -DEFAULT_RAG_DIR = "index_default" -app = FastAPI(title="LightRAG API", description="API for RAG operations") - -# Configure working directory -WORKING_DIR = os.environ.get("RAG_DIR", f"{DEFAULT_RAG_DIR}") -print(f"WORKING_DIR: {WORKING_DIR}") -LLM_MODEL = os.environ.get("LLM_MODEL", "gpt-4o-mini") -print(f"LLM_MODEL: {LLM_MODEL}") -EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL", "text-embedding-3-large") -print(f"EMBEDDING_MODEL: {EMBEDDING_MODEL}") -EMBEDDING_MAX_TOKEN_SIZE = int(os.environ.get("EMBEDDING_MAX_TOKEN_SIZE", 8192)) -print(f"EMBEDDING_MAX_TOKEN_SIZE: {EMBEDDING_MAX_TOKEN_SIZE}") -BASE_URL = os.environ.get("BASE_URL", "https://api.openai.com/v1") -print(f"BASE_URL: {BASE_URL}") -API_KEY = os.environ.get("API_KEY", "xxxxxxxx") -print(f"API_KEY: {API_KEY}") - -if not os.path.exists(WORKING_DIR): - os.mkdir(WORKING_DIR) - - -# LLM model function - - -async def llm_model_func( - prompt, system_prompt=None, history_messages=[], keyword_extraction=False, **kwargs -) -> str: - return await openai_complete_if_cache( - model=LLM_MODEL, - prompt=prompt, - system_prompt=system_prompt, - history_messages=history_messages, - base_url=BASE_URL, - api_key=API_KEY, - **kwargs, - ) - - -# Embedding function - - -async def embedding_func(texts: list[str]) -> np.ndarray: - return await openai_embed( - texts=texts, - model=EMBEDDING_MODEL, - base_url=BASE_URL, - api_key=API_KEY, - ) - - -async def get_embedding_dim(): - test_text = ["This is a test sentence."] - embedding = await embedding_func(test_text) - embedding_dim = embedding.shape[1] - print(f"{embedding_dim=}") - return embedding_dim - - -# Initialize RAG instance -async def init(): - embedding_dimension = await get_embedding_dim() - - rag = LightRAG( - working_dir=WORKING_DIR, - llm_model_func=llm_model_func, - embedding_func=EmbeddingFunc( - embedding_dim=embedding_dimension, - max_token_size=EMBEDDING_MAX_TOKEN_SIZE, - func=embedding_func, - ), - ) - - await rag.initialize_storages() - await initialize_pipeline_status() - - return rag - - -@asynccontextmanager -async def lifespan(app: FastAPI): - global rag - rag = await init() - print("done!") - yield - - -app = FastAPI( - title="LightRAG API", description="API for RAG operations", lifespan=lifespan -) - -# Data models - - -class QueryRequest(BaseModel): - query: str - mode: str = "hybrid" - only_need_context: bool = False - - -class InsertRequest(BaseModel): - text: str - - -class Response(BaseModel): - status: str - data: Optional[str] = None - message: Optional[str] = None - - -# API routes - - -@app.post("/query", response_model=Response) -async def query_endpoint(request: QueryRequest): - try: - loop = asyncio.get_event_loop() - result = await loop.run_in_executor( - None, - lambda: rag.query( - request.query, - param=QueryParam( - mode=request.mode, only_need_context=request.only_need_context - ), - ), - ) - return Response(status="success", data=result) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -@app.post("/insert", response_model=Response) -async def insert_endpoint(request: InsertRequest): - try: - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, lambda: rag.insert(request.text)) - return Response(status="success", message="Text inserted successfully") - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -@app.post("/insert_file", response_model=Response) -async def insert_file(file: UploadFile = File(...)): - try: - file_content = await file.read() - # Read file content - try: - content = file_content.decode("utf-8") - except UnicodeDecodeError: - # If UTF-8 decoding fails, try other encodings - content = file_content.decode("gbk") - # Insert file content - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, lambda: rag.insert(content)) - - return Response( - status="success", - message=f"File content from {file.filename} inserted successfully", - ) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -@app.get("/health") -async def health_check(): - return {"status": "healthy"} - - -if __name__ == "__main__": - import uvicorn - - uvicorn.run(app, host="0.0.0.0", port=8020) - -# Usage example -# To run the server, use the following command in your terminal: -# python lightrag_api_openai_compatible_demo.py - -# Example requests: -# 1. Query: -# curl -X POST "http://127.0.0.1:8020/query" -H "Content-Type: application/json" -d '{"query": "your query here", "mode": "hybrid"}' - -# 2. Insert text: -# curl -X POST "http://127.0.0.1:8020/insert" -H "Content-Type: application/json" -d '{"text": "your text here"}' - -# 3. Insert file: -# curl -X POST "http://127.0.0.1:8020/insert_file" -H "Content-Type: multipart/form-data" -F "file=@path/to/your/file.txt" - -# 4. Health check: -# curl -X GET "http://127.0.0.1:8020/health" diff --git a/examples/lightrag_api_oracle_demo.py b/examples/lightrag_api_oracle_demo.py deleted file mode 100644 index 3a82f479..00000000 --- a/examples/lightrag_api_oracle_demo.py +++ /dev/null @@ -1,267 +0,0 @@ -from fastapi import FastAPI, HTTPException, File, UploadFile -from fastapi import Query -from contextlib import asynccontextmanager -from pydantic import BaseModel -from typing import Optional, Any - -import sys -import os - - -from pathlib import Path - -import asyncio -import nest_asyncio -from lightrag import LightRAG, QueryParam -from lightrag.llm.openai import openai_complete_if_cache, openai_embed -from lightrag.utils import EmbeddingFunc -import numpy as np -from lightrag.kg.shared_storage import initialize_pipeline_status - - -print(os.getcwd()) -script_directory = Path(__file__).resolve().parent.parent -sys.path.append(os.path.abspath(script_directory)) - - -# Apply nest_asyncio to solve event loop issues -nest_asyncio.apply() - -DEFAULT_RAG_DIR = "index_default" - - -# We use OpenAI compatible API to call LLM on Oracle Cloud -# More docs here https://github.com/jin38324/OCI_GenAI_access_gateway -BASE_URL = "http://xxx.xxx.xxx.xxx:8088/v1/" -APIKEY = "ocigenerativeai" - -# Configure working directory -WORKING_DIR = os.environ.get("RAG_DIR", f"{DEFAULT_RAG_DIR}") -print(f"WORKING_DIR: {WORKING_DIR}") -LLM_MODEL = os.environ.get("LLM_MODEL", "cohere.command-r-plus-08-2024") -print(f"LLM_MODEL: {LLM_MODEL}") -EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL", "cohere.embed-multilingual-v3.0") -print(f"EMBEDDING_MODEL: {EMBEDDING_MODEL}") -EMBEDDING_MAX_TOKEN_SIZE = int(os.environ.get("EMBEDDING_MAX_TOKEN_SIZE", 512)) -print(f"EMBEDDING_MAX_TOKEN_SIZE: {EMBEDDING_MAX_TOKEN_SIZE}") - -if not os.path.exists(WORKING_DIR): - os.mkdir(WORKING_DIR) - -os.environ["ORACLE_USER"] = "" -os.environ["ORACLE_PASSWORD"] = "" -os.environ["ORACLE_DSN"] = "" -os.environ["ORACLE_CONFIG_DIR"] = "path_to_config_dir" -os.environ["ORACLE_WALLET_LOCATION"] = "path_to_wallet_location" -os.environ["ORACLE_WALLET_PASSWORD"] = "wallet_password" -os.environ["ORACLE_WORKSPACE"] = "company" - - -async def llm_model_func( - prompt, system_prompt=None, history_messages=[], keyword_extraction=False, **kwargs -) -> str: - return await openai_complete_if_cache( - LLM_MODEL, - prompt, - system_prompt=system_prompt, - history_messages=history_messages, - api_key=APIKEY, - base_url=BASE_URL, - **kwargs, - ) - - -async def embedding_func(texts: list[str]) -> np.ndarray: - return await openai_embed( - texts, - model=EMBEDDING_MODEL, - api_key=APIKEY, - base_url=BASE_URL, - ) - - -async def get_embedding_dim(): - test_text = ["This is a test sentence."] - embedding = await embedding_func(test_text) - embedding_dim = embedding.shape[1] - return embedding_dim - - -async def init(): - # Detect embedding dimension - embedding_dimension = await get_embedding_dim() - print(f"Detected embedding dimension: {embedding_dimension}") - # Create Oracle DB connection - # The `config` parameter is the connection configuration of Oracle DB - # More docs here https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html - # We storage data in unified tables, so we need to set a `workspace` parameter to specify which docs we want to store and query - # Below is an example of how to connect to Oracle Autonomous Database on Oracle Cloud - - # Initialize LightRAG - # We use Oracle DB as the KV/vector/graph storage - rag = LightRAG( - enable_llm_cache=False, - working_dir=WORKING_DIR, - chunk_token_size=512, - llm_model_func=llm_model_func, - embedding_func=EmbeddingFunc( - embedding_dim=embedding_dimension, - max_token_size=512, - func=embedding_func, - ), - graph_storage="OracleGraphStorage", - kv_storage="OracleKVStorage", - vector_storage="OracleVectorDBStorage", - ) - - await rag.initialize_storages() - await initialize_pipeline_status() - - return rag - - -# Extract and Insert into LightRAG storage -# with open("./dickens/book.txt", "r", encoding="utf-8") as f: -# await rag.ainsert(f.read()) - -# # Perform search in different modes -# modes = ["naive", "local", "global", "hybrid"] -# for mode in modes: -# print("="*20, mode, "="*20) -# print(await rag.aquery("这篇文档是关于什么内容的?", param=QueryParam(mode=mode))) -# print("-"*100, "\n") - -# Data models - - -class QueryRequest(BaseModel): - query: str - mode: str = "hybrid" - only_need_context: bool = False - only_need_prompt: bool = False - - -class DataRequest(BaseModel): - limit: int = 100 - - -class InsertRequest(BaseModel): - text: str - - -class Response(BaseModel): - status: str - data: Optional[Any] = None - message: Optional[str] = None - - -# API routes - -rag = None - - -@asynccontextmanager -async def lifespan(app: FastAPI): - global rag - rag = await init() - print("done!") - yield - - -app = FastAPI( - title="LightRAG API", description="API for RAG operations", lifespan=lifespan -) - - -@app.post("/query", response_model=Response) -async def query_endpoint(request: QueryRequest): - # try: - # loop = asyncio.get_event_loop() - if request.mode == "naive": - top_k = 3 - else: - top_k = 60 - result = await rag.aquery( - request.query, - param=QueryParam( - mode=request.mode, - only_need_context=request.only_need_context, - only_need_prompt=request.only_need_prompt, - top_k=top_k, - ), - ) - return Response(status="success", data=result) - # except Exception as e: - # raise HTTPException(status_code=500, detail=str(e)) - - -@app.get("/data", response_model=Response) -async def query_all_nodes(type: str = Query("nodes"), limit: int = Query(100)): - if type == "nodes": - result = await rag.chunk_entity_relation_graph.get_all_nodes(limit=limit) - elif type == "edges": - result = await rag.chunk_entity_relation_graph.get_all_edges(limit=limit) - elif type == "statistics": - result = await rag.chunk_entity_relation_graph.get_statistics() - return Response(status="success", data=result) - - -@app.post("/insert", response_model=Response) -async def insert_endpoint(request: InsertRequest): - try: - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, lambda: rag.insert(request.text)) - return Response(status="success", message="Text inserted successfully") - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -@app.post("/insert_file", response_model=Response) -async def insert_file(file: UploadFile = File(...)): - try: - file_content = await file.read() - # Read file content - try: - content = file_content.decode("utf-8") - except UnicodeDecodeError: - # If UTF-8 decoding fails, try other encodings - content = file_content.decode("gbk") - # Insert file content - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, lambda: rag.insert(content)) - - return Response( - status="success", - message=f"File content from {file.filename} inserted successfully", - ) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - -@app.get("/health") -async def health_check(): - return {"status": "healthy"} - - -if __name__ == "__main__": - import uvicorn - - uvicorn.run(app, host="127.0.0.1", port=8020) - -# Usage example -# To run the server, use the following command in your terminal: -# python lightrag_api_openai_compatible_demo.py - -# Example requests: -# 1. Query: -# curl -X POST "http://127.0.0.1:8020/query" -H "Content-Type: application/json" -d '{"query": "your query here", "mode": "hybrid"}' - -# 2. Insert text: -# curl -X POST "http://127.0.0.1:8020/insert" -H "Content-Type: application/json" -d '{"text": "your text here"}' - -# 3. Insert file: -# curl -X POST "http://127.0.0.1:8020/insert_file" -H "Content-Type: multipart/form-data" -F "file=@path/to/your/file.txt" - - -# 4. Health check: -# curl -X GET "http://127.0.0.1:8020/health" diff --git a/examples/lightrag_ollama_gremlin_demo.py b/examples/lightrag_ollama_gremlin_demo.py index 893b5606..7ae62810 100644 --- a/examples/lightrag_ollama_gremlin_demo.py +++ b/examples/lightrag_ollama_gremlin_demo.py @@ -1,3 +1,7 @@ +############################################## +# Gremlin storage implementation is deprecated +############################################## + import asyncio import inspect import os diff --git a/examples/lightrag_oracle_demo.py b/examples/lightrag_oracle_demo.py deleted file mode 100644 index 6663f6a1..00000000 --- a/examples/lightrag_oracle_demo.py +++ /dev/null @@ -1,141 +0,0 @@ -import sys -import os -from pathlib import Path -import asyncio -from lightrag import LightRAG, QueryParam -from lightrag.llm.openai import openai_complete_if_cache, openai_embed -from lightrag.utils import EmbeddingFunc -import numpy as np -from lightrag.kg.shared_storage import initialize_pipeline_status - -print(os.getcwd()) -script_directory = Path(__file__).resolve().parent.parent -sys.path.append(os.path.abspath(script_directory)) - -WORKING_DIR = "./dickens" - -# We use OpenAI compatible API to call LLM on Oracle Cloud -# More docs here https://github.com/jin38324/OCI_GenAI_access_gateway -BASE_URL = "http://xxx.xxx.xxx.xxx:8088/v1/" -APIKEY = "ocigenerativeai" -CHATMODEL = "cohere.command-r-plus" -EMBEDMODEL = "cohere.embed-multilingual-v3.0" -CHUNK_TOKEN_SIZE = 1024 -MAX_TOKENS = 4000 - -if not os.path.exists(WORKING_DIR): - os.mkdir(WORKING_DIR) - -os.environ["ORACLE_USER"] = "username" -os.environ["ORACLE_PASSWORD"] = "xxxxxxxxx" -os.environ["ORACLE_DSN"] = "xxxxxxx_medium" -os.environ["ORACLE_CONFIG_DIR"] = "path_to_config_dir" -os.environ["ORACLE_WALLET_LOCATION"] = "path_to_wallet_location" -os.environ["ORACLE_WALLET_PASSWORD"] = "wallet_password" -os.environ["ORACLE_WORKSPACE"] = "company" - - -async def llm_model_func( - prompt, system_prompt=None, history_messages=[], keyword_extraction=False, **kwargs -) -> str: - return await openai_complete_if_cache( - CHATMODEL, - prompt, - system_prompt=system_prompt, - history_messages=history_messages, - api_key=APIKEY, - base_url=BASE_URL, - **kwargs, - ) - - -async def embedding_func(texts: list[str]) -> np.ndarray: - return await openai_embed( - texts, - model=EMBEDMODEL, - api_key=APIKEY, - base_url=BASE_URL, - ) - - -async def get_embedding_dim(): - test_text = ["This is a test sentence."] - embedding = await embedding_func(test_text) - embedding_dim = embedding.shape[1] - return embedding_dim - - -async def initialize_rag(): - # Detect embedding dimension - embedding_dimension = await get_embedding_dim() - print(f"Detected embedding dimension: {embedding_dimension}") - - # Initialize LightRAG - # We use Oracle DB as the KV/vector/graph storage - # You can add `addon_params={"example_number": 1, "language": "Simplfied Chinese"}` to control the prompt - rag = LightRAG( - # log_level="DEBUG", - working_dir=WORKING_DIR, - entity_extract_max_gleaning=1, - enable_llm_cache=True, - enable_llm_cache_for_entity_extract=True, - embedding_cache_config=None, # {"enabled": True,"similarity_threshold": 0.90}, - chunk_token_size=CHUNK_TOKEN_SIZE, - llm_model_max_token_size=MAX_TOKENS, - llm_model_func=llm_model_func, - embedding_func=EmbeddingFunc( - embedding_dim=embedding_dimension, - max_token_size=500, - func=embedding_func, - ), - graph_storage="OracleGraphStorage", - kv_storage="OracleKVStorage", - vector_storage="OracleVectorDBStorage", - addon_params={ - "example_number": 1, - "language": "Simplfied Chinese", - "entity_types": ["organization", "person", "geo", "event"], - "insert_batch_size": 2, - }, - ) - await rag.initialize_storages() - await initialize_pipeline_status() - - return rag - - -async def main(): - try: - # Initialize RAG instance - rag = await initialize_rag() - - # Extract and Insert into LightRAG storage - with open(WORKING_DIR + "/docs.txt", "r", encoding="utf-8") as f: - all_text = f.read() - texts = [x for x in all_text.split("\n") if x] - - # New mode use pipeline - await rag.apipeline_enqueue_documents(texts) - await rag.apipeline_process_enqueue_documents() - - # Old method use ainsert - # await rag.ainsert(texts) - - # Perform search in different modes - modes = ["naive", "local", "global", "hybrid"] - for mode in modes: - print("=" * 20, mode, "=" * 20) - print( - await rag.aquery( - "What are the top themes in this story?", - param=QueryParam(mode=mode), - ) - ) - print("-" * 100, "\n") - - except Exception as e: - print(f"An error occurred: {e}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/lightrag_tidb_demo.py b/examples/lightrag_tidb_demo.py index 52695560..50eac2ca 100644 --- a/examples/lightrag_tidb_demo.py +++ b/examples/lightrag_tidb_demo.py @@ -1,3 +1,7 @@ +########################################### +# TiDB storage implementation is deprecated +########################################### + import asyncio import os diff --git a/lightrag/api/README-zh.md b/lightrag/api/README-zh.md index 4bf31a61..03718656 100644 --- a/lightrag/api/README-zh.md +++ b/lightrag/api/README-zh.md @@ -291,11 +291,9 @@ LightRAG 使用 4 种类型的存储用于不同目的: ``` JsonKVStorage JsonFile(默认) -MongoKVStorage MogonDB -RedisKVStorage Redis -TiDBKVStorage TiDB PGKVStorage Postgres -OracleKVStorage Oracle +RedisKVStorage Redis +MongoKVStorage MogonDB ``` * GRAPH_STORAGE 支持的实现名称 @@ -303,25 +301,19 @@ OracleKVStorage Oracle ``` NetworkXStorage NetworkX(默认) Neo4JStorage Neo4J -MongoGraphStorage MongoDB -TiDBGraphStorage TiDB -AGEStorage AGE -GremlinStorage Gremlin PGGraphStorage Postgres -OracleGraphStorage Postgres +AGEStorage AGE ``` * VECTOR_STORAGE 支持的实现名称 ``` NanoVectorDBStorage NanoVector(默认) +PGVectorStorage Postgres MilvusVectorDBStorge Milvus ChromaVectorDBStorage Chroma -TiDBVectorDBStorage TiDB -PGVectorStorage Postgres FaissVectorDBStorage Faiss QdrantVectorDBStorage Qdrant -OracleVectorDBStorage Oracle MongoVectorDBStorage MongoDB ``` diff --git a/lightrag/api/README.md b/lightrag/api/README.md index 8b2e8177..27f3d14a 100644 --- a/lightrag/api/README.md +++ b/lightrag/api/README.md @@ -302,11 +302,9 @@ Each storage type have servals implementations: ``` JsonKVStorage JsonFile(default) -MongoKVStorage MogonDB -RedisKVStorage Redis -TiDBKVStorage TiDB PGKVStorage Postgres -OracleKVStorage Oracle +RedisKVStorage Redis +MongoKVStorage MogonDB ``` * GRAPH_STORAGE supported implement-name @@ -314,25 +312,19 @@ OracleKVStorage Oracle ``` NetworkXStorage NetworkX(defualt) Neo4JStorage Neo4J -MongoGraphStorage MongoDB -TiDBGraphStorage TiDB -AGEStorage AGE -GremlinStorage Gremlin PGGraphStorage Postgres -OracleGraphStorage Postgres +AGEStorage AGE ``` * VECTOR_STORAGE supported implement-name ``` NanoVectorDBStorage NanoVector(default) -MilvusVectorDBStorage Milvus -ChromaVectorDBStorage Chroma -TiDBVectorDBStorage TiDB PGVectorStorage Postgres +MilvusVectorDBStorge Milvus +ChromaVectorDBStorage Chroma FaissVectorDBStorage Faiss QdrantVectorDBStorage Qdrant -OracleVectorDBStorage Oracle MongoVectorDBStorage MongoDB ``` diff --git a/lightrag/api/__init__.py b/lightrag/api/__init__.py index ec1959de..8679dd51 100644 --- a/lightrag/api/__init__.py +++ b/lightrag/api/__init__.py @@ -1 +1 @@ -__api_version__ = "1.2.8" +__api_version__ = "0132" diff --git a/lightrag/api/auth.py b/lightrag/api/auth.py index 58175b9d..0b61095d 100644 --- a/lightrag/api/auth.py +++ b/lightrag/api/auth.py @@ -1,9 +1,11 @@ -import os from datetime import datetime, timedelta + import jwt +from dotenv import load_dotenv from fastapi import HTTPException, status from pydantic import BaseModel -from dotenv import load_dotenv + +from .config import global_args # use the .env that is inside the current folder # allows to use different .env file for each lightrag instance @@ -20,13 +22,12 @@ class TokenPayload(BaseModel): class AuthHandler: def __init__(self): - self.secret = os.getenv("TOKEN_SECRET", "4f85ds4f56dsf46") - self.algorithm = "HS256" - self.expire_hours = int(os.getenv("TOKEN_EXPIRE_HOURS", 4)) - self.guest_expire_hours = int(os.getenv("GUEST_TOKEN_EXPIRE_HOURS", 2)) - + self.secret = global_args.token_secret + self.algorithm = global_args.jwt_algorithm + self.expire_hours = global_args.token_expire_hours + self.guest_expire_hours = global_args.guest_token_expire_hours self.accounts = {} - auth_accounts = os.getenv("AUTH_ACCOUNTS") + auth_accounts = global_args.auth_accounts if auth_accounts: for account in auth_accounts.split(","): username, password = account.split(":", 1) diff --git a/lightrag/api/config.py b/lightrag/api/config.py new file mode 100644 index 00000000..1bbdb1c9 --- /dev/null +++ b/lightrag/api/config.py @@ -0,0 +1,335 @@ +""" +Configs for the LightRAG API. +""" + +import os +import argparse +import logging +from dotenv import load_dotenv + +# use the .env that is inside the current folder +# allows to use different .env file for each lightrag instance +# the OS environment variables take precedence over the .env file +load_dotenv(dotenv_path=".env", override=False) + + +class OllamaServerInfos: + # Constants for emulated Ollama model information + LIGHTRAG_NAME = "lightrag" + LIGHTRAG_TAG = os.getenv("OLLAMA_EMULATING_MODEL_TAG", "latest") + LIGHTRAG_MODEL = f"{LIGHTRAG_NAME}:{LIGHTRAG_TAG}" + LIGHTRAG_SIZE = 7365960935 # it's a dummy value + LIGHTRAG_CREATED_AT = "2024-01-15T00:00:00Z" + LIGHTRAG_DIGEST = "sha256:lightrag" + + +ollama_server_infos = OllamaServerInfos() + + +class DefaultRAGStorageConfig: + KV_STORAGE = "JsonKVStorage" + VECTOR_STORAGE = "NanoVectorDBStorage" + GRAPH_STORAGE = "NetworkXStorage" + DOC_STATUS_STORAGE = "JsonDocStatusStorage" + + +def get_default_host(binding_type: str) -> str: + default_hosts = { + "ollama": os.getenv("LLM_BINDING_HOST", "http://localhost:11434"), + "lollms": os.getenv("LLM_BINDING_HOST", "http://localhost:9600"), + "azure_openai": os.getenv("AZURE_OPENAI_ENDPOINT", "https://api.openai.com/v1"), + "openai": os.getenv("LLM_BINDING_HOST", "https://api.openai.com/v1"), + } + return default_hosts.get( + binding_type, os.getenv("LLM_BINDING_HOST", "http://localhost:11434") + ) # fallback to ollama if unknown + + +def get_env_value(env_key: str, default: any, value_type: type = str) -> any: + """ + Get value from environment variable with type conversion + + Args: + env_key (str): Environment variable key + default (any): Default value if env variable is not set + value_type (type): Type to convert the value to + + Returns: + any: Converted value from environment or default + """ + value = os.getenv(env_key) + if value is None: + return default + + if value_type is bool: + return value.lower() in ("true", "1", "yes", "t", "on") + try: + return value_type(value) + except ValueError: + return default + + +def parse_args() -> argparse.Namespace: + """ + Parse command line arguments with environment variable fallback + + Args: + is_uvicorn_mode: Whether running under uvicorn mode + + Returns: + argparse.Namespace: Parsed arguments + """ + + parser = argparse.ArgumentParser( + description="LightRAG FastAPI Server with separate working and input directories" + ) + + # Server configuration + parser.add_argument( + "--host", + default=get_env_value("HOST", "0.0.0.0"), + help="Server host (default: from env or 0.0.0.0)", + ) + parser.add_argument( + "--port", + type=int, + default=get_env_value("PORT", 9621, int), + help="Server port (default: from env or 9621)", + ) + + # Directory configuration + parser.add_argument( + "--working-dir", + default=get_env_value("WORKING_DIR", "./rag_storage"), + help="Working directory for RAG storage (default: from env or ./rag_storage)", + ) + parser.add_argument( + "--input-dir", + default=get_env_value("INPUT_DIR", "./inputs"), + help="Directory containing input documents (default: from env or ./inputs)", + ) + + def timeout_type(value): + if value is None: + return 150 + if value is None or value == "None": + return None + return int(value) + + parser.add_argument( + "--timeout", + default=get_env_value("TIMEOUT", None, timeout_type), + type=timeout_type, + help="Timeout in seconds (useful when using slow AI). Use None for infinite timeout", + ) + + # RAG configuration + parser.add_argument( + "--max-async", + type=int, + default=get_env_value("MAX_ASYNC", 4, int), + help="Maximum async operations (default: from env or 4)", + ) + parser.add_argument( + "--max-tokens", + type=int, + default=get_env_value("MAX_TOKENS", 32768, int), + help="Maximum token size (default: from env or 32768)", + ) + + # Logging configuration + parser.add_argument( + "--log-level", + default=get_env_value("LOG_LEVEL", "INFO"), + choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], + help="Logging level (default: from env or INFO)", + ) + parser.add_argument( + "--verbose", + action="store_true", + default=get_env_value("VERBOSE", False, bool), + help="Enable verbose debug output(only valid for DEBUG log-level)", + ) + + parser.add_argument( + "--key", + type=str, + default=get_env_value("LIGHTRAG_API_KEY", None), + help="API key for authentication. This protects lightrag server against unauthorized access", + ) + + # Optional https parameters + parser.add_argument( + "--ssl", + action="store_true", + default=get_env_value("SSL", False, bool), + help="Enable HTTPS (default: from env or False)", + ) + parser.add_argument( + "--ssl-certfile", + default=get_env_value("SSL_CERTFILE", None), + help="Path to SSL certificate file (required if --ssl is enabled)", + ) + parser.add_argument( + "--ssl-keyfile", + default=get_env_value("SSL_KEYFILE", None), + help="Path to SSL private key file (required if --ssl is enabled)", + ) + + parser.add_argument( + "--history-turns", + type=int, + default=get_env_value("HISTORY_TURNS", 3, int), + help="Number of conversation history turns to include (default: from env or 3)", + ) + + # Search parameters + parser.add_argument( + "--top-k", + type=int, + default=get_env_value("TOP_K", 60, int), + help="Number of most similar results to return (default: from env or 60)", + ) + parser.add_argument( + "--cosine-threshold", + type=float, + default=get_env_value("COSINE_THRESHOLD", 0.2, float), + help="Cosine similarity threshold (default: from env or 0.4)", + ) + + # Ollama model name + parser.add_argument( + "--simulated-model-name", + type=str, + default=get_env_value( + "SIMULATED_MODEL_NAME", ollama_server_infos.LIGHTRAG_MODEL + ), + help="Number of conversation history turns to include (default: from env or 3)", + ) + + # Namespace + parser.add_argument( + "--namespace-prefix", + type=str, + default=get_env_value("NAMESPACE_PREFIX", ""), + help="Prefix of the namespace", + ) + + parser.add_argument( + "--auto-scan-at-startup", + action="store_true", + default=False, + help="Enable automatic scanning when the program starts", + ) + + # Server workers configuration + parser.add_argument( + "--workers", + type=int, + default=get_env_value("WORKERS", 1, int), + help="Number of worker processes (default: from env or 1)", + ) + + # LLM and embedding bindings + parser.add_argument( + "--llm-binding", + type=str, + default=get_env_value("LLM_BINDING", "ollama"), + choices=["lollms", "ollama", "openai", "openai-ollama", "azure_openai"], + help="LLM binding type (default: from env or ollama)", + ) + parser.add_argument( + "--embedding-binding", + type=str, + default=get_env_value("EMBEDDING_BINDING", "ollama"), + choices=["lollms", "ollama", "openai", "azure_openai"], + help="Embedding binding type (default: from env or ollama)", + ) + + args = parser.parse_args() + + # convert relative path to absolute path + args.working_dir = os.path.abspath(args.working_dir) + args.input_dir = os.path.abspath(args.input_dir) + + # Inject storage configuration from environment variables + args.kv_storage = get_env_value( + "LIGHTRAG_KV_STORAGE", DefaultRAGStorageConfig.KV_STORAGE + ) + args.doc_status_storage = get_env_value( + "LIGHTRAG_DOC_STATUS_STORAGE", DefaultRAGStorageConfig.DOC_STATUS_STORAGE + ) + args.graph_storage = get_env_value( + "LIGHTRAG_GRAPH_STORAGE", DefaultRAGStorageConfig.GRAPH_STORAGE + ) + args.vector_storage = get_env_value( + "LIGHTRAG_VECTOR_STORAGE", DefaultRAGStorageConfig.VECTOR_STORAGE + ) + + # Get MAX_PARALLEL_INSERT from environment + args.max_parallel_insert = get_env_value("MAX_PARALLEL_INSERT", 2, int) + + # Handle openai-ollama special case + if args.llm_binding == "openai-ollama": + args.llm_binding = "openai" + args.embedding_binding = "ollama" + + args.llm_binding_host = get_env_value( + "LLM_BINDING_HOST", get_default_host(args.llm_binding) + ) + args.embedding_binding_host = get_env_value( + "EMBEDDING_BINDING_HOST", get_default_host(args.embedding_binding) + ) + args.llm_binding_api_key = get_env_value("LLM_BINDING_API_KEY", None) + args.embedding_binding_api_key = get_env_value("EMBEDDING_BINDING_API_KEY", "") + + # Inject model configuration + args.llm_model = get_env_value("LLM_MODEL", "mistral-nemo:latest") + args.embedding_model = get_env_value("EMBEDDING_MODEL", "bge-m3:latest") + args.embedding_dim = get_env_value("EMBEDDING_DIM", 1024, int) + args.max_embed_tokens = get_env_value("MAX_EMBED_TOKENS", 8192, int) + + # Inject chunk configuration + args.chunk_size = get_env_value("CHUNK_SIZE", 1200, int) + args.chunk_overlap_size = get_env_value("CHUNK_OVERLAP_SIZE", 100, int) + + # Inject LLM cache configuration + args.enable_llm_cache_for_extract = get_env_value( + "ENABLE_LLM_CACHE_FOR_EXTRACT", True, bool + ) + + # Inject LLM temperature configuration + args.temperature = get_env_value("TEMPERATURE", 0.5, float) + + # Select Document loading tool (DOCLING, DEFAULT) + args.document_loading_engine = get_env_value("DOCUMENT_LOADING_ENGINE", "DEFAULT") + + # Add environment variables that were previously read directly + args.cors_origins = get_env_value("CORS_ORIGINS", "*") + args.summary_language = get_env_value("SUMMARY_LANGUAGE", "en") + args.whitelist_paths = get_env_value("WHITELIST_PATHS", "/health,/api/*") + + # For JWT Auth + args.auth_accounts = get_env_value("AUTH_ACCOUNTS", "") + args.token_secret = get_env_value("TOKEN_SECRET", "lightrag-jwt-default-secret") + args.token_expire_hours = get_env_value("TOKEN_EXPIRE_HOURS", 48, int) + args.guest_token_expire_hours = get_env_value("GUEST_TOKEN_EXPIRE_HOURS", 24, int) + args.jwt_algorithm = get_env_value("JWT_ALGORITHM", "HS256") + + ollama_server_infos.LIGHTRAG_MODEL = args.simulated_model_name + + return args + + +def update_uvicorn_mode_config(): + # If in uvicorn mode and workers > 1, force it to 1 and log warning + if global_args.workers > 1: + original_workers = global_args.workers + global_args.workers = 1 + # Log warning directly here + logging.warning( + f"In uvicorn mode, workers parameter was set to {original_workers}. Forcing workers=1" + ) + + +global_args = parse_args() diff --git a/lightrag/api/lightrag_server.py b/lightrag/api/lightrag_server.py index 8110d6d4..9f1e6e8a 100644 --- a/lightrag/api/lightrag_server.py +++ b/lightrag/api/lightrag_server.py @@ -19,11 +19,14 @@ from contextlib import asynccontextmanager from dotenv import load_dotenv from lightrag.api.utils_api import ( get_combined_auth_dependency, - parse_args, - get_default_host, display_splash_screen, check_env_file, ) +from .config import ( + global_args, + update_uvicorn_mode_config, + get_default_host, +) import sys from lightrag import LightRAG, __version__ as core_version from lightrag.api import __api_version__ @@ -52,6 +55,10 @@ from lightrag.api.auth import auth_handler # the OS environment variables take precedence over the .env file load_dotenv(dotenv_path=".env", override=False) + +webui_title = os.getenv("WEBUI_TITLE") +webui_description = os.getenv("WEBUI_DESCRIPTION") + # Initialize config parser config = configparser.ConfigParser() config.read("config.ini") @@ -164,10 +171,10 @@ def create_app(args): app = FastAPI(**app_kwargs) def get_cors_origins(): - """Get allowed origins from environment variable + """Get allowed origins from global_args Returns a list of allowed origins, defaults to ["*"] if not set """ - origins_str = os.getenv("CORS_ORIGINS", "*") + origins_str = global_args.cors_origins if origins_str == "*": return ["*"] return [origin.strip() for origin in origins_str.split(",")] @@ -315,9 +322,10 @@ def create_app(args): "similarity_threshold": 0.95, "use_llm_check": False, }, - namespace_prefix=args.namespace_prefix, + # namespace_prefix=args.namespace_prefix, auto_manage_storages_states=False, max_parallel_insert=args.max_parallel_insert, + addon_params={"language": args.summary_language}, ) else: # azure_openai rag = LightRAG( @@ -345,9 +353,10 @@ def create_app(args): "similarity_threshold": 0.95, "use_llm_check": False, }, - namespace_prefix=args.namespace_prefix, + # namespace_prefix=args.namespace_prefix, auto_manage_storages_states=False, max_parallel_insert=args.max_parallel_insert, + addon_params={"language": args.summary_language}, ) # Add routes @@ -381,6 +390,8 @@ def create_app(args): "message": "Authentication is disabled. Using guest access.", "core_version": core_version, "api_version": __api_version__, + "webui_title": webui_title, + "webui_description": webui_description, } return { @@ -388,6 +399,8 @@ def create_app(args): "auth_mode": "enabled", "core_version": core_version, "api_version": __api_version__, + "webui_title": webui_title, + "webui_description": webui_description, } @app.post("/login") @@ -404,6 +417,8 @@ def create_app(args): "message": "Authentication is disabled. Using guest access.", "core_version": core_version, "api_version": __api_version__, + "webui_title": webui_title, + "webui_description": webui_description, } username = form_data.username if auth_handler.accounts.get(username) != form_data.password: @@ -454,10 +469,12 @@ def create_app(args): "vector_storage": args.vector_storage, "enable_llm_cache_for_extract": args.enable_llm_cache_for_extract, }, - "core_version": core_version, - "api_version": __api_version__, "auth_mode": auth_mode, "pipeline_busy": pipeline_status.get("busy", False), + "core_version": core_version, + "api_version": __api_version__, + "webui_title": webui_title, + "webui_description": webui_description, } except Exception as e: logger.error(f"Error getting health status: {str(e)}") @@ -490,7 +507,7 @@ def create_app(args): def get_application(args=None): """Factory function for creating the FastAPI application""" if args is None: - args = parse_args() + args = global_args return create_app(args) @@ -611,30 +628,31 @@ def main(): # Configure logging before parsing args configure_logging() - - args = parse_args(is_uvicorn_mode=True) - display_splash_screen(args) + update_uvicorn_mode_config() + display_splash_screen(global_args) # Create application instance directly instead of using factory function - app = create_app(args) + app = create_app(global_args) # Start Uvicorn in single process mode uvicorn_config = { "app": app, # Pass application instance directly instead of string path - "host": args.host, - "port": args.port, + "host": global_args.host, + "port": global_args.port, "log_config": None, # Disable default config } - if args.ssl: + if global_args.ssl: uvicorn_config.update( { - "ssl_certfile": args.ssl_certfile, - "ssl_keyfile": args.ssl_keyfile, + "ssl_certfile": global_args.ssl_certfile, + "ssl_keyfile": global_args.ssl_keyfile, } ) - print(f"Starting Uvicorn server in single-process mode on {args.host}:{args.port}") + print( + f"Starting Uvicorn server in single-process mode on {global_args.host}:{global_args.port}" + ) uvicorn.run(**uvicorn_config) diff --git a/lightrag/api/routers/document_routes.py b/lightrag/api/routers/document_routes.py index 445008ec..8e664006 100644 --- a/lightrag/api/routers/document_routes.py +++ b/lightrag/api/routers/document_routes.py @@ -10,16 +10,14 @@ import traceback import pipmaster as pm from datetime import datetime from pathlib import Path -from typing import Dict, List, Optional, Any +from typing import Dict, List, Optional, Any, Literal from fastapi import APIRouter, BackgroundTasks, Depends, File, HTTPException, UploadFile from pydantic import BaseModel, Field, field_validator from lightrag import LightRAG from lightrag.base import DocProcessingStatus, DocStatus -from lightrag.api.utils_api import ( - get_combined_auth_dependency, - global_args, -) +from lightrag.api.utils_api import get_combined_auth_dependency +from ..config import global_args router = APIRouter( prefix="/documents", @@ -30,7 +28,37 @@ router = APIRouter( temp_prefix = "__tmp__" +class ScanResponse(BaseModel): + """Response model for document scanning operation + + Attributes: + status: Status of the scanning operation + message: Optional message with additional details + """ + + status: Literal["scanning_started"] = Field( + description="Status of the scanning operation" + ) + message: Optional[str] = Field( + default=None, description="Additional details about the scanning operation" + ) + + class Config: + json_schema_extra = { + "example": { + "status": "scanning_started", + "message": "Scanning process has been initiated in the background", + } + } + + class InsertTextRequest(BaseModel): + """Request model for inserting a single text document + + Attributes: + text: The text content to be inserted into the RAG system + """ + text: str = Field( min_length=1, description="The text to insert", @@ -41,8 +69,21 @@ class InsertTextRequest(BaseModel): def strip_after(cls, text: str) -> str: return text.strip() + class Config: + json_schema_extra = { + "example": { + "text": "This is a sample text to be inserted into the RAG system." + } + } + class InsertTextsRequest(BaseModel): + """Request model for inserting multiple text documents + + Attributes: + texts: List of text contents to be inserted into the RAG system + """ + texts: list[str] = Field( min_length=1, description="The texts to insert", @@ -53,11 +94,116 @@ class InsertTextsRequest(BaseModel): def strip_after(cls, texts: list[str]) -> list[str]: return [text.strip() for text in texts] + class Config: + json_schema_extra = { + "example": { + "texts": [ + "This is the first text to be inserted.", + "This is the second text to be inserted.", + ] + } + } + class InsertResponse(BaseModel): - status: str = Field(description="Status of the operation") + """Response model for document insertion operations + + Attributes: + status: Status of the operation (success, duplicated, partial_success, failure) + message: Detailed message describing the operation result + """ + + status: Literal["success", "duplicated", "partial_success", "failure"] = Field( + description="Status of the operation" + ) message: str = Field(description="Message describing the operation result") + class Config: + json_schema_extra = { + "example": { + "status": "success", + "message": "File 'document.pdf' uploaded successfully. Processing will continue in background.", + } + } + + +class ClearDocumentsResponse(BaseModel): + """Response model for document clearing operation + + Attributes: + status: Status of the clear operation + message: Detailed message describing the operation result + """ + + status: Literal["success", "partial_success", "busy", "fail"] = Field( + description="Status of the clear operation" + ) + message: str = Field(description="Message describing the operation result") + + class Config: + json_schema_extra = { + "example": { + "status": "success", + "message": "All documents cleared successfully. Deleted 15 files.", + } + } + + +class ClearCacheRequest(BaseModel): + """Request model for clearing cache + + Attributes: + modes: Optional list of cache modes to clear + """ + + modes: Optional[ + List[Literal["default", "naive", "local", "global", "hybrid", "mix"]] + ] = Field( + default=None, + description="Modes of cache to clear. If None, clears all cache.", + ) + + class Config: + json_schema_extra = {"example": {"modes": ["default", "naive"]}} + + +class ClearCacheResponse(BaseModel): + """Response model for cache clearing operation + + Attributes: + status: Status of the clear operation + message: Detailed message describing the operation result + """ + + status: Literal["success", "fail"] = Field( + description="Status of the clear operation" + ) + message: str = Field(description="Message describing the operation result") + + class Config: + json_schema_extra = { + "example": { + "status": "success", + "message": "Successfully cleared cache for modes: ['default', 'naive']", + } + } + + +"""Response model for document status + +Attributes: + id: Document identifier + content_summary: Summary of document content + content_length: Length of document content + status: Current processing status + created_at: Creation timestamp (ISO format string) + updated_at: Last update timestamp (ISO format string) + chunks_count: Number of chunks (optional) + error: Error message if any (optional) + metadata: Additional metadata (optional) + file_path: Path to the document file +""" + class DocStatusResponse(BaseModel): @staticmethod @@ -68,34 +214,82 @@ class DocStatusResponse(BaseModel): return dt return dt.isoformat() - """Response model for document status + id: str = Field(description="Document identifier") + content_summary: str = Field(description="Summary of document content") + content_length: int = Field(description="Length of document content in characters") + status: DocStatus = Field(description="Current processing status") + created_at: str = Field(description="Creation timestamp (ISO format string)") + updated_at: str = Field(description="Last update timestamp (ISO format string)") + chunks_count: Optional[int] = Field( + default=None, description="Number of chunks the document was split into" + ) + error: Optional[str] = Field( + default=None, description="Error message if processing failed" + ) + metadata: Optional[dict[str, Any]] = Field( + default=None, description="Additional metadata about the document" + ) + file_path: str = Field(description="Path to the document file") - Attributes: - id: Document identifier - content_summary: Summary of document content - content_length: Length of document content - status: Current processing status - created_at: Creation timestamp (ISO format string) - updated_at: Last update timestamp (ISO format string) - chunks_count: Number of chunks (optional) - error: Error message if any (optional) - metadata: Additional metadata (optional) - """ - - id: str - content_summary: str - content_length: int - status: DocStatus - created_at: str - updated_at: str - chunks_count: Optional[int] = None - error: Optional[str] = None - metadata: Optional[dict[str, Any]] = None - file_path: str + class Config: + json_schema_extra = { + "example": { + "id": "doc_123456", + "content_summary": "Research paper on machine learning", + "content_length": 15240, + "status": "PROCESSED", + "created_at": "2025-03-31T12:34:56", + "updated_at": "2025-03-31T12:35:30", + "chunks_count": 12, + "error": None, + "metadata": {"author": "John Doe", "year": 2025}, + "file_path": "research_paper.pdf", + } + } class DocsStatusesResponse(BaseModel): - statuses: Dict[DocStatus, List[DocStatusResponse]] = {} + """Response model for document statuses + + Attributes: + statuses: Dictionary mapping document status to lists of document status responses + """ + + statuses: Dict[DocStatus, List[DocStatusResponse]] = Field( + default_factory=dict, + description="Dictionary mapping document status to lists of document status responses", + ) + + class Config: + json_schema_extra = { + "example": { + "statuses": { + "PENDING": [ + { + "id": "doc_123", + "content_summary": "Pending document", + "content_length": 5000, + "status": "PENDING", + "created_at": "2025-03-31T10:00:00", + "updated_at": "2025-03-31T10:00:00", + "file_path": "pending_doc.pdf", + } + ], + "PROCESSED": [ + { + "id": "doc_456", + "content_summary": "Processed document", + "content_length": 8000, + "status": "PROCESSED", + "created_at": "2025-03-31T09:00:00", + "updated_at": "2025-03-31T09:05:00", + "chunks_count": 8, + "file_path": "processed_doc.pdf", + } + ], + } + } + } class PipelineStatusResponse(BaseModel): @@ -276,7 +470,7 @@ async def pipeline_enqueue_file(rag: LightRAG, file_path: Path) -> bool: ) return False case ".pdf": - if global_args["main_args"].document_loading_engine == "DOCLING": + if global_args.document_loading_engine == "DOCLING": if not pm.is_installed("docling"): # type: ignore pm.install("docling") from docling.document_converter import DocumentConverter # type: ignore @@ -295,7 +489,7 @@ async def pipeline_enqueue_file(rag: LightRAG, file_path: Path) -> bool: for page in reader.pages: content += page.extract_text() + "\n" case ".docx": - if global_args["main_args"].document_loading_engine == "DOCLING": + if global_args.document_loading_engine == "DOCLING": if not pm.is_installed("docling"): # type: ignore pm.install("docling") from docling.document_converter import DocumentConverter # type: ignore @@ -315,7 +509,7 @@ async def pipeline_enqueue_file(rag: LightRAG, file_path: Path) -> bool: [paragraph.text for paragraph in doc.paragraphs] ) case ".pptx": - if global_args["main_args"].document_loading_engine == "DOCLING": + if global_args.document_loading_engine == "DOCLING": if not pm.is_installed("docling"): # type: ignore pm.install("docling") from docling.document_converter import DocumentConverter # type: ignore @@ -336,7 +530,7 @@ async def pipeline_enqueue_file(rag: LightRAG, file_path: Path) -> bool: if hasattr(shape, "text"): content += shape.text + "\n" case ".xlsx": - if global_args["main_args"].document_loading_engine == "DOCLING": + if global_args.document_loading_engine == "DOCLING": if not pm.is_installed("docling"): # type: ignore pm.install("docling") from docling.document_converter import DocumentConverter # type: ignore @@ -443,6 +637,7 @@ async def pipeline_index_texts(rag: LightRAG, texts: List[str]): await rag.apipeline_process_enqueue_documents() +# TODO: deprecate after /insert_file is removed async def save_temp_file(input_dir: Path, file: UploadFile = File(...)) -> Path: """Save the uploaded file to a temporary location @@ -476,8 +671,8 @@ async def run_scanning_process(rag: LightRAG, doc_manager: DocumentManager): if not new_files: return - # Get MAX_PARALLEL_INSERT from global_args["main_args"] - max_parallel = global_args["main_args"].max_parallel_insert + # Get MAX_PARALLEL_INSERT from global_args + max_parallel = global_args.max_parallel_insert # Calculate batch size as 2 * MAX_PARALLEL_INSERT batch_size = 2 * max_parallel @@ -509,7 +704,9 @@ def create_document_routes( # Create combined auth dependency for document routes combined_auth = get_combined_auth_dependency(api_key) - @router.post("/scan", dependencies=[Depends(combined_auth)]) + @router.post( + "/scan", response_model=ScanResponse, dependencies=[Depends(combined_auth)] + ) async def scan_for_new_documents(background_tasks: BackgroundTasks): """ Trigger the scanning process for new documents. @@ -519,13 +716,18 @@ def create_document_routes( that fact. Returns: - dict: A dictionary containing the scanning status + ScanResponse: A response object containing the scanning status """ # Start the scanning process in the background background_tasks.add_task(run_scanning_process, rag, doc_manager) - return {"status": "scanning_started"} + return ScanResponse( + status="scanning_started", + message="Scanning process has been initiated in the background", + ) - @router.post("/upload", dependencies=[Depends(combined_auth)]) + @router.post( + "/upload", response_model=InsertResponse, dependencies=[Depends(combined_auth)] + ) async def upload_to_input_dir( background_tasks: BackgroundTasks, file: UploadFile = File(...) ): @@ -645,6 +847,7 @@ def create_document_routes( logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=str(e)) + # TODO: deprecated, use /upload instead @router.post( "/file", response_model=InsertResponse, dependencies=[Depends(combined_auth)] ) @@ -688,6 +891,7 @@ def create_document_routes( logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=str(e)) + # TODO: deprecated, use /upload instead @router.post( "/file_batch", response_model=InsertResponse, @@ -752,32 +956,186 @@ def create_document_routes( raise HTTPException(status_code=500, detail=str(e)) @router.delete( - "", response_model=InsertResponse, dependencies=[Depends(combined_auth)] + "", response_model=ClearDocumentsResponse, dependencies=[Depends(combined_auth)] ) async def clear_documents(): """ Clear all documents from the RAG system. - This endpoint deletes all text chunks, entities vector database, and relationships - vector database, effectively clearing all documents from the RAG system. + This endpoint deletes all documents, entities, relationships, and files from the system. + It uses the storage drop methods to properly clean up all data and removes all files + from the input directory. Returns: - InsertResponse: A response object containing the status and message. + ClearDocumentsResponse: A response object containing the status and message. + - status="success": All documents and files were successfully cleared. + - status="partial_success": Document clear job exit with some errors. + - status="busy": Operation could not be completed because the pipeline is busy. + - status="fail": All storage drop operations failed, with message + - message: Detailed information about the operation results, including counts + of deleted files and any errors encountered. Raises: - HTTPException: If an error occurs during the clearing process (500). + HTTPException: Raised when a serious error occurs during the clearing process, + with status code 500 and error details in the detail field. """ - try: - rag.text_chunks = [] - rag.entities_vdb = None - rag.relationships_vdb = None - return InsertResponse( - status="success", message="All documents cleared successfully" + from lightrag.kg.shared_storage import ( + get_namespace_data, + get_pipeline_status_lock, + ) + + # Get pipeline status and lock + pipeline_status = await get_namespace_data("pipeline_status") + pipeline_status_lock = get_pipeline_status_lock() + + # Check and set status with lock + async with pipeline_status_lock: + if pipeline_status.get("busy", False): + return ClearDocumentsResponse( + status="busy", + message="Cannot clear documents while pipeline is busy", + ) + # Set busy to true + pipeline_status.update( + { + "busy": True, + "job_name": "Clearing Documents", + "job_start": datetime.now().isoformat(), + "docs": 0, + "batchs": 0, + "cur_batch": 0, + "request_pending": False, # Clear any previous request + "latest_message": "Starting document clearing process", + } ) + # Cleaning history_messages without breaking it as a shared list object + del pipeline_status["history_messages"][:] + pipeline_status["history_messages"].append( + "Starting document clearing process" + ) + + try: + # Use drop method to clear all data + drop_tasks = [] + storages = [ + rag.text_chunks, + rag.full_docs, + rag.entities_vdb, + rag.relationships_vdb, + rag.chunks_vdb, + rag.chunk_entity_relation_graph, + rag.doc_status, + ] + + # Log storage drop start + if "history_messages" in pipeline_status: + pipeline_status["history_messages"].append( + "Starting to drop storage components" + ) + + for storage in storages: + if storage is not None: + drop_tasks.append(storage.drop()) + + # Wait for all drop tasks to complete + drop_results = await asyncio.gather(*drop_tasks, return_exceptions=True) + + # Check for errors and log results + errors = [] + storage_success_count = 0 + storage_error_count = 0 + + for i, result in enumerate(drop_results): + storage_name = storages[i].__class__.__name__ + if isinstance(result, Exception): + error_msg = f"Error dropping {storage_name}: {str(result)}" + errors.append(error_msg) + logger.error(error_msg) + storage_error_count += 1 + else: + logger.info(f"Successfully dropped {storage_name}") + storage_success_count += 1 + + # Log storage drop results + if "history_messages" in pipeline_status: + if storage_error_count > 0: + pipeline_status["history_messages"].append( + f"Dropped {storage_success_count} storage components with {storage_error_count} errors" + ) + else: + pipeline_status["history_messages"].append( + f"Successfully dropped all {storage_success_count} storage components" + ) + + # If all storage operations failed, return error status and don't proceed with file deletion + if storage_success_count == 0 and storage_error_count > 0: + error_message = "All storage drop operations failed. Aborting document clearing process." + logger.error(error_message) + if "history_messages" in pipeline_status: + pipeline_status["history_messages"].append(error_message) + return ClearDocumentsResponse(status="fail", message=error_message) + + # Log file deletion start + if "history_messages" in pipeline_status: + pipeline_status["history_messages"].append( + "Starting to delete files in input directory" + ) + + # Delete all files in input_dir + deleted_files_count = 0 + file_errors_count = 0 + + for file_path in doc_manager.input_dir.glob("**/*"): + if file_path.is_file(): + try: + file_path.unlink() + deleted_files_count += 1 + except Exception as e: + logger.error(f"Error deleting file {file_path}: {str(e)}") + file_errors_count += 1 + + # Log file deletion results + if "history_messages" in pipeline_status: + if file_errors_count > 0: + pipeline_status["history_messages"].append( + f"Deleted {deleted_files_count} files with {file_errors_count} errors" + ) + errors.append(f"Failed to delete {file_errors_count} files") + else: + pipeline_status["history_messages"].append( + f"Successfully deleted {deleted_files_count} files" + ) + + # Prepare final result message + final_message = "" + if errors: + final_message = f"Cleared documents with some errors. Deleted {deleted_files_count} files." + status = "partial_success" + else: + final_message = f"All documents cleared successfully. Deleted {deleted_files_count} files." + status = "success" + + # Log final result + if "history_messages" in pipeline_status: + pipeline_status["history_messages"].append(final_message) + + # Return response based on results + return ClearDocumentsResponse(status=status, message=final_message) except Exception as e: - logger.error(f"Error DELETE /documents: {str(e)}") + error_msg = f"Error clearing documents: {str(e)}" + logger.error(error_msg) logger.error(traceback.format_exc()) + if "history_messages" in pipeline_status: + pipeline_status["history_messages"].append(error_msg) raise HTTPException(status_code=500, detail=str(e)) + finally: + # Reset busy status after completion + async with pipeline_status_lock: + pipeline_status["busy"] = False + completion_msg = "Document clearing process completed" + pipeline_status["latest_message"] = completion_msg + if "history_messages" in pipeline_status: + pipeline_status["history_messages"].append(completion_msg) @router.get( "/pipeline_status", @@ -850,7 +1208,9 @@ def create_document_routes( logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=str(e)) - @router.get("", dependencies=[Depends(combined_auth)]) + @router.get( + "", response_model=DocsStatusesResponse, dependencies=[Depends(combined_auth)] + ) async def documents() -> DocsStatusesResponse: """ Get the status of all documents in the system. @@ -908,4 +1268,57 @@ def create_document_routes( logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=str(e)) + @router.post( + "/clear_cache", + response_model=ClearCacheResponse, + dependencies=[Depends(combined_auth)], + ) + async def clear_cache(request: ClearCacheRequest): + """ + Clear cache data from the LLM response cache storage. + + This endpoint allows clearing specific modes of cache or all cache if no modes are specified. + Valid modes include: "default", "naive", "local", "global", "hybrid", "mix". + - "default" represents extraction cache. + - Other modes correspond to different query modes. + + Args: + request (ClearCacheRequest): The request body containing optional modes to clear. + + Returns: + ClearCacheResponse: A response object containing the status and message. + + Raises: + HTTPException: If an error occurs during cache clearing (400 for invalid modes, 500 for other errors). + """ + try: + # Validate modes if provided + valid_modes = ["default", "naive", "local", "global", "hybrid", "mix"] + if request.modes and not all(mode in valid_modes for mode in request.modes): + invalid_modes = [ + mode for mode in request.modes if mode not in valid_modes + ] + raise HTTPException( + status_code=400, + detail=f"Invalid mode(s): {invalid_modes}. Valid modes are: {valid_modes}", + ) + + # Call the aclear_cache method + await rag.aclear_cache(request.modes) + + # Prepare success message + if request.modes: + message = f"Successfully cleared cache for modes: {request.modes}" + else: + message = "Successfully cleared all cache" + + return ClearCacheResponse(status="success", message=message) + except HTTPException: + # Re-raise HTTP exceptions + raise + except Exception as e: + logger.error(f"Error clearing cache: {str(e)}") + logger.error(traceback.format_exc()) + raise HTTPException(status_code=500, detail=str(e)) + return router diff --git a/lightrag/api/routers/graph_routes.py b/lightrag/api/routers/graph_routes.py index f9d77ff6..381df90b 100644 --- a/lightrag/api/routers/graph_routes.py +++ b/lightrag/api/routers/graph_routes.py @@ -3,7 +3,7 @@ This module contains all graph-related routes for the LightRAG API. """ from typing import Optional -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Query from ..utils_api import get_combined_auth_dependency @@ -25,23 +25,20 @@ def create_graph_routes(rag, api_key: Optional[str] = None): @router.get("/graphs", dependencies=[Depends(combined_auth)]) async def get_knowledge_graph( - label: str, max_depth: int = 3, min_degree: int = 0, inclusive: bool = False + label: str = Query(..., description="Label to get knowledge graph for"), + max_depth: int = Query(3, description="Maximum depth of graph", ge=1), + max_nodes: int = Query(1000, description="Maximum nodes to return", ge=1), ): """ Retrieve a connected subgraph of nodes where the label includes the specified label. - Maximum number of nodes is constrained by the environment variable `MAX_GRAPH_NODES` (default: 1000). When reducing the number of nodes, the prioritization criteria are as follows: - 1. min_degree does not affect nodes directly connected to the matching nodes - 2. Label matching nodes take precedence - 3. Followed by nodes directly connected to the matching nodes - 4. Finally, the degree of the nodes - Maximum number of nodes is limited to env MAX_GRAPH_NODES(default: 1000) + 1. Hops(path) to the staring node take precedence + 2. Followed by the degree of the nodes Args: - label (str): Label to get knowledge graph for - max_depth (int, optional): Maximum depth of graph. Defaults to 3. - inclusive_search (bool, optional): If True, search for nodes that include the label. Defaults to False. - min_degree (int, optional): Minimum degree of nodes. Defaults to 0. + label (str): Label of the starting node + max_depth (int, optional): Maximum depth of the subgraph,Defaults to 3 + max_nodes: Maxiumu nodes to return Returns: Dict[str, List[str]]: Knowledge graph for label @@ -49,8 +46,7 @@ def create_graph_routes(rag, api_key: Optional[str] = None): return await rag.get_knowledge_graph( node_label=label, max_depth=max_depth, - inclusive=inclusive, - min_degree=min_degree, + max_nodes=max_nodes, ) return router diff --git a/lightrag/api/run_with_gunicorn.py b/lightrag/api/run_with_gunicorn.py index 065a12a1..cf902a8a 100644 --- a/lightrag/api/run_with_gunicorn.py +++ b/lightrag/api/run_with_gunicorn.py @@ -7,14 +7,9 @@ import os import sys import signal import pipmaster as pm -from lightrag.api.utils_api import parse_args, display_splash_screen, check_env_file +from lightrag.api.utils_api import display_splash_screen, check_env_file from lightrag.kg.shared_storage import initialize_share_data, finalize_share_data -from dotenv import load_dotenv - -# use the .env that is inside the current folder -# allows to use different .env file for each lightrag instance -# the OS environment variables take precedence over the .env file -load_dotenv(dotenv_path=".env", override=False) +from .config import global_args def check_and_install_dependencies(): @@ -59,20 +54,17 @@ def main(): signal.signal(signal.SIGINT, signal_handler) # Ctrl+C signal.signal(signal.SIGTERM, signal_handler) # kill command - # Parse all arguments using parse_args - args = parse_args(is_uvicorn_mode=False) - # Display startup information - display_splash_screen(args) + display_splash_screen(global_args) print("🚀 Starting LightRAG with Gunicorn") - print(f"🔄 Worker management: Gunicorn (workers={args.workers})") + print(f"🔄 Worker management: Gunicorn (workers={global_args.workers})") print("🔍 Preloading app: Enabled") print("📝 Note: Using Gunicorn's preload feature for shared data initialization") print("\n\n" + "=" * 80) print("MAIN PROCESS INITIALIZATION") print(f"Process ID: {os.getpid()}") - print(f"Workers setting: {args.workers}") + print(f"Workers setting: {global_args.workers}") print("=" * 80 + "\n") # Import Gunicorn's StandaloneApplication @@ -128,31 +120,43 @@ def main(): # Set configuration variables in gunicorn_config, prioritizing command line arguments gunicorn_config.workers = ( - args.workers if args.workers else int(os.getenv("WORKERS", 1)) + global_args.workers + if global_args.workers + else int(os.getenv("WORKERS", 1)) ) # Bind configuration prioritizes command line arguments - host = args.host if args.host != "0.0.0.0" else os.getenv("HOST", "0.0.0.0") - port = args.port if args.port != 9621 else int(os.getenv("PORT", 9621)) + host = ( + global_args.host + if global_args.host != "0.0.0.0" + else os.getenv("HOST", "0.0.0.0") + ) + port = ( + global_args.port + if global_args.port != 9621 + else int(os.getenv("PORT", 9621)) + ) gunicorn_config.bind = f"{host}:{port}" # Log level configuration prioritizes command line arguments gunicorn_config.loglevel = ( - args.log_level.lower() - if args.log_level + global_args.log_level.lower() + if global_args.log_level else os.getenv("LOG_LEVEL", "info") ) # Timeout configuration prioritizes command line arguments gunicorn_config.timeout = ( - args.timeout if args.timeout * 2 else int(os.getenv("TIMEOUT", 150 * 2)) + global_args.timeout + if global_args.timeout * 2 + else int(os.getenv("TIMEOUT", 150 * 2)) ) # Keepalive configuration gunicorn_config.keepalive = int(os.getenv("KEEPALIVE", 5)) # SSL configuration prioritizes command line arguments - if args.ssl or os.getenv("SSL", "").lower() in ( + if global_args.ssl or os.getenv("SSL", "").lower() in ( "true", "1", "yes", @@ -160,12 +164,14 @@ def main(): "on", ): gunicorn_config.certfile = ( - args.ssl_certfile - if args.ssl_certfile + global_args.ssl_certfile + if global_args.ssl_certfile else os.getenv("SSL_CERTFILE") ) gunicorn_config.keyfile = ( - args.ssl_keyfile if args.ssl_keyfile else os.getenv("SSL_KEYFILE") + global_args.ssl_keyfile + if global_args.ssl_keyfile + else os.getenv("SSL_KEYFILE") ) # Set configuration options from the module @@ -190,13 +196,13 @@ def main(): # Import the application from lightrag.api.lightrag_server import get_application - return get_application(args) + return get_application(global_args) # Create the application app = GunicornApp("") # Force workers to be an integer and greater than 1 for multi-process mode - workers_count = int(args.workers) + workers_count = int(global_args.workers) if workers_count > 1: # Set a flag to indicate we're in the main process os.environ["LIGHTRAG_MAIN_PROCESS"] = "1" diff --git a/lightrag/api/utils_api.py b/lightrag/api/utils_api.py index c01b7a37..2bdc8d07 100644 --- a/lightrag/api/utils_api.py +++ b/lightrag/api/utils_api.py @@ -7,15 +7,13 @@ import argparse from typing import Optional, List, Tuple import sys from ascii_colors import ASCIIColors -import logging from lightrag.api import __api_version__ as api_version from lightrag import __version__ as core_version from fastapi import HTTPException, Security, Request, status -from dotenv import load_dotenv from fastapi.security import APIKeyHeader, OAuth2PasswordBearer from starlette.status import HTTP_403_FORBIDDEN from .auth import auth_handler -from ..prompt import PROMPTS +from .config import ollama_server_infos, global_args def check_env_file(): @@ -36,16 +34,8 @@ def check_env_file(): return True -# use the .env that is inside the current folder -# allows to use different .env file for each lightrag instance -# the OS environment variables take precedence over the .env file -load_dotenv(dotenv_path=".env", override=False) - -global_args = {"main_args": None} - -# Get whitelist paths from environment variable, only once during initialization -default_whitelist = "/health,/api/*" -whitelist_paths = os.getenv("WHITELIST_PATHS", default_whitelist).split(",") +# Get whitelist paths from global_args, only once during initialization +whitelist_paths = global_args.whitelist_paths.split(",") # Pre-compile path matching patterns whitelist_patterns: List[Tuple[str, bool]] = [] @@ -63,19 +53,6 @@ for path in whitelist_paths: auth_configured = bool(auth_handler.accounts) -class OllamaServerInfos: - # Constants for emulated Ollama model information - LIGHTRAG_NAME = "lightrag" - LIGHTRAG_TAG = os.getenv("OLLAMA_EMULATING_MODEL_TAG", "latest") - LIGHTRAG_MODEL = f"{LIGHTRAG_NAME}:{LIGHTRAG_TAG}" - LIGHTRAG_SIZE = 7365960935 # it's a dummy value - LIGHTRAG_CREATED_AT = "2024-01-15T00:00:00Z" - LIGHTRAG_DIGEST = "sha256:lightrag" - - -ollama_server_infos = OllamaServerInfos() - - def get_combined_auth_dependency(api_key: Optional[str] = None): """ Create a combined authentication dependency that implements authentication logic @@ -186,299 +163,6 @@ def get_combined_auth_dependency(api_key: Optional[str] = None): return combined_dependency -class DefaultRAGStorageConfig: - KV_STORAGE = "JsonKVStorage" - VECTOR_STORAGE = "NanoVectorDBStorage" - GRAPH_STORAGE = "NetworkXStorage" - DOC_STATUS_STORAGE = "JsonDocStatusStorage" - - -def get_default_host(binding_type: str) -> str: - default_hosts = { - "ollama": os.getenv("LLM_BINDING_HOST", "http://localhost:11434"), - "lollms": os.getenv("LLM_BINDING_HOST", "http://localhost:9600"), - "azure_openai": os.getenv("AZURE_OPENAI_ENDPOINT", "https://api.openai.com/v1"), - "openai": os.getenv("LLM_BINDING_HOST", "https://api.openai.com/v1"), - } - return default_hosts.get( - binding_type, os.getenv("LLM_BINDING_HOST", "http://localhost:11434") - ) # fallback to ollama if unknown - - -def get_env_value(env_key: str, default: any, value_type: type = str) -> any: - """ - Get value from environment variable with type conversion - - Args: - env_key (str): Environment variable key - default (any): Default value if env variable is not set - value_type (type): Type to convert the value to - - Returns: - any: Converted value from environment or default - """ - value = os.getenv(env_key) - if value is None: - return default - - if value_type is bool: - return value.lower() in ("true", "1", "yes", "t", "on") - try: - return value_type(value) - except ValueError: - return default - - -def parse_args(is_uvicorn_mode: bool = False) -> argparse.Namespace: - """ - Parse command line arguments with environment variable fallback - - Args: - is_uvicorn_mode: Whether running under uvicorn mode - - Returns: - argparse.Namespace: Parsed arguments - """ - - parser = argparse.ArgumentParser( - description="LightRAG FastAPI Server with separate working and input directories" - ) - - # Server configuration - parser.add_argument( - "--host", - default=get_env_value("HOST", "0.0.0.0"), - help="Server host (default: from env or 0.0.0.0)", - ) - parser.add_argument( - "--port", - type=int, - default=get_env_value("PORT", 9621, int), - help="Server port (default: from env or 9621)", - ) - - # Directory configuration - parser.add_argument( - "--working-dir", - default=get_env_value("WORKING_DIR", "./rag_storage"), - help="Working directory for RAG storage (default: from env or ./rag_storage)", - ) - parser.add_argument( - "--input-dir", - default=get_env_value("INPUT_DIR", "./inputs"), - help="Directory containing input documents (default: from env or ./inputs)", - ) - - def timeout_type(value): - if value is None: - return 150 - if value is None or value == "None": - return None - return int(value) - - parser.add_argument( - "--timeout", - default=get_env_value("TIMEOUT", None, timeout_type), - type=timeout_type, - help="Timeout in seconds (useful when using slow AI). Use None for infinite timeout", - ) - - # RAG configuration - parser.add_argument( - "--max-async", - type=int, - default=get_env_value("MAX_ASYNC", 4, int), - help="Maximum async operations (default: from env or 4)", - ) - parser.add_argument( - "--max-tokens", - type=int, - default=get_env_value("MAX_TOKENS", 32768, int), - help="Maximum token size (default: from env or 32768)", - ) - - # Logging configuration - parser.add_argument( - "--log-level", - default=get_env_value("LOG_LEVEL", "INFO"), - choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], - help="Logging level (default: from env or INFO)", - ) - parser.add_argument( - "--verbose", - action="store_true", - default=get_env_value("VERBOSE", False, bool), - help="Enable verbose debug output(only valid for DEBUG log-level)", - ) - - parser.add_argument( - "--key", - type=str, - default=get_env_value("LIGHTRAG_API_KEY", None), - help="API key for authentication. This protects lightrag server against unauthorized access", - ) - - # Optional https parameters - parser.add_argument( - "--ssl", - action="store_true", - default=get_env_value("SSL", False, bool), - help="Enable HTTPS (default: from env or False)", - ) - parser.add_argument( - "--ssl-certfile", - default=get_env_value("SSL_CERTFILE", None), - help="Path to SSL certificate file (required if --ssl is enabled)", - ) - parser.add_argument( - "--ssl-keyfile", - default=get_env_value("SSL_KEYFILE", None), - help="Path to SSL private key file (required if --ssl is enabled)", - ) - - parser.add_argument( - "--history-turns", - type=int, - default=get_env_value("HISTORY_TURNS", 3, int), - help="Number of conversation history turns to include (default: from env or 3)", - ) - - # Search parameters - parser.add_argument( - "--top-k", - type=int, - default=get_env_value("TOP_K", 60, int), - help="Number of most similar results to return (default: from env or 60)", - ) - parser.add_argument( - "--cosine-threshold", - type=float, - default=get_env_value("COSINE_THRESHOLD", 0.2, float), - help="Cosine similarity threshold (default: from env or 0.4)", - ) - - # Ollama model name - parser.add_argument( - "--simulated-model-name", - type=str, - default=get_env_value( - "SIMULATED_MODEL_NAME", ollama_server_infos.LIGHTRAG_MODEL - ), - help="Number of conversation history turns to include (default: from env or 3)", - ) - - # Namespace - parser.add_argument( - "--namespace-prefix", - type=str, - default=get_env_value("NAMESPACE_PREFIX", ""), - help="Prefix of the namespace", - ) - - parser.add_argument( - "--auto-scan-at-startup", - action="store_true", - default=False, - help="Enable automatic scanning when the program starts", - ) - - # Server workers configuration - parser.add_argument( - "--workers", - type=int, - default=get_env_value("WORKERS", 1, int), - help="Number of worker processes (default: from env or 1)", - ) - - # LLM and embedding bindings - parser.add_argument( - "--llm-binding", - type=str, - default=get_env_value("LLM_BINDING", "ollama"), - choices=["lollms", "ollama", "openai", "openai-ollama", "azure_openai"], - help="LLM binding type (default: from env or ollama)", - ) - parser.add_argument( - "--embedding-binding", - type=str, - default=get_env_value("EMBEDDING_BINDING", "ollama"), - choices=["lollms", "ollama", "openai", "azure_openai"], - help="Embedding binding type (default: from env or ollama)", - ) - - args = parser.parse_args() - - # If in uvicorn mode and workers > 1, force it to 1 and log warning - if is_uvicorn_mode and args.workers > 1: - original_workers = args.workers - args.workers = 1 - # Log warning directly here - logging.warning( - f"In uvicorn mode, workers parameter was set to {original_workers}. Forcing workers=1" - ) - - # convert relative path to absolute path - args.working_dir = os.path.abspath(args.working_dir) - args.input_dir = os.path.abspath(args.input_dir) - - # Inject storage configuration from environment variables - args.kv_storage = get_env_value( - "LIGHTRAG_KV_STORAGE", DefaultRAGStorageConfig.KV_STORAGE - ) - args.doc_status_storage = get_env_value( - "LIGHTRAG_DOC_STATUS_STORAGE", DefaultRAGStorageConfig.DOC_STATUS_STORAGE - ) - args.graph_storage = get_env_value( - "LIGHTRAG_GRAPH_STORAGE", DefaultRAGStorageConfig.GRAPH_STORAGE - ) - args.vector_storage = get_env_value( - "LIGHTRAG_VECTOR_STORAGE", DefaultRAGStorageConfig.VECTOR_STORAGE - ) - - # Get MAX_PARALLEL_INSERT from environment - args.max_parallel_insert = get_env_value("MAX_PARALLEL_INSERT", 2, int) - - # Handle openai-ollama special case - if args.llm_binding == "openai-ollama": - args.llm_binding = "openai" - args.embedding_binding = "ollama" - - args.llm_binding_host = get_env_value( - "LLM_BINDING_HOST", get_default_host(args.llm_binding) - ) - args.embedding_binding_host = get_env_value( - "EMBEDDING_BINDING_HOST", get_default_host(args.embedding_binding) - ) - args.llm_binding_api_key = get_env_value("LLM_BINDING_API_KEY", None) - args.embedding_binding_api_key = get_env_value("EMBEDDING_BINDING_API_KEY", "") - - # Inject model configuration - args.llm_model = get_env_value("LLM_MODEL", "mistral-nemo:latest") - args.embedding_model = get_env_value("EMBEDDING_MODEL", "bge-m3:latest") - args.embedding_dim = get_env_value("EMBEDDING_DIM", 1024, int) - args.max_embed_tokens = get_env_value("MAX_EMBED_TOKENS", 8192, int) - - # Inject chunk configuration - args.chunk_size = get_env_value("CHUNK_SIZE", 1200, int) - args.chunk_overlap_size = get_env_value("CHUNK_OVERLAP_SIZE", 100, int) - - # Inject LLM cache configuration - args.enable_llm_cache_for_extract = get_env_value( - "ENABLE_LLM_CACHE_FOR_EXTRACT", True, bool - ) - - # Inject LLM temperature configuration - args.temperature = get_env_value("TEMPERATURE", 0.5, float) - - # Select Document loading tool (DOCLING, DEFAULT) - args.document_loading_engine = get_env_value("DOCUMENT_LOADING_ENGINE", "DEFAULT") - - ollama_server_infos.LIGHTRAG_MODEL = args.simulated_model_name - - global_args["main_args"] = args - return args - - def display_splash_screen(args: argparse.Namespace) -> None: """ Display a colorful splash screen showing LightRAG server configuration @@ -503,7 +187,7 @@ def display_splash_screen(args: argparse.Namespace) -> None: ASCIIColors.white(" ├─ Workers: ", end="") ASCIIColors.yellow(f"{args.workers}") ASCIIColors.white(" ├─ CORS Origins: ", end="") - ASCIIColors.yellow(f"{os.getenv('CORS_ORIGINS', '*')}") + ASCIIColors.yellow(f"{args.cors_origins}") ASCIIColors.white(" ├─ SSL Enabled: ", end="") ASCIIColors.yellow(f"{args.ssl}") if args.ssl: @@ -519,8 +203,10 @@ def display_splash_screen(args: argparse.Namespace) -> None: ASCIIColors.yellow(f"{args.verbose}") ASCIIColors.white(" ├─ History Turns: ", end="") ASCIIColors.yellow(f"{args.history_turns}") - ASCIIColors.white(" └─ API Key: ", end="") + ASCIIColors.white(" ├─ API Key: ", end="") ASCIIColors.yellow("Set" if args.key else "Not Set") + ASCIIColors.white(" └─ JWT Auth: ", end="") + ASCIIColors.yellow("Enabled" if args.auth_accounts else "Disabled") # Directory Configuration ASCIIColors.magenta("\n📂 Directory Configuration:") @@ -558,10 +244,9 @@ def display_splash_screen(args: argparse.Namespace) -> None: ASCIIColors.yellow(f"{args.embedding_dim}") # RAG Configuration - summary_language = os.getenv("SUMMARY_LANGUAGE", PROMPTS["DEFAULT_LANGUAGE"]) ASCIIColors.magenta("\n⚙️ RAG Configuration:") ASCIIColors.white(" ├─ Summary Language: ", end="") - ASCIIColors.yellow(f"{summary_language}") + ASCIIColors.yellow(f"{args.summary_language}") ASCIIColors.white(" ├─ Max Parallel Insert: ", end="") ASCIIColors.yellow(f"{args.max_parallel_insert}") ASCIIColors.white(" ├─ Max Embed Tokens: ", end="") @@ -595,19 +280,17 @@ def display_splash_screen(args: argparse.Namespace) -> None: protocol = "https" if args.ssl else "http" if args.host == "0.0.0.0": ASCIIColors.magenta("\n🌐 Server Access Information:") - ASCIIColors.white(" ├─ Local Access: ", end="") + ASCIIColors.white(" ├─ WebUI (local): ", end="") ASCIIColors.yellow(f"{protocol}://localhost:{args.port}") ASCIIColors.white(" ├─ Remote Access: ", end="") ASCIIColors.yellow(f"{protocol}://:{args.port}") ASCIIColors.white(" ├─ API Documentation (local): ", end="") ASCIIColors.yellow(f"{protocol}://localhost:{args.port}/docs") - ASCIIColors.white(" ├─ Alternative Documentation (local): ", end="") + ASCIIColors.white(" └─ Alternative Documentation (local): ", end="") ASCIIColors.yellow(f"{protocol}://localhost:{args.port}/redoc") - ASCIIColors.white(" └─ WebUI (local): ", end="") - ASCIIColors.yellow(f"{protocol}://localhost:{args.port}/webui") - ASCIIColors.yellow("\n📝 Note:") - ASCIIColors.white(""" Since the server is running on 0.0.0.0: + ASCIIColors.magenta("\n📝 Note:") + ASCIIColors.cyan(""" Since the server is running on 0.0.0.0: - Use 'localhost' or '127.0.0.1' for local access - Use your machine's IP address for remote access - To find your IP address: @@ -617,42 +300,24 @@ def display_splash_screen(args: argparse.Namespace) -> None: else: base_url = f"{protocol}://{args.host}:{args.port}" ASCIIColors.magenta("\n🌐 Server Access Information:") - ASCIIColors.white(" ├─ Base URL: ", end="") + ASCIIColors.white(" ├─ WebUI (local): ", end="") ASCIIColors.yellow(f"{base_url}") ASCIIColors.white(" ├─ API Documentation: ", end="") ASCIIColors.yellow(f"{base_url}/docs") ASCIIColors.white(" └─ Alternative Documentation: ", end="") ASCIIColors.yellow(f"{base_url}/redoc") - # Usage Examples - ASCIIColors.magenta("\n📚 Quick Start Guide:") - ASCIIColors.cyan(""" - 1. Access the Swagger UI: - Open your browser and navigate to the API documentation URL above - - 2. API Authentication:""") - if args.key: - ASCIIColors.cyan(""" Add the following header to your requests: - X-API-Key: - """) - else: - ASCIIColors.cyan(" No authentication required\n") - - ASCIIColors.cyan(""" 3. Basic Operations: - - POST /upload_document: Upload new documents to RAG - - POST /query: Query your document collection - - 4. Monitor the server: - - Check server logs for detailed operation information - - Use healthcheck endpoint: GET /health - """) - # Security Notice if args.key: ASCIIColors.yellow("\n⚠️ Security Notice:") ASCIIColors.white(""" API Key authentication is enabled. Make sure to include the X-API-Key header in all your requests. """) + if args.auth_accounts: + ASCIIColors.yellow("\n⚠️ Security Notice:") + ASCIIColors.white(""" JWT authentication is enabled. + Make sure to login before making the request, and include the 'Authorization' in the header. + """) # Ensure splash output flush to system log sys.stdout.flush() diff --git a/lightrag/api/webui/assets/index-D8zGvNlV.js b/lightrag/api/webui/assets/index-BaHKTcxB.js similarity index 65% rename from lightrag/api/webui/assets/index-D8zGvNlV.js rename to lightrag/api/webui/assets/index-BaHKTcxB.js index bbcae58f..70170bb8 100644 --- a/lightrag/api/webui/assets/index-D8zGvNlV.js +++ b/lightrag/api/webui/assets/index-BaHKTcxB.js @@ -1,4 +1,4 @@ -var eq=Object.defineProperty;var tq=(e,t,n)=>t in e?eq(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Xr=(e,t,n)=>tq(e,typeof t!="symbol"?t+"":t,n);function nq(e,t){for(var n=0;nr[a]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))r(a);new MutationObserver(a=>{for(const o of a)if(o.type==="childList")for(const s of o.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(a){const o={};return a.integrity&&(o.integrity=a.integrity),a.referrerPolicy&&(o.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?o.credentials="include":a.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(a){if(a.ep)return;a.ep=!0;const o=n(a);fetch(a.href,o)}})();var df=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function cn(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function rq(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var n=function r(){return this instanceof r?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};n.prototype=t.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(e).forEach(function(r){var a=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(n,r,a.get?a:{enumerable:!0,get:function(){return e[r]}})}),n}var qh={exports:{}},Zl={};/** +var nq=Object.defineProperty;var rq=(e,t,n)=>t in e?nq(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Zr=(e,t,n)=>rq(e,typeof t!="symbol"?t+"":t,n);function aq(e,t){for(var n=0;nr[a]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))r(a);new MutationObserver(a=>{for(const o of a)if(o.type==="childList")for(const s of o.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(a){const o={};return a.integrity&&(o.integrity=a.integrity),a.referrerPolicy&&(o.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?o.credentials="include":a.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(a){if(a.ep)return;a.ep=!0;const o=n(a);fetch(a.href,o)}})();var ff=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function cn(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function oq(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var n=function r(){return this instanceof r?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};n.prototype=t.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(e).forEach(function(r){var a=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(n,r,a.get?a:{enumerable:!0,get:function(){return e[r]}})}),n}var Wh={exports:{}},Zl={};/** * @license React * react-jsx-runtime.production.js * @@ -6,7 +6,7 @@ var eq=Object.defineProperty;var tq=(e,t,n)=>t in e?eq(e,t,{enumerable:!0,config * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var W_;function aq(){if(W_)return Zl;W_=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function n(r,a,o){var s=null;if(o!==void 0&&(s=""+o),a.key!==void 0&&(s=""+a.key),"key"in a){o={};for(var u in a)u!=="key"&&(o[u]=a[u])}else o=a;return a=o.ref,{$$typeof:e,type:r,key:s,ref:a!==void 0?a:null,props:o}}return Zl.Fragment=t,Zl.jsx=n,Zl.jsxs=n,Zl}var Y_;function oq(){return Y_||(Y_=1,qh.exports=aq()),qh.exports}var E=oq(),Vh={exports:{}},it={};/** + */var XC;function iq(){if(XC)return Zl;XC=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function n(r,a,o){var s=null;if(o!==void 0&&(s=""+o),a.key!==void 0&&(s=""+a.key),"key"in a){o={};for(var u in a)u!=="key"&&(o[u]=a[u])}else o=a;return a=o.ref,{$$typeof:e,type:r,key:s,ref:a!==void 0?a:null,props:o}}return Zl.Fragment=t,Zl.jsx=n,Zl.jsxs=n,Zl}var ZC;function sq(){return ZC||(ZC=1,Wh.exports=iq()),Wh.exports}var E=sq(),Yh={exports:{}},st={};/** * @license React * react.production.js * @@ -14,7 +14,7 @@ var eq=Object.defineProperty;var tq=(e,t,n)=>t in e?eq(e,t,{enumerable:!0,config * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var K_;function iq(){if(K_)return it;K_=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),o=Symbol.for("react.consumer"),s=Symbol.for("react.context"),u=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),p=Symbol.for("react.lazy"),g=Symbol.iterator;function m(M){return M===null||typeof M!="object"?null:(M=g&&M[g]||M["@@iterator"],typeof M=="function"?M:null)}var b={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},y=Object.assign,v={};function x(M,V,U){this.props=M,this.context=V,this.refs=v,this.updater=U||b}x.prototype.isReactComponent={},x.prototype.setState=function(M,V){if(typeof M!="object"&&typeof M!="function"&&M!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,M,V,"setState")},x.prototype.forceUpdate=function(M){this.updater.enqueueForceUpdate(this,M,"forceUpdate")};function R(){}R.prototype=x.prototype;function k(M,V,U){this.props=M,this.context=V,this.refs=v,this.updater=U||b}var A=k.prototype=new R;A.constructor=k,y(A,x.prototype),A.isPureReactComponent=!0;var O=Array.isArray,N={H:null,A:null,T:null,S:null},_=Object.prototype.hasOwnProperty;function C(M,V,U,P,K,ee){return U=ee.ref,{$$typeof:e,type:M,key:V,ref:U!==void 0?U:null,props:ee}}function L(M,V){return C(M.type,V,void 0,void 0,void 0,M.props)}function I(M){return typeof M=="object"&&M!==null&&M.$$typeof===e}function D(M){var V={"=":"=0",":":"=2"};return"$"+M.replace(/[=:]/g,function(U){return V[U]})}var H=/\/+/g;function $(M,V){return typeof M=="object"&&M!==null&&M.key!=null?D(""+M.key):V.toString(36)}function B(){}function W(M){switch(M.status){case"fulfilled":return M.value;case"rejected":throw M.reason;default:switch(typeof M.status=="string"?M.then(B,B):(M.status="pending",M.then(function(V){M.status==="pending"&&(M.status="fulfilled",M.value=V)},function(V){M.status==="pending"&&(M.status="rejected",M.reason=V)})),M.status){case"fulfilled":return M.value;case"rejected":throw M.reason}}throw M}function Q(M,V,U,P,K){var ee=typeof M;(ee==="undefined"||ee==="boolean")&&(M=null);var le=!1;if(M===null)le=!0;else switch(ee){case"bigint":case"string":case"number":le=!0;break;case"object":switch(M.$$typeof){case e:case t:le=!0;break;case p:return le=M._init,Q(le(M._payload),V,U,P,K)}}if(le)return K=K(M),le=P===""?"."+$(M,0):P,O(K)?(U="",le!=null&&(U=le.replace(H,"$&/")+"/"),Q(K,V,U,"",function(he){return he})):K!=null&&(I(K)&&(K=L(K,U+(K.key==null||M&&M.key===K.key?"":(""+K.key).replace(H,"$&/")+"/")+le)),V.push(K)),1;le=0;var X=P===""?".":P+":";if(O(M))for(var J=0;Jt in e?eq(e,t,{enumerable:!0,config * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Z_;function lq(){return Z_||(Z_=1,function(e){function t(G,j){var F=G.length;G.push(j);e:for(;0>>1,M=G[Y];if(0>>1;Ya(P,F))Ka(ee,P)?(G[Y]=ee,G[K]=F,Y=K):(G[Y]=P,G[U]=F,Y=U);else if(Ka(ee,F))G[Y]=ee,G[K]=F,Y=K;else break e}}return j}function a(G,j){var F=G.sortIndex-j.sortIndex;return F!==0?F:G.id-j.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var s=Date,u=s.now();e.unstable_now=function(){return s.now()-u}}var c=[],d=[],p=1,g=null,m=3,b=!1,y=!1,v=!1,x=typeof setTimeout=="function"?setTimeout:null,R=typeof clearTimeout=="function"?clearTimeout:null,k=typeof setImmediate<"u"?setImmediate:null;function A(G){for(var j=n(d);j!==null;){if(j.callback===null)r(d);else if(j.startTime<=G)r(d),j.sortIndex=j.expirationTime,t(c,j);else break;j=n(d)}}function O(G){if(v=!1,A(G),!y)if(n(c)!==null)y=!0,W();else{var j=n(d);j!==null&&Q(O,j.startTime-G)}}var N=!1,_=-1,C=5,L=-1;function I(){return!(e.unstable_now()-LG&&I());){var Y=g.callback;if(typeof Y=="function"){g.callback=null,m=g.priorityLevel;var M=Y(g.expirationTime<=G);if(G=e.unstable_now(),typeof M=="function"){g.callback=M,A(G),j=!0;break t}g===n(c)&&r(c),A(G)}else r(c);g=n(c)}if(g!==null)j=!0;else{var V=n(d);V!==null&&Q(O,V.startTime-G),j=!1}}break e}finally{g=null,m=F,b=!1}j=void 0}}finally{j?H():N=!1}}}var H;if(typeof k=="function")H=function(){k(D)};else if(typeof MessageChannel<"u"){var $=new MessageChannel,B=$.port2;$.port1.onmessage=D,H=function(){B.postMessage(null)}}else H=function(){x(D,0)};function W(){N||(N=!0,H())}function Q(G,j){_=x(function(){G(e.unstable_now())},j)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(G){G.callback=null},e.unstable_continueExecution=function(){y||b||(y=!0,W())},e.unstable_forceFrameRate=function(G){0>G||125Y?(G.sortIndex=F,t(d,G),n(c)===null&&G===n(d)&&(v?(R(_),_=-1):v=!0,Q(O,F-Y))):(G.sortIndex=M,t(c,G),y||b||(y=!0,W())),G},e.unstable_shouldYield=I,e.unstable_wrapCallback=function(G){var j=m;return function(){var F=m;m=j;try{return G.apply(this,arguments)}finally{m=F}}}}(Kh)),Kh}var Q_;function uq(){return Q_||(Q_=1,Yh.exports=lq()),Yh.exports}var Xh={exports:{}},wn={};/** + */var e_;function cq(){return e_||(e_=1,function(e){function t(G,j){var F=G.length;G.push(j);e:for(;0>>1,M=G[Y];if(0>>1;Ya(P,F))Ka(ee,P)?(G[Y]=ee,G[K]=F,Y=K):(G[Y]=P,G[U]=F,Y=U);else if(Ka(ee,F))G[Y]=ee,G[K]=F,Y=K;else break e}}return j}function a(G,j){var F=G.sortIndex-j.sortIndex;return F!==0?F:G.id-j.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var s=Date,u=s.now();e.unstable_now=function(){return s.now()-u}}var c=[],d=[],p=1,g=null,m=3,b=!1,y=!1,v=!1,x=typeof setTimeout=="function"?setTimeout:null,A=typeof clearTimeout=="function"?clearTimeout:null,k=typeof setImmediate<"u"?setImmediate:null;function R(G){for(var j=n(d);j!==null;){if(j.callback===null)r(d);else if(j.startTime<=G)r(d),j.sortIndex=j.expirationTime,t(c,j);else break;j=n(d)}}function O(G){if(v=!1,R(G),!y)if(n(c)!==null)y=!0,W();else{var j=n(d);j!==null&&Q(O,j.startTime-G)}}var N=!1,C=-1,_=5,L=-1;function I(){return!(e.unstable_now()-L<_)}function D(){if(N){var G=e.unstable_now();L=G;var j=!0;try{e:{y=!1,v&&(v=!1,A(C),C=-1),b=!0;var F=m;try{t:{for(R(G),g=n(c);g!==null&&!(g.expirationTime>G&&I());){var Y=g.callback;if(typeof Y=="function"){g.callback=null,m=g.priorityLevel;var M=Y(g.expirationTime<=G);if(G=e.unstable_now(),typeof M=="function"){g.callback=M,R(G),j=!0;break t}g===n(c)&&r(c),R(G)}else r(c);g=n(c)}if(g!==null)j=!0;else{var V=n(d);V!==null&&Q(O,V.startTime-G),j=!1}}break e}finally{g=null,m=F,b=!1}j=void 0}}finally{j?H():N=!1}}}var H;if(typeof k=="function")H=function(){k(D)};else if(typeof MessageChannel<"u"){var $=new MessageChannel,B=$.port2;$.port1.onmessage=D,H=function(){B.postMessage(null)}}else H=function(){x(D,0)};function W(){N||(N=!0,H())}function Q(G,j){C=x(function(){G(e.unstable_now())},j)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(G){G.callback=null},e.unstable_continueExecution=function(){y||b||(y=!0,W())},e.unstable_forceFrameRate=function(G){0>G||125Y?(G.sortIndex=F,t(d,G),n(c)===null&&G===n(d)&&(v?(A(C),C=-1):v=!0,Q(O,F-Y))):(G.sortIndex=M,t(c,G),y||b||(y=!0,W())),G},e.unstable_shouldYield=I,e.unstable_wrapCallback=function(G){var j=m;return function(){var F=m;m=j;try{return G.apply(this,arguments)}finally{m=F}}}}(Zh)),Zh}var t_;function dq(){return t_||(t_=1,Xh.exports=cq()),Xh.exports}var Qh={exports:{}},wn={};/** * @license React * react-dom.production.js * @@ -30,7 +30,7 @@ var eq=Object.defineProperty;var tq=(e,t,n)=>t in e?eq(e,t,{enumerable:!0,config * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var J_;function cq(){if(J_)return wn;J_=1;var e=Hf();function t(c){var d="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),Xh.exports=cq(),Xh.exports}/** + */var n_;function fq(){if(n_)return wn;n_=1;var e=$f();function t(c){var d="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),Qh.exports=fq(),Qh.exports}/** * @license React * react-dom-client.production.js * @@ -38,15 +38,15 @@ var eq=Object.defineProperty;var tq=(e,t,n)=>t in e?eq(e,t,{enumerable:!0,config * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var tC;function dq(){if(tC)return Ql;tC=1;var e=uq(),t=Hf(),n=zz();function r(i){var l="https://react.dev/errors/"+i;if(1)":-1S||Z[h]!==ae[S]){var ye=` `+Z[h].replace(" at new "," at ");return i.displayName&&ye.includes("")&&(ye=ye.replace("",i.displayName)),ye}while(1<=h&&0<=S);break}}}finally{W=!1,Error.prepareStackTrace=f}return(f=i?i.displayName||i.name:"")?B(f):""}function G(i){switch(i.tag){case 26:case 27:case 5:return B(i.type);case 16:return B("Lazy");case 13:return B("Suspense");case 19:return B("SuspenseList");case 0:case 15:return i=Q(i.type,!1),i;case 11:return i=Q(i.type.render,!1),i;case 1:return i=Q(i.type,!0),i;default:return""}}function j(i){try{var l="";do l+=G(i),i=i.return;while(i);return l}catch(f){return` Error generating stack: `+f.message+` -`+f.stack}}function F(i){var l=i,f=i;if(i.alternate)for(;l.return;)l=l.return;else{i=l;do l=i,l.flags&4098&&(f=l.return),i=l.return;while(i)}return l.tag===3?f:null}function Y(i){if(i.tag===13){var l=i.memoizedState;if(l===null&&(i=i.alternate,i!==null&&(l=i.memoizedState)),l!==null)return l.dehydrated}return null}function M(i){if(F(i)!==i)throw Error(r(188))}function V(i){var l=i.alternate;if(!l){if(l=F(i),l===null)throw Error(r(188));return l!==i?null:i}for(var f=i,h=l;;){var S=f.return;if(S===null)break;var T=S.alternate;if(T===null){if(h=S.return,h!==null){f=h;continue}break}if(S.child===T.child){for(T=S.child;T;){if(T===f)return M(S),i;if(T===h)return M(S),l;T=T.sibling}throw Error(r(188))}if(f.return!==h.return)f=S,h=T;else{for(var z=!1,q=S.child;q;){if(q===f){z=!0,f=S,h=T;break}if(q===h){z=!0,h=S,f=T;break}q=q.sibling}if(!z){for(q=T.child;q;){if(q===f){z=!0,f=T,h=S;break}if(q===h){z=!0,h=T,f=S;break}q=q.sibling}if(!z)throw Error(r(189))}}if(f.alternate!==h)throw Error(r(190))}if(f.tag!==3)throw Error(r(188));return f.stateNode.current===f?i:l}function U(i){var l=i.tag;if(l===5||l===26||l===27||l===6)return i;for(i=i.child;i!==null;){if(l=U(i),l!==null)return l;i=i.sibling}return null}var P=Array.isArray,K=n.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ee={pending:!1,data:null,method:null,action:null},le=[],X=-1;function J(i){return{current:i}}function he(i){0>X||(i.current=le[X],le[X]=null,X--)}function oe(i,l){X++,le[X]=i.current,i.current=l}var Se=J(null),we=J(null),De=J(null),_e=J(null);function Ee(i,l){switch(oe(De,l),oe(we,i),oe(Se,null),i=l.nodeType,i){case 9:case 11:l=(l=l.documentElement)&&(l=l.namespaceURI)?w_(l):0;break;default:if(i=i===8?l.parentNode:l,l=i.tagName,i=i.namespaceURI)i=w_(i),l=x_(i,l);else switch(l){case"svg":l=1;break;case"math":l=2;break;default:l=0}}he(Se),oe(Se,l)}function te(){he(Se),he(we),he(De)}function fe(i){i.memoizedState!==null&&oe(_e,i);var l=Se.current,f=x_(l,i.type);l!==f&&(oe(we,i),oe(Se,f))}function Te(i){we.current===i&&(he(Se),he(we)),_e.current===i&&(he(_e),Vl._currentValue=ee)}var be=Object.prototype.hasOwnProperty,xe=e.unstable_scheduleCallback,se=e.unstable_cancelCallback,ze=e.unstable_shouldYield,Be=e.unstable_requestPaint,me=e.unstable_now,Ne=e.unstable_getCurrentPriorityLevel,ne=e.unstable_ImmediatePriority,ce=e.unstable_UserBlockingPriority,Ce=e.unstable_NormalPriority,Fe=e.unstable_LowPriority,We=e.unstable_IdlePriority,St=e.log,kt=e.unstable_setDisableYieldValue,bt=null,et=null;function Tt(i){if(et&&typeof et.onCommitFiberRoot=="function")try{et.onCommitFiberRoot(bt,i,void 0,(i.current.flags&128)===128)}catch{}}function ot(i){if(typeof St=="function"&&kt(i),et&&typeof et.setStrictMode=="function")try{et.setStrictMode(bt,i)}catch{}}var Et=Math.clz32?Math.clz32:Ft,Ht=Math.log,pn=Math.LN2;function Ft(i){return i>>>=0,i===0?32:31-(Ht(i)/pn|0)|0}var or=128,$r=4194304;function Jt(i){var l=i&42;if(l!==0)return l;switch(i&-i){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return i&4194176;case 4194304:case 8388608:case 16777216:case 33554432:return i&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return i}}function fa(i,l){var f=i.pendingLanes;if(f===0)return 0;var h=0,S=i.suspendedLanes,T=i.pingedLanes,z=i.warmLanes;i=i.finishedLanes!==0;var q=f&134217727;return q!==0?(f=q&~S,f!==0?h=Jt(f):(T&=q,T!==0?h=Jt(T):i||(z=q&~z,z!==0&&(h=Jt(z))))):(q=f&~S,q!==0?h=Jt(q):T!==0?h=Jt(T):i||(z=f&~z,z!==0&&(h=Jt(z)))),h===0?0:l!==0&&l!==h&&!(l&S)&&(S=h&-h,z=l&-l,S>=z||S===32&&(z&4194176)!==0)?l:h}function Xe(i,l){return(i.pendingLanes&~(i.suspendedLanes&~i.pingedLanes)&l)===0}function yt(i,l){switch(i){case 1:case 2:case 4:case 8:return l+250;case 16:case 32:case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return l+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Nt(){var i=or;return or<<=1,!(or&4194176)&&(or=128),i}function In(){var i=$r;return $r<<=1,!($r&62914560)&&($r=4194304),i}function Cn(i){for(var l=[],f=0;31>f;f++)l.push(i);return l}function Ln(i,l){i.pendingLanes|=l,l!==268435456&&(i.suspendedLanes=0,i.pingedLanes=0,i.warmLanes=0)}function pa(i,l,f,h,S,T){var z=i.pendingLanes;i.pendingLanes=f,i.suspendedLanes=0,i.pingedLanes=0,i.warmLanes=0,i.expiredLanes&=f,i.entangledLanes&=f,i.errorRecoveryDisabledLanes&=f,i.shellSuspendCounter=0;var q=i.entanglements,Z=i.expirationTimes,ae=i.hiddenUpdates;for(f=z&~f;0"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),WH=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$"),xA={},kA={};function YH(i){return be.call(kA,i)?!0:be.call(xA,i)?!1:WH.test(i)?kA[i]=!0:(xA[i]=!0,!1)}function sc(i,l,f){if(YH(l))if(f===null)i.removeAttribute(l);else{switch(typeof f){case"undefined":case"function":case"symbol":i.removeAttribute(l);return;case"boolean":var h=l.toLowerCase().slice(0,5);if(h!=="data-"&&h!=="aria-"){i.removeAttribute(l);return}}i.setAttribute(l,""+f)}}function lc(i,l,f){if(f===null)i.removeAttribute(l);else{switch(typeof f){case"undefined":case"function":case"symbol":case"boolean":i.removeAttribute(l);return}i.setAttribute(l,""+f)}}function ha(i,l,f,h){if(h===null)i.removeAttribute(f);else{switch(typeof h){case"undefined":case"function":case"symbol":case"boolean":i.removeAttribute(f);return}i.setAttributeNS(l,f,""+h)}}function ir(i){switch(typeof i){case"bigint":case"boolean":case"number":case"string":case"undefined":return i;case"object":return i;default:return""}}function TA(i){var l=i.type;return(i=i.nodeName)&&i.toLowerCase()==="input"&&(l==="checkbox"||l==="radio")}function KH(i){var l=TA(i)?"checked":"value",f=Object.getOwnPropertyDescriptor(i.constructor.prototype,l),h=""+i[l];if(!i.hasOwnProperty(l)&&typeof f<"u"&&typeof f.get=="function"&&typeof f.set=="function"){var S=f.get,T=f.set;return Object.defineProperty(i,l,{configurable:!0,get:function(){return S.call(this)},set:function(z){h=""+z,T.call(this,z)}}),Object.defineProperty(i,l,{enumerable:f.enumerable}),{getValue:function(){return h},setValue:function(z){h=""+z},stopTracking:function(){i._valueTracker=null,delete i[l]}}}}function uc(i){i._valueTracker||(i._valueTracker=KH(i))}function AA(i){if(!i)return!1;var l=i._valueTracker;if(!l)return!0;var f=l.getValue(),h="";return i&&(h=TA(i)?i.checked?"true":"false":i.value),i=h,i!==f?(l.setValue(i),!0):!1}function cc(i){if(i=i||(typeof document<"u"?document:void 0),typeof i>"u")return null;try{return i.activeElement||i.body}catch{return i.body}}var XH=/[\n"\\]/g;function sr(i){return i.replace(XH,function(l){return"\\"+l.charCodeAt(0).toString(16)+" "})}function Hp(i,l,f,h,S,T,z,q){i.name="",z!=null&&typeof z!="function"&&typeof z!="symbol"&&typeof z!="boolean"?i.type=z:i.removeAttribute("type"),l!=null?z==="number"?(l===0&&i.value===""||i.value!=l)&&(i.value=""+ir(l)):i.value!==""+ir(l)&&(i.value=""+ir(l)):z!=="submit"&&z!=="reset"||i.removeAttribute("value"),l!=null?$p(i,z,ir(l)):f!=null?$p(i,z,ir(f)):h!=null&&i.removeAttribute("value"),S==null&&T!=null&&(i.defaultChecked=!!T),S!=null&&(i.checked=S&&typeof S!="function"&&typeof S!="symbol"),q!=null&&typeof q!="function"&&typeof q!="symbol"&&typeof q!="boolean"?i.name=""+ir(q):i.removeAttribute("name")}function RA(i,l,f,h,S,T,z,q){if(T!=null&&typeof T!="function"&&typeof T!="symbol"&&typeof T!="boolean"&&(i.type=T),l!=null||f!=null){if(!(T!=="submit"&&T!=="reset"||l!=null))return;f=f!=null?""+ir(f):"",l=l!=null?""+ir(l):f,q||l===i.value||(i.value=l),i.defaultValue=l}h=h??S,h=typeof h!="function"&&typeof h!="symbol"&&!!h,i.checked=q?i.checked:!!h,i.defaultChecked=!!h,z!=null&&typeof z!="function"&&typeof z!="symbol"&&typeof z!="boolean"&&(i.name=z)}function $p(i,l,f){l==="number"&&cc(i.ownerDocument)===i||i.defaultValue===""+f||(i.defaultValue=""+f)}function Di(i,l,f,h){if(i=i.options,l){l={};for(var S=0;S=cl),UA=" ",jA=!1;function GA(i,l){switch(i){case"keyup":return k$.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function HA(i){return i=i.detail,typeof i=="object"&&"data"in i?i.data:null}var Pi=!1;function A$(i,l){switch(i){case"compositionend":return HA(l);case"keypress":return l.which!==32?null:(jA=!0,UA);case"textInput":return i=l.data,i===UA&&jA?null:i;default:return null}}function R$(i,l){if(Pi)return i==="compositionend"||!tg&&GA(i,l)?(i=LA(),fc=Xp=Ya=null,Pi=!1,i):null;switch(i){case"paste":return null;case"keypress":if(!(l.ctrlKey||l.altKey||l.metaKey)||l.ctrlKey&&l.altKey){if(l.char&&1=l)return{node:f,offset:l-i};i=h}e:{for(;f;){if(f.nextSibling){f=f.nextSibling;break e}f=f.parentNode}f=void 0}f=ZA(f)}}function JA(i,l){return i&&l?i===l?!0:i&&i.nodeType===3?!1:l&&l.nodeType===3?JA(i,l.parentNode):"contains"in i?i.contains(l):i.compareDocumentPosition?!!(i.compareDocumentPosition(l)&16):!1:!1}function e1(i){i=i!=null&&i.ownerDocument!=null&&i.ownerDocument.defaultView!=null?i.ownerDocument.defaultView:window;for(var l=cc(i.document);l instanceof i.HTMLIFrameElement;){try{var f=typeof l.contentWindow.location.href=="string"}catch{f=!1}if(f)i=l.contentWindow;else break;l=cc(i.document)}return l}function ag(i){var l=i&&i.nodeName&&i.nodeName.toLowerCase();return l&&(l==="input"&&(i.type==="text"||i.type==="search"||i.type==="tel"||i.type==="url"||i.type==="password")||l==="textarea"||i.contentEditable==="true")}function M$(i,l){var f=e1(l);l=i.focusedElem;var h=i.selectionRange;if(f!==l&&l&&l.ownerDocument&&JA(l.ownerDocument.documentElement,l)){if(h!==null&&ag(l)){if(i=h.start,f=h.end,f===void 0&&(f=i),"selectionStart"in l)l.selectionStart=i,l.selectionEnd=Math.min(f,l.value.length);else if(f=(i=l.ownerDocument||document)&&i.defaultView||window,f.getSelection){f=f.getSelection();var S=l.textContent.length,T=Math.min(h.start,S);h=h.end===void 0?T:Math.min(h.end,S),!f.extend&&T>h&&(S=h,h=T,T=S),S=QA(l,T);var z=QA(l,h);S&&z&&(f.rangeCount!==1||f.anchorNode!==S.node||f.anchorOffset!==S.offset||f.focusNode!==z.node||f.focusOffset!==z.offset)&&(i=i.createRange(),i.setStart(S.node,S.offset),f.removeAllRanges(),T>h?(f.addRange(i),f.extend(z.node,z.offset)):(i.setEnd(z.node,z.offset),f.addRange(i)))}}for(i=[],f=l;f=f.parentNode;)f.nodeType===1&&i.push({element:f,left:f.scrollLeft,top:f.scrollTop});for(typeof l.focus=="function"&&l.focus(),l=0;l=document.documentMode,Fi=null,og=null,gl=null,ig=!1;function t1(i,l,f){var h=f.window===f?f.document:f.nodeType===9?f:f.ownerDocument;ig||Fi==null||Fi!==cc(h)||(h=Fi,"selectionStart"in h&&ag(h)?h={start:h.selectionStart,end:h.selectionEnd}:(h=(h.ownerDocument&&h.ownerDocument.defaultView||window).getSelection(),h={anchorNode:h.anchorNode,anchorOffset:h.anchorOffset,focusNode:h.focusNode,focusOffset:h.focusOffset}),gl&&pl(gl,h)||(gl=h,h=Jc(og,"onSelect"),0>=z,S-=z,ma=1<<32-Et(l)+S|f<Qe?(ln=Ye,Ye=null):ln=Ye.sibling;var xt=de(ie,Ye,ue[Qe],ke);if(xt===null){Ye===null&&(Ye=ln);break}i&&Ye&&xt.alternate===null&&l(ie,Ye),re=T(xt,re,Qe),ct===null?je=xt:ct.sibling=xt,ct=xt,Ye=ln}if(Qe===ue.length)return f(ie,Ye),wt&&Go(ie,Qe),je;if(Ye===null){for(;QeQe?(ln=Ye,Ye=null):ln=Ye.sibling;var ho=de(ie,Ye,xt.value,ke);if(ho===null){Ye===null&&(Ye=ln);break}i&&Ye&&ho.alternate===null&&l(ie,Ye),re=T(ho,re,Qe),ct===null?je=ho:ct.sibling=ho,ct=ho,Ye=ln}if(xt.done)return f(ie,Ye),wt&&Go(ie,Qe),je;if(Ye===null){for(;!xt.done;Qe++,xt=ue.next())xt=Ae(ie,xt.value,ke),xt!==null&&(re=T(xt,re,Qe),ct===null?je=xt:ct.sibling=xt,ct=xt);return wt&&Go(ie,Qe),je}for(Ye=h(Ye);!xt.done;Qe++,xt=ue.next())xt=ge(Ye,ie,Qe,xt.value,ke),xt!==null&&(i&&xt.alternate!==null&&Ye.delete(xt.key===null?Qe:xt.key),re=T(xt,re,Qe),ct===null?je=xt:ct.sibling=xt,ct=xt);return i&&Ye.forEach(function(J6){return l(ie,J6)}),wt&&Go(ie,Qe),je}function Vt(ie,re,ue,ke){if(typeof ue=="object"&&ue!==null&&ue.type===c&&ue.key===null&&(ue=ue.props.children),typeof ue=="object"&&ue!==null){switch(ue.$$typeof){case s:e:{for(var je=ue.key;re!==null;){if(re.key===je){if(je=ue.type,je===c){if(re.tag===7){f(ie,re.sibling),ke=S(re,ue.props.children),ke.return=ie,ie=ke;break e}}else if(re.elementType===je||typeof je=="object"&&je!==null&&je.$$typeof===k&&y1(je)===re.type){f(ie,re.sibling),ke=S(re,ue.props),El(ke,ue),ke.return=ie,ie=ke;break e}f(ie,re);break}else l(ie,re);re=re.sibling}ue.type===c?(ke=Jo(ue.props.children,ie.mode,ke,ue.key),ke.return=ie,ie=ke):(ke=Hc(ue.type,ue.key,ue.props,null,ie.mode,ke),El(ke,ue),ke.return=ie,ie=ke)}return z(ie);case u:e:{for(je=ue.key;re!==null;){if(re.key===je)if(re.tag===4&&re.stateNode.containerInfo===ue.containerInfo&&re.stateNode.implementation===ue.implementation){f(ie,re.sibling),ke=S(re,ue.children||[]),ke.return=ie,ie=ke;break e}else{f(ie,re);break}else l(ie,re);re=re.sibling}ke=lh(ue,ie.mode,ke),ke.return=ie,ie=ke}return z(ie);case k:return je=ue._init,ue=je(ue._payload),Vt(ie,re,ue,ke)}if(P(ue))return qe(ie,re,ue,ke);if(_(ue)){if(je=_(ue),typeof je!="function")throw Error(r(150));return ue=je.call(ue),nt(ie,re,ue,ke)}if(typeof ue.then=="function")return Vt(ie,re,kc(ue),ke);if(ue.$$typeof===b)return Vt(ie,re,Uc(ie,ue),ke);Tc(ie,ue)}return typeof ue=="string"&&ue!==""||typeof ue=="number"||typeof ue=="bigint"?(ue=""+ue,re!==null&&re.tag===6?(f(ie,re.sibling),ke=S(re,ue),ke.return=ie,ie=ke):(f(ie,re),ke=sh(ue,ie.mode,ke),ke.return=ie,ie=ke),z(ie)):f(ie,re)}return function(ie,re,ue,ke){try{Sl=0;var je=Vt(ie,re,ue,ke);return Hi=null,je}catch(Ye){if(Ye===yl)throw Ye;var ct=hr(29,Ye,null,ie.mode);return ct.lanes=ke,ct.return=ie,ct}finally{}}}var $o=v1(!0),S1=v1(!1),$i=J(null),Ac=J(0);function E1(i,l){i=_a,oe(Ac,i),oe($i,l),_a=i|l.baseLanes}function gg(){oe(Ac,_a),oe($i,$i.current)}function hg(){_a=Ac.current,he($i),he(Ac)}var fr=J(null),Vr=null;function Xa(i){var l=i.alternate;oe(en,en.current&1),oe(fr,i),Vr===null&&(l===null||$i.current!==null||l.memoizedState!==null)&&(Vr=i)}function w1(i){if(i.tag===22){if(oe(en,en.current),oe(fr,i),Vr===null){var l=i.alternate;l!==null&&l.memoizedState!==null&&(Vr=i)}}else Za()}function Za(){oe(en,en.current),oe(fr,fr.current)}function ya(i){he(fr),Vr===i&&(Vr=null),he(en)}var en=J(0);function Rc(i){for(var l=i;l!==null;){if(l.tag===13){var f=l.memoizedState;if(f!==null&&(f=f.dehydrated,f===null||f.data==="$?"||f.data==="$!"))return l}else if(l.tag===19&&l.memoizedProps.revealOrder!==void 0){if(l.flags&128)return l}else if(l.child!==null){l.child.return=l,l=l.child;continue}if(l===i)break;for(;l.sibling===null;){if(l.return===null||l.return===i)return null;l=l.return}l.sibling.return=l.return,l=l.sibling}return null}var U$=typeof AbortController<"u"?AbortController:function(){var i=[],l=this.signal={aborted:!1,addEventListener:function(f,h){i.push(h)}};this.abort=function(){l.aborted=!0,i.forEach(function(f){return f()})}},j$=e.unstable_scheduleCallback,G$=e.unstable_NormalPriority,tn={$$typeof:b,Consumer:null,Provider:null,_currentValue:null,_currentValue2:null,_threadCount:0};function mg(){return{controller:new U$,data:new Map,refCount:0}}function wl(i){i.refCount--,i.refCount===0&&j$(G$,function(){i.controller.abort()})}var xl=null,bg=0,qi=0,Vi=null;function H$(i,l){if(xl===null){var f=xl=[];bg=0,qi=xh(),Vi={status:"pending",value:void 0,then:function(h){f.push(h)}}}return bg++,l.then(x1,x1),l}function x1(){if(--bg===0&&xl!==null){Vi!==null&&(Vi.status="fulfilled");var i=xl;xl=null,qi=0,Vi=null;for(var l=0;lT?T:8;var z=I.T,q={};I.T=q,Lg(i,!1,l,f);try{var Z=S(),ae=I.S;if(ae!==null&&ae(q,Z),Z!==null&&typeof Z=="object"&&typeof Z.then=="function"){var ye=$$(Z,h);Al(i,l,ye,Zn(i))}else Al(i,l,h,Zn(i))}catch(Ae){Al(i,l,{then:function(){},status:"rejected",reason:Ae},Zn())}finally{K.p=T,I.T=z}}function K$(){}function Dg(i,l,f,h){if(i.tag!==5)throw Error(r(476));var S=eR(i).queue;J1(i,S,l,ee,f===null?K$:function(){return tR(i),f(h)})}function eR(i){var l=i.memoizedState;if(l!==null)return l;l={memoizedState:ee,baseState:ee,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:va,lastRenderedState:ee},next:null};var f={};return l.next={memoizedState:f,baseState:f,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:va,lastRenderedState:f},next:null},i.memoizedState=l,i=i.alternate,i!==null&&(i.memoizedState=l),l}function tR(i){var l=eR(i).next.queue;Al(i,l,{},Zn())}function Ig(){return En(Vl)}function nR(){return Xt().memoizedState}function rR(){return Xt().memoizedState}function X$(i){for(var l=i.return;l!==null;){switch(l.tag){case 24:case 3:var f=Zn();i=no(f);var h=ro(l,i,f);h!==null&&(On(h,l,f),Cl(h,l,f)),l={cache:mg()},i.payload=l;return}l=l.return}}function Z$(i,l,f){var h=Zn();f={lane:h,revertLane:0,action:f,hasEagerState:!1,eagerState:null,next:null},Pc(i)?oR(l,f):(f=ug(i,l,f,h),f!==null&&(On(f,i,h),iR(f,l,h)))}function aR(i,l,f){var h=Zn();Al(i,l,f,h)}function Al(i,l,f,h){var S={lane:h,revertLane:0,action:f,hasEagerState:!1,eagerState:null,next:null};if(Pc(i))oR(l,S);else{var T=i.alternate;if(i.lanes===0&&(T===null||T.lanes===0)&&(T=l.lastRenderedReducer,T!==null))try{var z=l.lastRenderedState,q=T(z,f);if(S.hasEagerState=!0,S.eagerState=q,Wn(q,z))return vc(i,l,S,0),Lt===null&&yc(),!1}catch{}finally{}if(f=ug(i,l,S,h),f!==null)return On(f,i,h),iR(f,l,h),!0}return!1}function Lg(i,l,f,h){if(h={lane:2,revertLane:xh(),action:h,hasEagerState:!1,eagerState:null,next:null},Pc(i)){if(l)throw Error(r(479))}else l=ug(i,f,h,2),l!==null&&On(l,i,2)}function Pc(i){var l=i.alternate;return i===lt||l!==null&&l===lt}function oR(i,l){Wi=Cc=!0;var f=i.pending;f===null?l.next=l:(l.next=f.next,f.next=l),i.pending=l}function iR(i,l,f){if(f&4194176){var h=l.lanes;h&=i.pendingLanes,f|=h,l.lanes=f,Ar(i,f)}}var Wr={readContext:En,use:Dc,useCallback:Wt,useContext:Wt,useEffect:Wt,useImperativeHandle:Wt,useLayoutEffect:Wt,useInsertionEffect:Wt,useMemo:Wt,useReducer:Wt,useRef:Wt,useState:Wt,useDebugValue:Wt,useDeferredValue:Wt,useTransition:Wt,useSyncExternalStore:Wt,useId:Wt};Wr.useCacheRefresh=Wt,Wr.useMemoCache=Wt,Wr.useHostTransitionStatus=Wt,Wr.useFormState=Wt,Wr.useActionState=Wt,Wr.useOptimistic=Wt;var Wo={readContext:En,use:Dc,useCallback:function(i,l){return zn().memoizedState=[i,l===void 0?null:l],i},useContext:En,useEffect:q1,useImperativeHandle:function(i,l,f){f=f!=null?f.concat([i]):null,Lc(4194308,4,Y1.bind(null,l,i),f)},useLayoutEffect:function(i,l){return Lc(4194308,4,i,l)},useInsertionEffect:function(i,l){Lc(4,2,i,l)},useMemo:function(i,l){var f=zn();l=l===void 0?null:l;var h=i();if(Vo){ot(!0);try{i()}finally{ot(!1)}}return f.memoizedState=[h,l],h},useReducer:function(i,l,f){var h=zn();if(f!==void 0){var S=f(l);if(Vo){ot(!0);try{f(l)}finally{ot(!1)}}}else S=l;return h.memoizedState=h.baseState=S,i={pending:null,lanes:0,dispatch:null,lastRenderedReducer:i,lastRenderedState:S},h.queue=i,i=i.dispatch=Z$.bind(null,lt,i),[h.memoizedState,i]},useRef:function(i){var l=zn();return i={current:i},l.memoizedState=i},useState:function(i){i=Rg(i);var l=i.queue,f=aR.bind(null,lt,l);return l.dispatch=f,[i.memoizedState,f]},useDebugValue:Ng,useDeferredValue:function(i,l){var f=zn();return Og(f,i,l)},useTransition:function(){var i=Rg(!1);return i=J1.bind(null,lt,i.queue,!0,!1),zn().memoizedState=i,[!1,i]},useSyncExternalStore:function(i,l,f){var h=lt,S=zn();if(wt){if(f===void 0)throw Error(r(407));f=f()}else{if(f=l(),Lt===null)throw Error(r(349));vt&60||C1(h,l,f)}S.memoizedState=f;var T={value:f,getSnapshot:l};return S.queue=T,q1(O1.bind(null,h,T,i),[i]),h.flags|=2048,Ki(9,N1.bind(null,h,T,f,l),{destroy:void 0},null),f},useId:function(){var i=zn(),l=Lt.identifierPrefix;if(wt){var f=ba,h=ma;f=(h&~(1<<32-Et(h)-1)).toString(32)+f,l=":"+l+"R"+f,f=Nc++,0 title"))),mn(T,h,f),T[Sn]=i,an(T),h=T;break e;case"link":var z=I_("link","href",S).get(h+(f.href||""));if(z){for(var q=0;q<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof h.is=="string"?S.createElement("select",{is:h.is}):S.createElement("select"),h.multiple?i.multiple=!0:h.size&&(i.size=h.size);break;default:i=typeof h.is=="string"?S.createElement(f,{is:h.is}):S.createElement(f)}}i[Sn]=l,i[Pn]=h;e:for(S=l.child;S!==null;){if(S.tag===5||S.tag===6)i.appendChild(S.stateNode);else if(S.tag!==4&&S.tag!==27&&S.child!==null){S.child.return=S,S=S.child;continue}if(S===l)break e;for(;S.sibling===null;){if(S.return===null||S.return===l)break e;S=S.return}S.sibling.return=S.return,S=S.sibling}l.stateNode=i;e:switch(mn(i,f,h),f){case"button":case"input":case"select":case"textarea":i=!!h.autoFocus;break e;case"img":i=!0;break e;default:i=!1}i&&Aa(l)}}return zt(l),l.flags&=-16777217,null;case 6:if(i&&l.stateNode!=null)i.memoizedProps!==h&&Aa(l);else{if(typeof h!="string"&&l.stateNode===null)throw Error(r(166));if(i=De.current,hl(l)){if(i=l.stateNode,f=l.memoizedProps,h=null,S=Nn,S!==null)switch(S.tag){case 27:case 5:h=S.memoizedProps}i[Sn]=l,i=!!(i.nodeValue===f||h!==null&&h.suppressHydrationWarning===!0||E_(i.nodeValue,f)),i||Ho(l)}else i=td(i).createTextNode(h),i[Sn]=l,l.stateNode=i}return zt(l),null;case 13:if(h=l.memoizedState,i===null||i.memoizedState!==null&&i.memoizedState.dehydrated!==null){if(S=hl(l),h!==null&&h.dehydrated!==null){if(i===null){if(!S)throw Error(r(318));if(S=l.memoizedState,S=S!==null?S.dehydrated:null,!S)throw Error(r(317));S[Sn]=l}else ml(),!(l.flags&128)&&(l.memoizedState=null),l.flags|=4;zt(l),S=!1}else _r!==null&&(mh(_r),_r=null),S=!0;if(!S)return l.flags&256?(ya(l),l):(ya(l),null)}if(ya(l),l.flags&128)return l.lanes=f,l;if(f=h!==null,i=i!==null&&i.memoizedState!==null,f){h=l.child,S=null,h.alternate!==null&&h.alternate.memoizedState!==null&&h.alternate.memoizedState.cachePool!==null&&(S=h.alternate.memoizedState.cachePool.pool);var T=null;h.memoizedState!==null&&h.memoizedState.cachePool!==null&&(T=h.memoizedState.cachePool.pool),T!==S&&(h.flags|=2048)}return f!==i&&f&&(l.child.flags|=8192),$c(l,l.updateQueue),zt(l),null;case 4:return te(),i===null&&Rh(l.stateNode.containerInfo),zt(l),null;case 10:return wa(l.type),zt(l),null;case 19:if(he(en),S=l.memoizedState,S===null)return zt(l),null;if(h=(l.flags&128)!==0,T=S.rendering,T===null)if(h)Pl(S,!1);else{if(qt!==0||i!==null&&i.flags&128)for(i=l.child;i!==null;){if(T=Rc(i),T!==null){for(l.flags|=128,Pl(S,!1),i=T.updateQueue,l.updateQueue=i,$c(l,i),l.subtreeFlags=0,i=f,f=l.child;f!==null;)KR(f,i),f=f.sibling;return oe(en,en.current&1|2),l.child}i=i.sibling}S.tail!==null&&me()>qc&&(l.flags|=128,h=!0,Pl(S,!1),l.lanes=4194304)}else{if(!h)if(i=Rc(T),i!==null){if(l.flags|=128,h=!0,i=i.updateQueue,l.updateQueue=i,$c(l,i),Pl(S,!0),S.tail===null&&S.tailMode==="hidden"&&!T.alternate&&!wt)return zt(l),null}else 2*me()-S.renderingStartTime>qc&&f!==536870912&&(l.flags|=128,h=!0,Pl(S,!1),l.lanes=4194304);S.isBackwards?(T.sibling=l.child,l.child=T):(i=S.last,i!==null?i.sibling=T:l.child=T,S.last=T)}return S.tail!==null?(l=S.tail,S.rendering=l,S.tail=l.sibling,S.renderingStartTime=me(),l.sibling=null,i=en.current,oe(en,h?i&1|2:i&1),l):(zt(l),null);case 22:case 23:return ya(l),hg(),h=l.memoizedState!==null,i!==null?i.memoizedState!==null!==h&&(l.flags|=8192):h&&(l.flags|=8192),h?f&536870912&&!(l.flags&128)&&(zt(l),l.subtreeFlags&6&&(l.flags|=8192)):zt(l),f=l.updateQueue,f!==null&&$c(l,f.retryQueue),f=null,i!==null&&i.memoizedState!==null&&i.memoizedState.cachePool!==null&&(f=i.memoizedState.cachePool.pool),h=null,l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(h=l.memoizedState.cachePool.pool),h!==f&&(l.flags|=2048),i!==null&&he(qo),null;case 24:return f=null,i!==null&&(f=i.memoizedState.cache),l.memoizedState.cache!==f&&(l.flags|=2048),wa(tn),zt(l),null;case 25:return null}throw Error(r(156,l.tag))}function a6(i,l){switch(dg(l),l.tag){case 1:return i=l.flags,i&65536?(l.flags=i&-65537|128,l):null;case 3:return wa(tn),te(),i=l.flags,i&65536&&!(i&128)?(l.flags=i&-65537|128,l):null;case 26:case 27:case 5:return Te(l),null;case 13:if(ya(l),i=l.memoizedState,i!==null&&i.dehydrated!==null){if(l.alternate===null)throw Error(r(340));ml()}return i=l.flags,i&65536?(l.flags=i&-65537|128,l):null;case 19:return he(en),null;case 4:return te(),null;case 10:return wa(l.type),null;case 22:case 23:return ya(l),hg(),i!==null&&he(qo),i=l.flags,i&65536?(l.flags=i&-65537|128,l):null;case 24:return wa(tn),null;case 25:return null;default:return null}}function QR(i,l){switch(dg(l),l.tag){case 3:wa(tn),te();break;case 26:case 27:case 5:Te(l);break;case 4:te();break;case 13:ya(l);break;case 19:he(en);break;case 10:wa(l.type);break;case 22:case 23:ya(l),hg(),i!==null&&he(qo);break;case 24:wa(tn)}}var o6={getCacheForType:function(i){var l=En(tn),f=l.data.get(i);return f===void 0&&(f=i(),l.data.set(i,f)),f}},i6=typeof WeakMap=="function"?WeakMap:Map,Bt=0,Lt=null,ft=null,vt=0,Mt=0,Xn=null,Ra=!1,Ji=!1,uh=!1,_a=0,qt=0,lo=0,ei=0,ch=0,mr=0,es=0,Fl=null,Yr=null,dh=!1,fh=0,qc=1/0,Vc=null,uo=null,Wc=!1,ti=null,zl=0,ph=0,gh=null,Bl=0,hh=null;function Zn(){if(Bt&2&&vt!==0)return vt&-vt;if(I.T!==null){var i=qi;return i!==0?i:xh()}return vA()}function JR(){mr===0&&(mr=!(vt&536870912)||wt?Nt():536870912);var i=fr.current;return i!==null&&(i.flags|=32),mr}function On(i,l,f){(i===Lt&&Mt===2||i.cancelPendingCommit!==null)&&(ts(i,0),Ca(i,vt,mr,!1)),Ln(i,f),(!(Bt&2)||i!==Lt)&&(i===Lt&&(!(Bt&2)&&(ei|=f),qt===4&&Ca(i,vt,mr,!1)),Kr(i))}function e_(i,l,f){if(Bt&6)throw Error(r(327));var h=!f&&(l&60)===0&&(l&i.expiredLanes)===0||Xe(i,l),S=h?u6(i,l):vh(i,l,!0),T=h;do{if(S===0){Ji&&!h&&Ca(i,l,0,!1);break}else if(S===6)Ca(i,l,0,!Ra);else{if(f=i.current.alternate,T&&!s6(f)){S=vh(i,l,!1),T=!1;continue}if(S===2){if(T=l,i.errorRecoveryDisabledLanes&T)var z=0;else z=i.pendingLanes&-536870913,z=z!==0?z:z&536870912?536870912:0;if(z!==0){l=z;e:{var q=i;S=Fl;var Z=q.current.memoizedState.isDehydrated;if(Z&&(ts(q,z).flags|=256),z=vh(q,z,!1),z!==2){if(uh&&!Z){q.errorRecoveryDisabledLanes|=T,ei|=T,S=4;break e}T=Yr,Yr=S,T!==null&&mh(T)}S=z}if(T=!1,S!==2)continue}}if(S===1){ts(i,0),Ca(i,l,0,!0);break}e:{switch(h=i,S){case 0:case 1:throw Error(r(345));case 4:if((l&4194176)===l){Ca(h,l,mr,!Ra);break e}break;case 2:Yr=null;break;case 3:case 5:break;default:throw Error(r(329))}if(h.finishedWork=f,h.finishedLanes=l,(l&62914560)===l&&(T=fh+300-me(),10f?32:f,I.T=null,ti===null)var T=!1;else{f=gh,gh=null;var z=ti,q=zl;if(ti=null,zl=0,Bt&6)throw Error(r(331));var Z=Bt;if(Bt|=4,WR(z.current),$R(z,z.current,q,f),Bt=Z,Ul(0,!1),et&&typeof et.onPostCommitFiberRoot=="function")try{et.onPostCommitFiberRoot(bt,z)}catch{}T=!0}return T}finally{K.p=S,I.T=h,u_(i,l)}}return!1}function c_(i,l,f){l=ur(f,l),l=Fg(i.stateNode,l,2),i=ro(i,l,2),i!==null&&(Ln(i,2),Kr(i))}function Ot(i,l,f){if(i.tag===3)c_(i,i,f);else for(;l!==null;){if(l.tag===3){c_(l,i,f);break}else if(l.tag===1){var h=l.stateNode;if(typeof l.type.getDerivedStateFromError=="function"||typeof h.componentDidCatch=="function"&&(uo===null||!uo.has(h))){i=ur(f,i),f=pR(2),h=ro(l,f,2),h!==null&&(gR(f,h,l,i),Ln(h,2),Kr(h));break}}l=l.return}}function Sh(i,l,f){var h=i.pingCache;if(h===null){h=i.pingCache=new i6;var S=new Set;h.set(l,S)}else S=h.get(l),S===void 0&&(S=new Set,h.set(l,S));S.has(f)||(uh=!0,S.add(f),i=f6.bind(null,i,l,f),l.then(i,i))}function f6(i,l,f){var h=i.pingCache;h!==null&&h.delete(l),i.pingedLanes|=i.suspendedLanes&f,i.warmLanes&=~f,Lt===i&&(vt&f)===f&&(qt===4||qt===3&&(vt&62914560)===vt&&300>me()-fh?!(Bt&2)&&ts(i,0):ch|=f,es===vt&&(es=0)),Kr(i)}function d_(i,l){l===0&&(l=In()),i=Ka(i,l),i!==null&&(Ln(i,l),Kr(i))}function p6(i){var l=i.memoizedState,f=0;l!==null&&(f=l.retryLane),d_(i,f)}function g6(i,l){var f=0;switch(i.tag){case 13:var h=i.stateNode,S=i.memoizedState;S!==null&&(f=S.retryLane);break;case 19:h=i.stateNode;break;case 22:h=i.stateNode._retryCache;break;default:throw Error(r(314))}h!==null&&h.delete(l),d_(i,f)}function h6(i,l){return xe(i,l)}var Xc=null,as=null,Eh=!1,Zc=!1,wh=!1,ni=0;function Kr(i){i!==as&&i.next===null&&(as===null?Xc=as=i:as=as.next=i),Zc=!0,Eh||(Eh=!0,b6(m6))}function Ul(i,l){if(!wh&&Zc){wh=!0;do for(var f=!1,h=Xc;h!==null;){if(i!==0){var S=h.pendingLanes;if(S===0)var T=0;else{var z=h.suspendedLanes,q=h.pingedLanes;T=(1<<31-Et(42|i)+1)-1,T&=S&~(z&~q),T=T&201326677?T&201326677|1:T?T|2:0}T!==0&&(f=!0,g_(h,T))}else T=vt,T=fa(h,h===Lt?T:0),!(T&3)||Xe(h,T)||(f=!0,g_(h,T));h=h.next}while(f);wh=!1}}function m6(){Zc=Eh=!1;var i=0;ni!==0&&(T6()&&(i=ni),ni=0);for(var l=me(),f=null,h=Xc;h!==null;){var S=h.next,T=f_(h,l);T===0?(h.next=null,f===null?Xc=S:f.next=S,S===null&&(as=f)):(f=h,(i!==0||T&3)&&(Zc=!0)),h=S}Ul(i)}function f_(i,l){for(var f=i.suspendedLanes,h=i.pingedLanes,S=i.expirationTimes,T=i.pendingLanes&-62914561;0"u"?null:document;function C_(i,l,f){var h=is;if(h&&typeof l=="string"&&l){var S=sr(l);S='link[rel="'+i+'"][href="'+S+'"]',typeof f=="string"&&(S+='[crossorigin="'+f+'"]'),__.has(S)||(__.add(S),i={rel:i,crossOrigin:f,href:l},h.querySelector(S)===null&&(l=h.createElement("link"),mn(l,"link",i),an(l),h.head.appendChild(l)))}}function I6(i){Na.D(i),C_("dns-prefetch",i,null)}function L6(i,l){Na.C(i,l),C_("preconnect",i,l)}function M6(i,l,f){Na.L(i,l,f);var h=is;if(h&&i&&l){var S='link[rel="preload"][as="'+sr(l)+'"]';l==="image"&&f&&f.imageSrcSet?(S+='[imagesrcset="'+sr(f.imageSrcSet)+'"]',typeof f.imageSizes=="string"&&(S+='[imagesizes="'+sr(f.imageSizes)+'"]')):S+='[href="'+sr(i)+'"]';var T=S;switch(l){case"style":T=ss(i);break;case"script":T=ls(i)}br.has(T)||(i=D({rel:"preload",href:l==="image"&&f&&f.imageSrcSet?void 0:i,as:l},f),br.set(T,i),h.querySelector(S)!==null||l==="style"&&h.querySelector(Hl(T))||l==="script"&&h.querySelector($l(T))||(l=h.createElement("link"),mn(l,"link",i),an(l),h.head.appendChild(l)))}}function P6(i,l){Na.m(i,l);var f=is;if(f&&i){var h=l&&typeof l.as=="string"?l.as:"script",S='link[rel="modulepreload"][as="'+sr(h)+'"][href="'+sr(i)+'"]',T=S;switch(h){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":T=ls(i)}if(!br.has(T)&&(i=D({rel:"modulepreload",href:i},l),br.set(T,i),f.querySelector(S)===null)){switch(h){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(f.querySelector($l(T)))return}h=f.createElement("link"),mn(h,"link",i),an(h),f.head.appendChild(h)}}}function F6(i,l,f){Na.S(i,l,f);var h=is;if(h&&i){var S=Ni(h).hoistableStyles,T=ss(i);l=l||"default";var z=S.get(T);if(!z){var q={loading:0,preload:null};if(z=h.querySelector(Hl(T)))q.loading=5;else{i=D({rel:"stylesheet",href:i,"data-precedence":l},f),(f=br.get(T))&&Ph(i,f);var Z=z=h.createElement("link");an(Z),mn(Z,"link",i),Z._p=new Promise(function(ae,ye){Z.onload=ae,Z.onerror=ye}),Z.addEventListener("load",function(){q.loading|=1}),Z.addEventListener("error",function(){q.loading|=2}),q.loading|=4,rd(z,l,h)}z={type:"stylesheet",instance:z,count:1,state:q},S.set(T,z)}}}function z6(i,l){Na.X(i,l);var f=is;if(f&&i){var h=Ni(f).hoistableScripts,S=ls(i),T=h.get(S);T||(T=f.querySelector($l(S)),T||(i=D({src:i,async:!0},l),(l=br.get(S))&&Fh(i,l),T=f.createElement("script"),an(T),mn(T,"link",i),f.head.appendChild(T)),T={type:"script",instance:T,count:1,state:null},h.set(S,T))}}function B6(i,l){Na.M(i,l);var f=is;if(f&&i){var h=Ni(f).hoistableScripts,S=ls(i),T=h.get(S);T||(T=f.querySelector($l(S)),T||(i=D({src:i,async:!0,type:"module"},l),(l=br.get(S))&&Fh(i,l),T=f.createElement("script"),an(T),mn(T,"link",i),f.head.appendChild(T)),T={type:"script",instance:T,count:1,state:null},h.set(S,T))}}function N_(i,l,f,h){var S=(S=De.current)?nd(S):null;if(!S)throw Error(r(446));switch(i){case"meta":case"title":return null;case"style":return typeof f.precedence=="string"&&typeof f.href=="string"?(l=ss(f.href),f=Ni(S).hoistableStyles,h=f.get(l),h||(h={type:"style",instance:null,count:0,state:null},f.set(l,h)),h):{type:"void",instance:null,count:0,state:null};case"link":if(f.rel==="stylesheet"&&typeof f.href=="string"&&typeof f.precedence=="string"){i=ss(f.href);var T=Ni(S).hoistableStyles,z=T.get(i);if(z||(S=S.ownerDocument||S,z={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},T.set(i,z),(T=S.querySelector(Hl(i)))&&!T._p&&(z.instance=T,z.state.loading=5),br.has(i)||(f={rel:"preload",as:"style",href:f.href,crossOrigin:f.crossOrigin,integrity:f.integrity,media:f.media,hrefLang:f.hrefLang,referrerPolicy:f.referrerPolicy},br.set(i,f),T||U6(S,i,f,z.state))),l&&h===null)throw Error(r(528,""));return z}if(l&&h!==null)throw Error(r(529,""));return null;case"script":return l=f.async,f=f.src,typeof f=="string"&&l&&typeof l!="function"&&typeof l!="symbol"?(l=ls(f),f=Ni(S).hoistableScripts,h=f.get(l),h||(h={type:"script",instance:null,count:0,state:null},f.set(l,h)),h):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,i))}}function ss(i){return'href="'+sr(i)+'"'}function Hl(i){return'link[rel="stylesheet"]['+i+"]"}function O_(i){return D({},i,{"data-precedence":i.precedence,precedence:null})}function U6(i,l,f,h){i.querySelector('link[rel="preload"][as="style"]['+l+"]")?h.loading=1:(l=i.createElement("link"),h.preload=l,l.addEventListener("load",function(){return h.loading|=1}),l.addEventListener("error",function(){return h.loading|=2}),mn(l,"link",f),an(l),i.head.appendChild(l))}function ls(i){return'[src="'+sr(i)+'"]'}function $l(i){return"script[async]"+i}function D_(i,l,f){if(l.count++,l.instance===null)switch(l.type){case"style":var h=i.querySelector('style[data-href~="'+sr(f.href)+'"]');if(h)return l.instance=h,an(h),h;var S=D({},f,{"data-href":f.href,"data-precedence":f.precedence,href:null,precedence:null});return h=(i.ownerDocument||i).createElement("style"),an(h),mn(h,"style",S),rd(h,f.precedence,i),l.instance=h;case"stylesheet":S=ss(f.href);var T=i.querySelector(Hl(S));if(T)return l.state.loading|=4,l.instance=T,an(T),T;h=O_(f),(S=br.get(S))&&Ph(h,S),T=(i.ownerDocument||i).createElement("link"),an(T);var z=T;return z._p=new Promise(function(q,Z){z.onload=q,z.onerror=Z}),mn(T,"link",h),l.state.loading|=4,rd(T,f.precedence,i),l.instance=T;case"script":return T=ls(f.src),(S=i.querySelector($l(T)))?(l.instance=S,an(S),S):(h=f,(S=br.get(T))&&(h=D({},f),Fh(h,S)),i=i.ownerDocument||i,S=i.createElement("script"),an(S),mn(S,"link",h),i.head.appendChild(S),l.instance=S);case"void":return null;default:throw Error(r(443,l.type))}else l.type==="stylesheet"&&!(l.state.loading&4)&&(h=l.instance,l.state.loading|=4,rd(h,f.precedence,i));return l.instance}function rd(i,l,f){for(var h=f.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),S=h.length?h[h.length-1]:null,T=S,z=0;z title"):null)}function j6(i,l,f){if(f===1||l.itemProp!=null)return!1;switch(i){case"meta":case"title":return!0;case"style":if(typeof l.precedence!="string"||typeof l.href!="string"||l.href==="")break;return!0;case"link":if(typeof l.rel!="string"||typeof l.href!="string"||l.href===""||l.onLoad||l.onError)break;switch(l.rel){case"stylesheet":return i=l.disabled,typeof l.precedence=="string"&&i==null;default:return!0}case"script":if(l.async&&typeof l.async!="function"&&typeof l.async!="symbol"&&!l.onLoad&&!l.onError&&l.src&&typeof l.src=="string")return!0}return!1}function M_(i){return!(i.type==="stylesheet"&&!(i.state.loading&3))}var ql=null;function G6(){}function H6(i,l,f){if(ql===null)throw Error(r(475));var h=ql;if(l.type==="stylesheet"&&(typeof f.media!="string"||matchMedia(f.media).matches!==!1)&&!(l.state.loading&4)){if(l.instance===null){var S=ss(f.href),T=i.querySelector(Hl(S));if(T){i=T._p,i!==null&&typeof i=="object"&&typeof i.then=="function"&&(h.count++,h=od.bind(h),i.then(h,h)),l.state.loading|=4,l.instance=T,an(T);return}T=i.ownerDocument||i,f=O_(f),(S=br.get(S))&&Ph(f,S),T=T.createElement("link"),an(T);var z=T;z._p=new Promise(function(q,Z){z.onload=q,z.onerror=Z}),mn(T,"link",f),l.instance=T}h.stylesheets===null&&(h.stylesheets=new Map),h.stylesheets.set(l,i),(i=l.state.preload)&&!(l.state.loading&3)&&(h.count++,l=od.bind(h),i.addEventListener("load",l),i.addEventListener("error",l))}}function $6(){if(ql===null)throw Error(r(475));var i=ql;return i.stylesheets&&i.count===0&&zh(i,i.stylesheets),0"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),Wh.exports=dq(),Wh.exports}var pq=fq(),Jl={},rC;function gq(){if(rC)return Jl;rC=1,Object.defineProperty(Jl,"__esModule",{value:!0}),Jl.parse=s,Jl.serialize=d;const e=/^[\u0021-\u003A\u003C\u003E-\u007E]+$/,t=/^[\u0021-\u003A\u003C-\u007E]*$/,n=/^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i,r=/^[\u0020-\u003A\u003D-\u007E]*$/,a=Object.prototype.toString,o=(()=>{const m=function(){};return m.prototype=Object.create(null),m})();function s(m,b){const y=new o,v=m.length;if(v<2)return y;const x=(b==null?void 0:b.decode)||p;let R=0;do{const k=m.indexOf("=",R);if(k===-1)break;const A=m.indexOf(";",R),O=A===-1?v:A;if(k>O){R=m.lastIndexOf(";",k-1)+1;continue}const N=u(m,R,k),_=c(m,k,N),C=m.slice(N,_);if(y[C]===void 0){let L=u(m,k+1,O),I=c(m,O,L);const D=x(m.slice(L,I));y[C]=D}R=O+1}while(Ry;){const v=m.charCodeAt(--b);if(v!==32&&v!==9)return b+1}return y}function d(m,b,y){const v=(y==null?void 0:y.encode)||encodeURIComponent;if(!e.test(m))throw new TypeError(`argument name is invalid: ${m}`);const x=v(b);if(!t.test(x))throw new TypeError(`argument val is invalid: ${b}`);let R=m+"="+x;if(!y)return R;if(y.maxAge!==void 0){if(!Number.isInteger(y.maxAge))throw new TypeError(`option maxAge is invalid: ${y.maxAge}`);R+="; Max-Age="+y.maxAge}if(y.domain){if(!n.test(y.domain))throw new TypeError(`option domain is invalid: ${y.domain}`);R+="; Domain="+y.domain}if(y.path){if(!r.test(y.path))throw new TypeError(`option path is invalid: ${y.path}`);R+="; Path="+y.path}if(y.expires){if(!g(y.expires)||!Number.isFinite(y.expires.valueOf()))throw new TypeError(`option expires is invalid: ${y.expires}`);R+="; Expires="+y.expires.toUTCString()}if(y.httpOnly&&(R+="; HttpOnly"),y.secure&&(R+="; Secure"),y.partitioned&&(R+="; Partitioned"),y.priority)switch(typeof y.priority=="string"?y.priority.toLowerCase():void 0){case"low":R+="; Priority=Low";break;case"medium":R+="; Priority=Medium";break;case"high":R+="; Priority=High";break;default:throw new TypeError(`option priority is invalid: ${y.priority}`)}if(y.sameSite)switch(typeof y.sameSite=="string"?y.sameSite.toLowerCase():y.sameSite){case!0:case"strict":R+="; SameSite=Strict";break;case"lax":R+="; SameSite=Lax";break;case"none":R+="; SameSite=None";break;default:throw new TypeError(`option sameSite is invalid: ${y.sameSite}`)}return R}function p(m){if(m.indexOf("%")===-1)return m;try{return decodeURIComponent(m)}catch{return m}}function g(m){return a.call(m)==="[object Date]"}return Jl}gq();/** +`+f.stack}}function F(i){var l=i,f=i;if(i.alternate)for(;l.return;)l=l.return;else{i=l;do l=i,l.flags&4098&&(f=l.return),i=l.return;while(i)}return l.tag===3?f:null}function Y(i){if(i.tag===13){var l=i.memoizedState;if(l===null&&(i=i.alternate,i!==null&&(l=i.memoizedState)),l!==null)return l.dehydrated}return null}function M(i){if(F(i)!==i)throw Error(r(188))}function V(i){var l=i.alternate;if(!l){if(l=F(i),l===null)throw Error(r(188));return l!==i?null:i}for(var f=i,h=l;;){var S=f.return;if(S===null)break;var T=S.alternate;if(T===null){if(h=S.return,h!==null){f=h;continue}break}if(S.child===T.child){for(T=S.child;T;){if(T===f)return M(S),i;if(T===h)return M(S),l;T=T.sibling}throw Error(r(188))}if(f.return!==h.return)f=S,h=T;else{for(var z=!1,q=S.child;q;){if(q===f){z=!0,f=S,h=T;break}if(q===h){z=!0,h=S,f=T;break}q=q.sibling}if(!z){for(q=T.child;q;){if(q===f){z=!0,f=T,h=S;break}if(q===h){z=!0,h=T,f=S;break}q=q.sibling}if(!z)throw Error(r(189))}}if(f.alternate!==h)throw Error(r(190))}if(f.tag!==3)throw Error(r(188));return f.stateNode.current===f?i:l}function U(i){var l=i.tag;if(l===5||l===26||l===27||l===6)return i;for(i=i.child;i!==null;){if(l=U(i),l!==null)return l;i=i.sibling}return null}var P=Array.isArray,K=n.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ee={pending:!1,data:null,method:null,action:null},le=[],X=-1;function J(i){return{current:i}}function he(i){0>X||(i.current=le[X],le[X]=null,X--)}function oe(i,l){X++,le[X]=i.current,i.current=l}var Se=J(null),we=J(null),Ie=J(null),Ce=J(null);function Ee(i,l){switch(oe(Ie,l),oe(we,i),oe(Se,null),i=l.nodeType,i){case 9:case 11:l=(l=l.documentElement)&&(l=l.namespaceURI)?TC(l):0;break;default:if(i=i===8?l.parentNode:l,l=i.tagName,i=i.namespaceURI)i=TC(i),l=AC(i,l);else switch(l){case"svg":l=1;break;case"math":l=2;break;default:l=0}}he(Se),oe(Se,l)}function te(){he(Se),he(we),he(Ie)}function fe(i){i.memoizedState!==null&&oe(Ce,i);var l=Se.current,f=AC(l,i.type);l!==f&&(oe(we,i),oe(Se,f))}function Te(i){we.current===i&&(he(Se),he(we)),Ce.current===i&&(he(Ce),Vl._currentValue=ee)}var be=Object.prototype.hasOwnProperty,xe=e.unstable_scheduleCallback,se=e.unstable_cancelCallback,ze=e.unstable_shouldYield,Be=e.unstable_requestPaint,me=e.unstable_now,Ne=e.unstable_getCurrentPriorityLevel,ne=e.unstable_ImmediatePriority,ce=e.unstable_UserBlockingPriority,_e=e.unstable_NormalPriority,Fe=e.unstable_LowPriority,We=e.unstable_IdlePriority,St=e.log,Tt=e.unstable_setDisableYieldValue,bt=null,et=null;function At(i){if(et&&typeof et.onCommitFiberRoot=="function")try{et.onCommitFiberRoot(bt,i,void 0,(i.current.flags&128)===128)}catch{}}function it(i){if(typeof St=="function"&&Tt(i),et&&typeof et.setStrictMode=="function")try{et.setStrictMode(bt,i)}catch{}}var Et=Math.clz32?Math.clz32:zt,Ht=Math.log,pn=Math.LN2;function zt(i){return i>>>=0,i===0?32:31-(Ht(i)/pn|0)|0}var ir=128,qr=4194304;function Jt(i){var l=i&42;if(l!==0)return l;switch(i&-i){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return i&4194176;case 4194304:case 8388608:case 16777216:case 33554432:return i&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return i}}function pa(i,l){var f=i.pendingLanes;if(f===0)return 0;var h=0,S=i.suspendedLanes,T=i.pingedLanes,z=i.warmLanes;i=i.finishedLanes!==0;var q=f&134217727;return q!==0?(f=q&~S,f!==0?h=Jt(f):(T&=q,T!==0?h=Jt(T):i||(z=q&~z,z!==0&&(h=Jt(z))))):(q=f&~S,q!==0?h=Jt(q):T!==0?h=Jt(T):i||(z=f&~z,z!==0&&(h=Jt(z)))),h===0?0:l!==0&&l!==h&&!(l&S)&&(S=h&-h,z=l&-l,S>=z||S===32&&(z&4194176)!==0)?l:h}function Xe(i,l){return(i.pendingLanes&~(i.suspendedLanes&~i.pingedLanes)&l)===0}function yt(i,l){switch(i){case 1:case 2:case 4:case 8:return l+250;case 16:case 32:case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return l+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Nt(){var i=ir;return ir<<=1,!(ir&4194176)&&(ir=128),i}function Ln(){var i=qr;return qr<<=1,!(qr&62914560)&&(qr=4194304),i}function _n(i){for(var l=[],f=0;31>f;f++)l.push(i);return l}function Mn(i,l){i.pendingLanes|=l,l!==268435456&&(i.suspendedLanes=0,i.pingedLanes=0,i.warmLanes=0)}function ga(i,l,f,h,S,T){var z=i.pendingLanes;i.pendingLanes=f,i.suspendedLanes=0,i.pingedLanes=0,i.warmLanes=0,i.expiredLanes&=f,i.entangledLanes&=f,i.errorRecoveryDisabledLanes&=f,i.shellSuspendCounter=0;var q=i.entanglements,Z=i.expirationTimes,ae=i.hiddenUpdates;for(f=z&~f;0"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),KH=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$"),AA={},RA={};function XH(i){return be.call(RA,i)?!0:be.call(AA,i)?!1:KH.test(i)?RA[i]=!0:(AA[i]=!0,!1)}function lc(i,l,f){if(XH(l))if(f===null)i.removeAttribute(l);else{switch(typeof f){case"undefined":case"function":case"symbol":i.removeAttribute(l);return;case"boolean":var h=l.toLowerCase().slice(0,5);if(h!=="data-"&&h!=="aria-"){i.removeAttribute(l);return}}i.setAttribute(l,""+f)}}function uc(i,l,f){if(f===null)i.removeAttribute(l);else{switch(typeof f){case"undefined":case"function":case"symbol":case"boolean":i.removeAttribute(l);return}i.setAttribute(l,""+f)}}function ma(i,l,f,h){if(h===null)i.removeAttribute(f);else{switch(typeof h){case"undefined":case"function":case"symbol":case"boolean":i.removeAttribute(f);return}i.setAttributeNS(l,f,""+h)}}function sr(i){switch(typeof i){case"bigint":case"boolean":case"number":case"string":case"undefined":return i;case"object":return i;default:return""}}function CA(i){var l=i.type;return(i=i.nodeName)&&i.toLowerCase()==="input"&&(l==="checkbox"||l==="radio")}function ZH(i){var l=CA(i)?"checked":"value",f=Object.getOwnPropertyDescriptor(i.constructor.prototype,l),h=""+i[l];if(!i.hasOwnProperty(l)&&typeof f<"u"&&typeof f.get=="function"&&typeof f.set=="function"){var S=f.get,T=f.set;return Object.defineProperty(i,l,{configurable:!0,get:function(){return S.call(this)},set:function(z){h=""+z,T.call(this,z)}}),Object.defineProperty(i,l,{enumerable:f.enumerable}),{getValue:function(){return h},setValue:function(z){h=""+z},stopTracking:function(){i._valueTracker=null,delete i[l]}}}}function cc(i){i._valueTracker||(i._valueTracker=ZH(i))}function _A(i){if(!i)return!1;var l=i._valueTracker;if(!l)return!0;var f=l.getValue(),h="";return i&&(h=CA(i)?i.checked?"true":"false":i.value),i=h,i!==f?(l.setValue(i),!0):!1}function dc(i){if(i=i||(typeof document<"u"?document:void 0),typeof i>"u")return null;try{return i.activeElement||i.body}catch{return i.body}}var QH=/[\n"\\]/g;function lr(i){return i.replace(QH,function(l){return"\\"+l.charCodeAt(0).toString(16)+" "})}function qp(i,l,f,h,S,T,z,q){i.name="",z!=null&&typeof z!="function"&&typeof z!="symbol"&&typeof z!="boolean"?i.type=z:i.removeAttribute("type"),l!=null?z==="number"?(l===0&&i.value===""||i.value!=l)&&(i.value=""+sr(l)):i.value!==""+sr(l)&&(i.value=""+sr(l)):z!=="submit"&&z!=="reset"||i.removeAttribute("value"),l!=null?Vp(i,z,sr(l)):f!=null?Vp(i,z,sr(f)):h!=null&&i.removeAttribute("value"),S==null&&T!=null&&(i.defaultChecked=!!T),S!=null&&(i.checked=S&&typeof S!="function"&&typeof S!="symbol"),q!=null&&typeof q!="function"&&typeof q!="symbol"&&typeof q!="boolean"?i.name=""+sr(q):i.removeAttribute("name")}function NA(i,l,f,h,S,T,z,q){if(T!=null&&typeof T!="function"&&typeof T!="symbol"&&typeof T!="boolean"&&(i.type=T),l!=null||f!=null){if(!(T!=="submit"&&T!=="reset"||l!=null))return;f=f!=null?""+sr(f):"",l=l!=null?""+sr(l):f,q||l===i.value||(i.value=l),i.defaultValue=l}h=h??S,h=typeof h!="function"&&typeof h!="symbol"&&!!h,i.checked=q?i.checked:!!h,i.defaultChecked=!!h,z!=null&&typeof z!="function"&&typeof z!="symbol"&&typeof z!="boolean"&&(i.name=z)}function Vp(i,l,f){l==="number"&&dc(i.ownerDocument)===i||i.defaultValue===""+f||(i.defaultValue=""+f)}function Ii(i,l,f,h){if(i=i.options,l){l={};for(var S=0;S=cl),HA=" ",$A=!1;function qA(i,l){switch(i){case"keyup":return A$.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function VA(i){return i=i.detail,typeof i=="object"&&"data"in i?i.data:null}var Pi=!1;function C$(i,l){switch(i){case"compositionend":return VA(l);case"keypress":return l.which!==32?null:($A=!0,HA);case"textInput":return i=l.data,i===HA&&$A?null:i;default:return null}}function _$(i,l){if(Pi)return i==="compositionend"||!rg&&qA(i,l)?(i=FA(),pc=Qp=Ya=null,Pi=!1,i):null;switch(i){case"paste":return null;case"keypress":if(!(l.ctrlKey||l.altKey||l.metaKey)||l.ctrlKey&&l.altKey){if(l.char&&1=l)return{node:f,offset:l-i};i=h}e:{for(;f;){if(f.nextSibling){f=f.nextSibling;break e}f=f.parentNode}f=void 0}f=e1(f)}}function n1(i,l){return i&&l?i===l?!0:i&&i.nodeType===3?!1:l&&l.nodeType===3?n1(i,l.parentNode):"contains"in i?i.contains(l):i.compareDocumentPosition?!!(i.compareDocumentPosition(l)&16):!1:!1}function r1(i){i=i!=null&&i.ownerDocument!=null&&i.ownerDocument.defaultView!=null?i.ownerDocument.defaultView:window;for(var l=dc(i.document);l instanceof i.HTMLIFrameElement;){try{var f=typeof l.contentWindow.location.href=="string"}catch{f=!1}if(f)i=l.contentWindow;else break;l=dc(i.document)}return l}function ig(i){var l=i&&i.nodeName&&i.nodeName.toLowerCase();return l&&(l==="input"&&(i.type==="text"||i.type==="search"||i.type==="tel"||i.type==="url"||i.type==="password")||l==="textarea"||i.contentEditable==="true")}function F$(i,l){var f=r1(l);l=i.focusedElem;var h=i.selectionRange;if(f!==l&&l&&l.ownerDocument&&n1(l.ownerDocument.documentElement,l)){if(h!==null&&ig(l)){if(i=h.start,f=h.end,f===void 0&&(f=i),"selectionStart"in l)l.selectionStart=i,l.selectionEnd=Math.min(f,l.value.length);else if(f=(i=l.ownerDocument||document)&&i.defaultView||window,f.getSelection){f=f.getSelection();var S=l.textContent.length,T=Math.min(h.start,S);h=h.end===void 0?T:Math.min(h.end,S),!f.extend&&T>h&&(S=h,h=T,T=S),S=t1(l,T);var z=t1(l,h);S&&z&&(f.rangeCount!==1||f.anchorNode!==S.node||f.anchorOffset!==S.offset||f.focusNode!==z.node||f.focusOffset!==z.offset)&&(i=i.createRange(),i.setStart(S.node,S.offset),f.removeAllRanges(),T>h?(f.addRange(i),f.extend(z.node,z.offset)):(i.setEnd(z.node,z.offset),f.addRange(i)))}}for(i=[],f=l;f=f.parentNode;)f.nodeType===1&&i.push({element:f,left:f.scrollLeft,top:f.scrollTop});for(typeof l.focus=="function"&&l.focus(),l=0;l=document.documentMode,Fi=null,sg=null,gl=null,lg=!1;function a1(i,l,f){var h=f.window===f?f.document:f.nodeType===9?f:f.ownerDocument;lg||Fi==null||Fi!==dc(h)||(h=Fi,"selectionStart"in h&&ig(h)?h={start:h.selectionStart,end:h.selectionEnd}:(h=(h.ownerDocument&&h.ownerDocument.defaultView||window).getSelection(),h={anchorNode:h.anchorNode,anchorOffset:h.anchorOffset,focusNode:h.focusNode,focusOffset:h.focusOffset}),gl&&pl(gl,h)||(gl=h,h=ed(sg,"onSelect"),0>=z,S-=z,ba=1<<32-Et(l)+S|f<Qe?(ln=Ye,Ye=null):ln=Ye.sibling;var xt=de(ie,Ye,ue[Qe],ke);if(xt===null){Ye===null&&(Ye=ln);break}i&&Ye&&xt.alternate===null&&l(ie,Ye),re=T(xt,re,Qe),ct===null?je=xt:ct.sibling=xt,ct=xt,Ye=ln}if(Qe===ue.length)return f(ie,Ye),wt&&Go(ie,Qe),je;if(Ye===null){for(;QeQe?(ln=Ye,Ye=null):ln=Ye.sibling;var ho=de(ie,Ye,xt.value,ke);if(ho===null){Ye===null&&(Ye=ln);break}i&&Ye&&ho.alternate===null&&l(ie,Ye),re=T(ho,re,Qe),ct===null?je=ho:ct.sibling=ho,ct=ho,Ye=ln}if(xt.done)return f(ie,Ye),wt&&Go(ie,Qe),je;if(Ye===null){for(;!xt.done;Qe++,xt=ue.next())xt=Ae(ie,xt.value,ke),xt!==null&&(re=T(xt,re,Qe),ct===null?je=xt:ct.sibling=xt,ct=xt);return wt&&Go(ie,Qe),je}for(Ye=h(Ye);!xt.done;Qe++,xt=ue.next())xt=ge(Ye,ie,Qe,xt.value,ke),xt!==null&&(i&&xt.alternate!==null&&Ye.delete(xt.key===null?Qe:xt.key),re=T(xt,re,Qe),ct===null?je=xt:ct.sibling=xt,ct=xt);return i&&Ye.forEach(function(tq){return l(ie,tq)}),wt&&Go(ie,Qe),je}function Vt(ie,re,ue,ke){if(typeof ue=="object"&&ue!==null&&ue.type===c&&ue.key===null&&(ue=ue.props.children),typeof ue=="object"&&ue!==null){switch(ue.$$typeof){case s:e:{for(var je=ue.key;re!==null;){if(re.key===je){if(je=ue.type,je===c){if(re.tag===7){f(ie,re.sibling),ke=S(re,ue.props.children),ke.return=ie,ie=ke;break e}}else if(re.elementType===je||typeof je=="object"&&je!==null&&je.$$typeof===k&&E1(je)===re.type){f(ie,re.sibling),ke=S(re,ue.props),El(ke,ue),ke.return=ie,ie=ke;break e}f(ie,re);break}else l(ie,re);re=re.sibling}ue.type===c?(ke=Jo(ue.props.children,ie.mode,ke,ue.key),ke.return=ie,ie=ke):(ke=$c(ue.type,ue.key,ue.props,null,ie.mode,ke),El(ke,ue),ke.return=ie,ie=ke)}return z(ie);case u:e:{for(je=ue.key;re!==null;){if(re.key===je)if(re.tag===4&&re.stateNode.containerInfo===ue.containerInfo&&re.stateNode.implementation===ue.implementation){f(ie,re.sibling),ke=S(re,ue.children||[]),ke.return=ie,ie=ke;break e}else{f(ie,re);break}else l(ie,re);re=re.sibling}ke=ch(ue,ie.mode,ke),ke.return=ie,ie=ke}return z(ie);case k:return je=ue._init,ue=je(ue._payload),Vt(ie,re,ue,ke)}if(P(ue))return qe(ie,re,ue,ke);if(C(ue)){if(je=C(ue),typeof je!="function")throw Error(r(150));return ue=je.call(ue),nt(ie,re,ue,ke)}if(typeof ue.then=="function")return Vt(ie,re,Tc(ue),ke);if(ue.$$typeof===b)return Vt(ie,re,jc(ie,ue),ke);Ac(ie,ue)}return typeof ue=="string"&&ue!==""||typeof ue=="number"||typeof ue=="bigint"?(ue=""+ue,re!==null&&re.tag===6?(f(ie,re.sibling),ke=S(re,ue),ke.return=ie,ie=ke):(f(ie,re),ke=uh(ue,ie.mode,ke),ke.return=ie,ie=ke),z(ie)):f(ie,re)}return function(ie,re,ue,ke){try{Sl=0;var je=Vt(ie,re,ue,ke);return Hi=null,je}catch(Ye){if(Ye===yl)throw Ye;var ct=mr(29,Ye,null,ie.mode);return ct.lanes=ke,ct.return=ie,ct}finally{}}}var $o=w1(!0),x1=w1(!1),$i=J(null),Rc=J(0);function k1(i,l){i=_a,oe(Rc,i),oe($i,l),_a=i|l.baseLanes}function mg(){oe(Rc,_a),oe($i,$i.current)}function bg(){_a=Rc.current,he($i),he(Rc)}var pr=J(null),Wr=null;function Xa(i){var l=i.alternate;oe(en,en.current&1),oe(pr,i),Wr===null&&(l===null||$i.current!==null||l.memoizedState!==null)&&(Wr=i)}function T1(i){if(i.tag===22){if(oe(en,en.current),oe(pr,i),Wr===null){var l=i.alternate;l!==null&&l.memoizedState!==null&&(Wr=i)}}else Za()}function Za(){oe(en,en.current),oe(pr,pr.current)}function va(i){he(pr),Wr===i&&(Wr=null),he(en)}var en=J(0);function Cc(i){for(var l=i;l!==null;){if(l.tag===13){var f=l.memoizedState;if(f!==null&&(f=f.dehydrated,f===null||f.data==="$?"||f.data==="$!"))return l}else if(l.tag===19&&l.memoizedProps.revealOrder!==void 0){if(l.flags&128)return l}else if(l.child!==null){l.child.return=l,l=l.child;continue}if(l===i)break;for(;l.sibling===null;){if(l.return===null||l.return===i)return null;l=l.return}l.sibling.return=l.return,l=l.sibling}return null}var G$=typeof AbortController<"u"?AbortController:function(){var i=[],l=this.signal={aborted:!1,addEventListener:function(f,h){i.push(h)}};this.abort=function(){l.aborted=!0,i.forEach(function(f){return f()})}},H$=e.unstable_scheduleCallback,$$=e.unstable_NormalPriority,tn={$$typeof:b,Consumer:null,Provider:null,_currentValue:null,_currentValue2:null,_threadCount:0};function yg(){return{controller:new G$,data:new Map,refCount:0}}function wl(i){i.refCount--,i.refCount===0&&H$($$,function(){i.controller.abort()})}var xl=null,vg=0,qi=0,Vi=null;function q$(i,l){if(xl===null){var f=xl=[];vg=0,qi=Th(),Vi={status:"pending",value:void 0,then:function(h){f.push(h)}}}return vg++,l.then(A1,A1),l}function A1(){if(--vg===0&&xl!==null){Vi!==null&&(Vi.status="fulfilled");var i=xl;xl=null,qi=0,Vi=null;for(var l=0;lT?T:8;var z=I.T,q={};I.T=q,Pg(i,!1,l,f);try{var Z=S(),ae=I.S;if(ae!==null&&ae(q,Z),Z!==null&&typeof Z=="object"&&typeof Z.then=="function"){var ye=V$(Z,h);Al(i,l,ye,Jn(i))}else Al(i,l,h,Jn(i))}catch(Ae){Al(i,l,{then:function(){},status:"rejected",reason:Ae},Jn())}finally{K.p=T,I.T=z}}function Z$(){}function Lg(i,l,f,h){if(i.tag!==5)throw Error(r(476));var S=rR(i).queue;nR(i,S,l,ee,f===null?Z$:function(){return aR(i),f(h)})}function rR(i){var l=i.memoizedState;if(l!==null)return l;l={memoizedState:ee,baseState:ee,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Sa,lastRenderedState:ee},next:null};var f={};return l.next={memoizedState:f,baseState:f,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Sa,lastRenderedState:f},next:null},i.memoizedState=l,i=i.alternate,i!==null&&(i.memoizedState=l),l}function aR(i){var l=rR(i).next.queue;Al(i,l,{},Jn())}function Mg(){return En(Vl)}function oR(){return Xt().memoizedState}function iR(){return Xt().memoizedState}function Q$(i){for(var l=i.return;l!==null;){switch(l.tag){case 24:case 3:var f=Jn();i=no(f);var h=ro(l,i,f);h!==null&&(On(h,l,f),_l(h,l,f)),l={cache:yg()},i.payload=l;return}l=l.return}}function J$(i,l,f){var h=Jn();f={lane:h,revertLane:0,action:f,hasEagerState:!1,eagerState:null,next:null},Fc(i)?lR(l,f):(f=dg(i,l,f,h),f!==null&&(On(f,i,h),uR(f,l,h)))}function sR(i,l,f){var h=Jn();Al(i,l,f,h)}function Al(i,l,f,h){var S={lane:h,revertLane:0,action:f,hasEagerState:!1,eagerState:null,next:null};if(Fc(i))lR(l,S);else{var T=i.alternate;if(i.lanes===0&&(T===null||T.lanes===0)&&(T=l.lastRenderedReducer,T!==null))try{var z=l.lastRenderedState,q=T(z,f);if(S.hasEagerState=!0,S.eagerState=q,Kn(q,z))return Sc(i,l,S,0),Lt===null&&vc(),!1}catch{}finally{}if(f=dg(i,l,S,h),f!==null)return On(f,i,h),uR(f,l,h),!0}return!1}function Pg(i,l,f,h){if(h={lane:2,revertLane:Th(),action:h,hasEagerState:!1,eagerState:null,next:null},Fc(i)){if(l)throw Error(r(479))}else l=dg(i,f,h,2),l!==null&&On(l,i,2)}function Fc(i){var l=i.alternate;return i===ut||l!==null&&l===ut}function lR(i,l){Wi=Nc=!0;var f=i.pending;f===null?l.next=l:(l.next=f.next,f.next=l),i.pending=l}function uR(i,l,f){if(f&4194176){var h=l.lanes;h&=i.pendingLanes,f|=h,l.lanes=f,Rr(i,f)}}var Yr={readContext:En,use:Dc,useCallback:Wt,useContext:Wt,useEffect:Wt,useImperativeHandle:Wt,useLayoutEffect:Wt,useInsertionEffect:Wt,useMemo:Wt,useReducer:Wt,useRef:Wt,useState:Wt,useDebugValue:Wt,useDeferredValue:Wt,useTransition:Wt,useSyncExternalStore:Wt,useId:Wt};Yr.useCacheRefresh=Wt,Yr.useMemoCache=Wt,Yr.useHostTransitionStatus=Wt,Yr.useFormState=Wt,Yr.useActionState=Wt,Yr.useOptimistic=Wt;var Wo={readContext:En,use:Dc,useCallback:function(i,l){return Bn().memoizedState=[i,l===void 0?null:l],i},useContext:En,useEffect:Y1,useImperativeHandle:function(i,l,f){f=f!=null?f.concat([i]):null,Mc(4194308,4,Z1.bind(null,l,i),f)},useLayoutEffect:function(i,l){return Mc(4194308,4,i,l)},useInsertionEffect:function(i,l){Mc(4,2,i,l)},useMemo:function(i,l){var f=Bn();l=l===void 0?null:l;var h=i();if(Vo){it(!0);try{i()}finally{it(!1)}}return f.memoizedState=[h,l],h},useReducer:function(i,l,f){var h=Bn();if(f!==void 0){var S=f(l);if(Vo){it(!0);try{f(l)}finally{it(!1)}}}else S=l;return h.memoizedState=h.baseState=S,i={pending:null,lanes:0,dispatch:null,lastRenderedReducer:i,lastRenderedState:S},h.queue=i,i=i.dispatch=J$.bind(null,ut,i),[h.memoizedState,i]},useRef:function(i){var l=Bn();return i={current:i},l.memoizedState=i},useState:function(i){i=_g(i);var l=i.queue,f=sR.bind(null,ut,l);return l.dispatch=f,[i.memoizedState,f]},useDebugValue:Ig,useDeferredValue:function(i,l){var f=Bn();return Dg(f,i,l)},useTransition:function(){var i=_g(!1);return i=nR.bind(null,ut,i.queue,!0,!1),Bn().memoizedState=i,[!1,i]},useSyncExternalStore:function(i,l,f){var h=ut,S=Bn();if(wt){if(f===void 0)throw Error(r(407));f=f()}else{if(f=l(),Lt===null)throw Error(r(349));vt&60||I1(h,l,f)}S.memoizedState=f;var T={value:f,getSnapshot:l};return S.queue=T,Y1(L1.bind(null,h,T,i),[i]),h.flags|=2048,Ki(9,D1.bind(null,h,T,f,l),{destroy:void 0},null),f},useId:function(){var i=Bn(),l=Lt.identifierPrefix;if(wt){var f=ya,h=ba;f=(h&~(1<<32-Et(h)-1)).toString(32)+f,l=":"+l+"R"+f,f=Oc++,0 title"))),mn(T,h,f),T[Sn]=i,an(T),h=T;break e;case"link":var z=PC("link","href",S).get(h+(f.href||""));if(z){for(var q=0;q<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof h.is=="string"?S.createElement("select",{is:h.is}):S.createElement("select"),h.multiple?i.multiple=!0:h.size&&(i.size=h.size);break;default:i=typeof h.is=="string"?S.createElement(f,{is:h.is}):S.createElement(f)}}i[Sn]=l,i[Fn]=h;e:for(S=l.child;S!==null;){if(S.tag===5||S.tag===6)i.appendChild(S.stateNode);else if(S.tag!==4&&S.tag!==27&&S.child!==null){S.child.return=S,S=S.child;continue}if(S===l)break e;for(;S.sibling===null;){if(S.return===null||S.return===l)break e;S=S.return}S.sibling.return=S.return,S=S.sibling}l.stateNode=i;e:switch(mn(i,f,h),f){case"button":case"input":case"select":case"textarea":i=!!h.autoFocus;break e;case"img":i=!0;break e;default:i=!1}i&&Ra(l)}}return Bt(l),l.flags&=-16777217,null;case 6:if(i&&l.stateNode!=null)i.memoizedProps!==h&&Ra(l);else{if(typeof h!="string"&&l.stateNode===null)throw Error(r(166));if(i=Ie.current,hl(l)){if(i=l.stateNode,f=l.memoizedProps,h=null,S=Nn,S!==null)switch(S.tag){case 27:case 5:h=S.memoizedProps}i[Sn]=l,i=!!(i.nodeValue===f||h!==null&&h.suppressHydrationWarning===!0||kC(i.nodeValue,f)),i||Ho(l)}else i=nd(i).createTextNode(h),i[Sn]=l,l.stateNode=i}return Bt(l),null;case 13:if(h=l.memoizedState,i===null||i.memoizedState!==null&&i.memoizedState.dehydrated!==null){if(S=hl(l),h!==null&&h.dehydrated!==null){if(i===null){if(!S)throw Error(r(318));if(S=l.memoizedState,S=S!==null?S.dehydrated:null,!S)throw Error(r(317));S[Sn]=l}else ml(),!(l.flags&128)&&(l.memoizedState=null),l.flags|=4;Bt(l),S=!1}else _r!==null&&(yh(_r),_r=null),S=!0;if(!S)return l.flags&256?(va(l),l):(va(l),null)}if(va(l),l.flags&128)return l.lanes=f,l;if(f=h!==null,i=i!==null&&i.memoizedState!==null,f){h=l.child,S=null,h.alternate!==null&&h.alternate.memoizedState!==null&&h.alternate.memoizedState.cachePool!==null&&(S=h.alternate.memoizedState.cachePool.pool);var T=null;h.memoizedState!==null&&h.memoizedState.cachePool!==null&&(T=h.memoizedState.cachePool.pool),T!==S&&(h.flags|=2048)}return f!==i&&f&&(l.child.flags|=8192),qc(l,l.updateQueue),Bt(l),null;case 4:return te(),i===null&&_h(l.stateNode.containerInfo),Bt(l),null;case 10:return xa(l.type),Bt(l),null;case 19:if(he(en),S=l.memoizedState,S===null)return Bt(l),null;if(h=(l.flags&128)!==0,T=S.rendering,T===null)if(h)Pl(S,!1);else{if(qt!==0||i!==null&&i.flags&128)for(i=l.child;i!==null;){if(T=Cc(i),T!==null){for(l.flags|=128,Pl(S,!1),i=T.updateQueue,l.updateQueue=i,qc(l,i),l.subtreeFlags=0,i=f,f=l.child;f!==null;)QR(f,i),f=f.sibling;return oe(en,en.current&1|2),l.child}i=i.sibling}S.tail!==null&&me()>Vc&&(l.flags|=128,h=!0,Pl(S,!1),l.lanes=4194304)}else{if(!h)if(i=Cc(T),i!==null){if(l.flags|=128,h=!0,i=i.updateQueue,l.updateQueue=i,qc(l,i),Pl(S,!0),S.tail===null&&S.tailMode==="hidden"&&!T.alternate&&!wt)return Bt(l),null}else 2*me()-S.renderingStartTime>Vc&&f!==536870912&&(l.flags|=128,h=!0,Pl(S,!1),l.lanes=4194304);S.isBackwards?(T.sibling=l.child,l.child=T):(i=S.last,i!==null?i.sibling=T:l.child=T,S.last=T)}return S.tail!==null?(l=S.tail,S.rendering=l,S.tail=l.sibling,S.renderingStartTime=me(),l.sibling=null,i=en.current,oe(en,h?i&1|2:i&1),l):(Bt(l),null);case 22:case 23:return va(l),bg(),h=l.memoizedState!==null,i!==null?i.memoizedState!==null!==h&&(l.flags|=8192):h&&(l.flags|=8192),h?f&536870912&&!(l.flags&128)&&(Bt(l),l.subtreeFlags&6&&(l.flags|=8192)):Bt(l),f=l.updateQueue,f!==null&&qc(l,f.retryQueue),f=null,i!==null&&i.memoizedState!==null&&i.memoizedState.cachePool!==null&&(f=i.memoizedState.cachePool.pool),h=null,l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(h=l.memoizedState.cachePool.pool),h!==f&&(l.flags|=2048),i!==null&&he(qo),null;case 24:return f=null,i!==null&&(f=i.memoizedState.cache),l.memoizedState.cache!==f&&(l.flags|=2048),xa(tn),Bt(l),null;case 25:return null}throw Error(r(156,l.tag))}function i6(i,l){switch(pg(l),l.tag){case 1:return i=l.flags,i&65536?(l.flags=i&-65537|128,l):null;case 3:return xa(tn),te(),i=l.flags,i&65536&&!(i&128)?(l.flags=i&-65537|128,l):null;case 26:case 27:case 5:return Te(l),null;case 13:if(va(l),i=l.memoizedState,i!==null&&i.dehydrated!==null){if(l.alternate===null)throw Error(r(340));ml()}return i=l.flags,i&65536?(l.flags=i&-65537|128,l):null;case 19:return he(en),null;case 4:return te(),null;case 10:return xa(l.type),null;case 22:case 23:return va(l),bg(),i!==null&&he(qo),i=l.flags,i&65536?(l.flags=i&-65537|128,l):null;case 24:return xa(tn),null;case 25:return null;default:return null}}function tC(i,l){switch(pg(l),l.tag){case 3:xa(tn),te();break;case 26:case 27:case 5:Te(l);break;case 4:te();break;case 13:va(l);break;case 19:he(en);break;case 10:xa(l.type);break;case 22:case 23:va(l),bg(),i!==null&&he(qo);break;case 24:xa(tn)}}var s6={getCacheForType:function(i){var l=En(tn),f=l.data.get(i);return f===void 0&&(f=i(),l.data.set(i,f)),f}},l6=typeof WeakMap=="function"?WeakMap:Map,Ut=0,Lt=null,ft=null,vt=0,Mt=0,Qn=null,Ca=!1,Ji=!1,dh=!1,_a=0,qt=0,lo=0,ei=0,fh=0,br=0,es=0,Fl=null,Kr=null,ph=!1,gh=0,Vc=1/0,Wc=null,uo=null,Yc=!1,ti=null,zl=0,hh=0,mh=null,Bl=0,bh=null;function Jn(){if(Ut&2&&vt!==0)return vt&-vt;if(I.T!==null){var i=qi;return i!==0?i:Th()}return wA()}function nC(){br===0&&(br=!(vt&536870912)||wt?Nt():536870912);var i=pr.current;return i!==null&&(i.flags|=32),br}function On(i,l,f){(i===Lt&&Mt===2||i.cancelPendingCommit!==null)&&(ts(i,0),Na(i,vt,br,!1)),Mn(i,f),(!(Ut&2)||i!==Lt)&&(i===Lt&&(!(Ut&2)&&(ei|=f),qt===4&&Na(i,vt,br,!1)),Xr(i))}function rC(i,l,f){if(Ut&6)throw Error(r(327));var h=!f&&(l&60)===0&&(l&i.expiredLanes)===0||Xe(i,l),S=h?d6(i,l):Eh(i,l,!0),T=h;do{if(S===0){Ji&&!h&&Na(i,l,0,!1);break}else if(S===6)Na(i,l,0,!Ca);else{if(f=i.current.alternate,T&&!u6(f)){S=Eh(i,l,!1),T=!1;continue}if(S===2){if(T=l,i.errorRecoveryDisabledLanes&T)var z=0;else z=i.pendingLanes&-536870913,z=z!==0?z:z&536870912?536870912:0;if(z!==0){l=z;e:{var q=i;S=Fl;var Z=q.current.memoizedState.isDehydrated;if(Z&&(ts(q,z).flags|=256),z=Eh(q,z,!1),z!==2){if(dh&&!Z){q.errorRecoveryDisabledLanes|=T,ei|=T,S=4;break e}T=Kr,Kr=S,T!==null&&yh(T)}S=z}if(T=!1,S!==2)continue}}if(S===1){ts(i,0),Na(i,l,0,!0);break}e:{switch(h=i,S){case 0:case 1:throw Error(r(345));case 4:if((l&4194176)===l){Na(h,l,br,!Ca);break e}break;case 2:Kr=null;break;case 3:case 5:break;default:throw Error(r(329))}if(h.finishedWork=f,h.finishedLanes=l,(l&62914560)===l&&(T=gh+300-me(),10f?32:f,I.T=null,ti===null)var T=!1;else{f=mh,mh=null;var z=ti,q=zl;if(ti=null,zl=0,Ut&6)throw Error(r(331));var Z=Ut;if(Ut|=4,XR(z.current),WR(z,z.current,q,f),Ut=Z,Ul(0,!1),et&&typeof et.onPostCommitFiberRoot=="function")try{et.onPostCommitFiberRoot(bt,z)}catch{}T=!0}return T}finally{K.p=S,I.T=h,fC(i,l)}}return!1}function pC(i,l,f){l=cr(f,l),l=Bg(i.stateNode,l,2),i=ro(i,l,2),i!==null&&(Mn(i,2),Xr(i))}function Ot(i,l,f){if(i.tag===3)pC(i,i,f);else for(;l!==null;){if(l.tag===3){pC(l,i,f);break}else if(l.tag===1){var h=l.stateNode;if(typeof l.type.getDerivedStateFromError=="function"||typeof h.componentDidCatch=="function"&&(uo===null||!uo.has(h))){i=cr(f,i),f=mR(2),h=ro(l,f,2),h!==null&&(bR(f,h,l,i),Mn(h,2),Xr(h));break}}l=l.return}}function wh(i,l,f){var h=i.pingCache;if(h===null){h=i.pingCache=new l6;var S=new Set;h.set(l,S)}else S=h.get(l),S===void 0&&(S=new Set,h.set(l,S));S.has(f)||(dh=!0,S.add(f),i=g6.bind(null,i,l,f),l.then(i,i))}function g6(i,l,f){var h=i.pingCache;h!==null&&h.delete(l),i.pingedLanes|=i.suspendedLanes&f,i.warmLanes&=~f,Lt===i&&(vt&f)===f&&(qt===4||qt===3&&(vt&62914560)===vt&&300>me()-gh?!(Ut&2)&&ts(i,0):fh|=f,es===vt&&(es=0)),Xr(i)}function gC(i,l){l===0&&(l=Ln()),i=Ka(i,l),i!==null&&(Mn(i,l),Xr(i))}function h6(i){var l=i.memoizedState,f=0;l!==null&&(f=l.retryLane),gC(i,f)}function m6(i,l){var f=0;switch(i.tag){case 13:var h=i.stateNode,S=i.memoizedState;S!==null&&(f=S.retryLane);break;case 19:h=i.stateNode;break;case 22:h=i.stateNode._retryCache;break;default:throw Error(r(314))}h!==null&&h.delete(l),gC(i,f)}function b6(i,l){return xe(i,l)}var Zc=null,as=null,xh=!1,Qc=!1,kh=!1,ni=0;function Xr(i){i!==as&&i.next===null&&(as===null?Zc=as=i:as=as.next=i),Qc=!0,xh||(xh=!0,v6(y6))}function Ul(i,l){if(!kh&&Qc){kh=!0;do for(var f=!1,h=Zc;h!==null;){if(i!==0){var S=h.pendingLanes;if(S===0)var T=0;else{var z=h.suspendedLanes,q=h.pingedLanes;T=(1<<31-Et(42|i)+1)-1,T&=S&~(z&~q),T=T&201326677?T&201326677|1:T?T|2:0}T!==0&&(f=!0,bC(h,T))}else T=vt,T=pa(h,h===Lt?T:0),!(T&3)||Xe(h,T)||(f=!0,bC(h,T));h=h.next}while(f);kh=!1}}function y6(){Qc=xh=!1;var i=0;ni!==0&&(R6()&&(i=ni),ni=0);for(var l=me(),f=null,h=Zc;h!==null;){var S=h.next,T=hC(h,l);T===0?(h.next=null,f===null?Zc=S:f.next=S,S===null&&(as=f)):(f=h,(i!==0||T&3)&&(Qc=!0)),h=S}Ul(i)}function hC(i,l){for(var f=i.suspendedLanes,h=i.pingedLanes,S=i.expirationTimes,T=i.pendingLanes&-62914561;0"u"?null:document;function IC(i,l,f){var h=is;if(h&&typeof l=="string"&&l){var S=lr(l);S='link[rel="'+i+'"][href="'+S+'"]',typeof f=="string"&&(S+='[crossorigin="'+f+'"]'),OC.has(S)||(OC.add(S),i={rel:i,crossOrigin:f,href:l},h.querySelector(S)===null&&(l=h.createElement("link"),mn(l,"link",i),an(l),h.head.appendChild(l)))}}function M6(i){Oa.D(i),IC("dns-prefetch",i,null)}function P6(i,l){Oa.C(i,l),IC("preconnect",i,l)}function F6(i,l,f){Oa.L(i,l,f);var h=is;if(h&&i&&l){var S='link[rel="preload"][as="'+lr(l)+'"]';l==="image"&&f&&f.imageSrcSet?(S+='[imagesrcset="'+lr(f.imageSrcSet)+'"]',typeof f.imageSizes=="string"&&(S+='[imagesizes="'+lr(f.imageSizes)+'"]')):S+='[href="'+lr(i)+'"]';var T=S;switch(l){case"style":T=ss(i);break;case"script":T=ls(i)}yr.has(T)||(i=D({rel:"preload",href:l==="image"&&f&&f.imageSrcSet?void 0:i,as:l},f),yr.set(T,i),h.querySelector(S)!==null||l==="style"&&h.querySelector(Hl(T))||l==="script"&&h.querySelector($l(T))||(l=h.createElement("link"),mn(l,"link",i),an(l),h.head.appendChild(l)))}}function z6(i,l){Oa.m(i,l);var f=is;if(f&&i){var h=l&&typeof l.as=="string"?l.as:"script",S='link[rel="modulepreload"][as="'+lr(h)+'"][href="'+lr(i)+'"]',T=S;switch(h){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":T=ls(i)}if(!yr.has(T)&&(i=D({rel:"modulepreload",href:i},l),yr.set(T,i),f.querySelector(S)===null)){switch(h){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(f.querySelector($l(T)))return}h=f.createElement("link"),mn(h,"link",i),an(h),f.head.appendChild(h)}}}function B6(i,l,f){Oa.S(i,l,f);var h=is;if(h&&i){var S=Ni(h).hoistableStyles,T=ss(i);l=l||"default";var z=S.get(T);if(!z){var q={loading:0,preload:null};if(z=h.querySelector(Hl(T)))q.loading=5;else{i=D({rel:"stylesheet",href:i,"data-precedence":l},f),(f=yr.get(T))&&zh(i,f);var Z=z=h.createElement("link");an(Z),mn(Z,"link",i),Z._p=new Promise(function(ae,ye){Z.onload=ae,Z.onerror=ye}),Z.addEventListener("load",function(){q.loading|=1}),Z.addEventListener("error",function(){q.loading|=2}),q.loading|=4,ad(z,l,h)}z={type:"stylesheet",instance:z,count:1,state:q},S.set(T,z)}}}function U6(i,l){Oa.X(i,l);var f=is;if(f&&i){var h=Ni(f).hoistableScripts,S=ls(i),T=h.get(S);T||(T=f.querySelector($l(S)),T||(i=D({src:i,async:!0},l),(l=yr.get(S))&&Bh(i,l),T=f.createElement("script"),an(T),mn(T,"link",i),f.head.appendChild(T)),T={type:"script",instance:T,count:1,state:null},h.set(S,T))}}function j6(i,l){Oa.M(i,l);var f=is;if(f&&i){var h=Ni(f).hoistableScripts,S=ls(i),T=h.get(S);T||(T=f.querySelector($l(S)),T||(i=D({src:i,async:!0,type:"module"},l),(l=yr.get(S))&&Bh(i,l),T=f.createElement("script"),an(T),mn(T,"link",i),f.head.appendChild(T)),T={type:"script",instance:T,count:1,state:null},h.set(S,T))}}function DC(i,l,f,h){var S=(S=Ie.current)?rd(S):null;if(!S)throw Error(r(446));switch(i){case"meta":case"title":return null;case"style":return typeof f.precedence=="string"&&typeof f.href=="string"?(l=ss(f.href),f=Ni(S).hoistableStyles,h=f.get(l),h||(h={type:"style",instance:null,count:0,state:null},f.set(l,h)),h):{type:"void",instance:null,count:0,state:null};case"link":if(f.rel==="stylesheet"&&typeof f.href=="string"&&typeof f.precedence=="string"){i=ss(f.href);var T=Ni(S).hoistableStyles,z=T.get(i);if(z||(S=S.ownerDocument||S,z={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},T.set(i,z),(T=S.querySelector(Hl(i)))&&!T._p&&(z.instance=T,z.state.loading=5),yr.has(i)||(f={rel:"preload",as:"style",href:f.href,crossOrigin:f.crossOrigin,integrity:f.integrity,media:f.media,hrefLang:f.hrefLang,referrerPolicy:f.referrerPolicy},yr.set(i,f),T||G6(S,i,f,z.state))),l&&h===null)throw Error(r(528,""));return z}if(l&&h!==null)throw Error(r(529,""));return null;case"script":return l=f.async,f=f.src,typeof f=="string"&&l&&typeof l!="function"&&typeof l!="symbol"?(l=ls(f),f=Ni(S).hoistableScripts,h=f.get(l),h||(h={type:"script",instance:null,count:0,state:null},f.set(l,h)),h):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,i))}}function ss(i){return'href="'+lr(i)+'"'}function Hl(i){return'link[rel="stylesheet"]['+i+"]"}function LC(i){return D({},i,{"data-precedence":i.precedence,precedence:null})}function G6(i,l,f,h){i.querySelector('link[rel="preload"][as="style"]['+l+"]")?h.loading=1:(l=i.createElement("link"),h.preload=l,l.addEventListener("load",function(){return h.loading|=1}),l.addEventListener("error",function(){return h.loading|=2}),mn(l,"link",f),an(l),i.head.appendChild(l))}function ls(i){return'[src="'+lr(i)+'"]'}function $l(i){return"script[async]"+i}function MC(i,l,f){if(l.count++,l.instance===null)switch(l.type){case"style":var h=i.querySelector('style[data-href~="'+lr(f.href)+'"]');if(h)return l.instance=h,an(h),h;var S=D({},f,{"data-href":f.href,"data-precedence":f.precedence,href:null,precedence:null});return h=(i.ownerDocument||i).createElement("style"),an(h),mn(h,"style",S),ad(h,f.precedence,i),l.instance=h;case"stylesheet":S=ss(f.href);var T=i.querySelector(Hl(S));if(T)return l.state.loading|=4,l.instance=T,an(T),T;h=LC(f),(S=yr.get(S))&&zh(h,S),T=(i.ownerDocument||i).createElement("link"),an(T);var z=T;return z._p=new Promise(function(q,Z){z.onload=q,z.onerror=Z}),mn(T,"link",h),l.state.loading|=4,ad(T,f.precedence,i),l.instance=T;case"script":return T=ls(f.src),(S=i.querySelector($l(T)))?(l.instance=S,an(S),S):(h=f,(S=yr.get(T))&&(h=D({},f),Bh(h,S)),i=i.ownerDocument||i,S=i.createElement("script"),an(S),mn(S,"link",h),i.head.appendChild(S),l.instance=S);case"void":return null;default:throw Error(r(443,l.type))}else l.type==="stylesheet"&&!(l.state.loading&4)&&(h=l.instance,l.state.loading|=4,ad(h,f.precedence,i));return l.instance}function ad(i,l,f){for(var h=f.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),S=h.length?h[h.length-1]:null,T=S,z=0;z title"):null)}function H6(i,l,f){if(f===1||l.itemProp!=null)return!1;switch(i){case"meta":case"title":return!0;case"style":if(typeof l.precedence!="string"||typeof l.href!="string"||l.href==="")break;return!0;case"link":if(typeof l.rel!="string"||typeof l.href!="string"||l.href===""||l.onLoad||l.onError)break;switch(l.rel){case"stylesheet":return i=l.disabled,typeof l.precedence=="string"&&i==null;default:return!0}case"script":if(l.async&&typeof l.async!="function"&&typeof l.async!="symbol"&&!l.onLoad&&!l.onError&&l.src&&typeof l.src=="string")return!0}return!1}function zC(i){return!(i.type==="stylesheet"&&!(i.state.loading&3))}var ql=null;function $6(){}function q6(i,l,f){if(ql===null)throw Error(r(475));var h=ql;if(l.type==="stylesheet"&&(typeof f.media!="string"||matchMedia(f.media).matches!==!1)&&!(l.state.loading&4)){if(l.instance===null){var S=ss(f.href),T=i.querySelector(Hl(S));if(T){i=T._p,i!==null&&typeof i=="object"&&typeof i.then=="function"&&(h.count++,h=id.bind(h),i.then(h,h)),l.state.loading|=4,l.instance=T,an(T);return}T=i.ownerDocument||i,f=LC(f),(S=yr.get(S))&&zh(f,S),T=T.createElement("link"),an(T);var z=T;z._p=new Promise(function(q,Z){z.onload=q,z.onerror=Z}),mn(T,"link",f),l.instance=T}h.stylesheets===null&&(h.stylesheets=new Map),h.stylesheets.set(l,i),(i=l.state.preload)&&!(l.state.loading&3)&&(h.count++,l=id.bind(h),i.addEventListener("load",l),i.addEventListener("error",l))}}function V6(){if(ql===null)throw Error(r(475));var i=ql;return i.stylesheets&&i.count===0&&Uh(i,i.stylesheets),0"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),Kh.exports=pq(),Kh.exports}var hq=gq(),Jl={},i_;function mq(){if(i_)return Jl;i_=1,Object.defineProperty(Jl,"__esModule",{value:!0}),Jl.parse=s,Jl.serialize=d;const e=/^[\u0021-\u003A\u003C\u003E-\u007E]+$/,t=/^[\u0021-\u003A\u003C-\u007E]*$/,n=/^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i,r=/^[\u0020-\u003A\u003D-\u007E]*$/,a=Object.prototype.toString,o=(()=>{const m=function(){};return m.prototype=Object.create(null),m})();function s(m,b){const y=new o,v=m.length;if(v<2)return y;const x=(b==null?void 0:b.decode)||p;let A=0;do{const k=m.indexOf("=",A);if(k===-1)break;const R=m.indexOf(";",A),O=R===-1?v:R;if(k>O){A=m.lastIndexOf(";",k-1)+1;continue}const N=u(m,A,k),C=c(m,k,N),_=m.slice(N,C);if(y[_]===void 0){let L=u(m,k+1,O),I=c(m,O,L);const D=x(m.slice(L,I));y[_]=D}A=O+1}while(Ay;){const v=m.charCodeAt(--b);if(v!==32&&v!==9)return b+1}return y}function d(m,b,y){const v=(y==null?void 0:y.encode)||encodeURIComponent;if(!e.test(m))throw new TypeError(`argument name is invalid: ${m}`);const x=v(b);if(!t.test(x))throw new TypeError(`argument val is invalid: ${b}`);let A=m+"="+x;if(!y)return A;if(y.maxAge!==void 0){if(!Number.isInteger(y.maxAge))throw new TypeError(`option maxAge is invalid: ${y.maxAge}`);A+="; Max-Age="+y.maxAge}if(y.domain){if(!n.test(y.domain))throw new TypeError(`option domain is invalid: ${y.domain}`);A+="; Domain="+y.domain}if(y.path){if(!r.test(y.path))throw new TypeError(`option path is invalid: ${y.path}`);A+="; Path="+y.path}if(y.expires){if(!g(y.expires)||!Number.isFinite(y.expires.valueOf()))throw new TypeError(`option expires is invalid: ${y.expires}`);A+="; Expires="+y.expires.toUTCString()}if(y.httpOnly&&(A+="; HttpOnly"),y.secure&&(A+="; Secure"),y.partitioned&&(A+="; Partitioned"),y.priority)switch(typeof y.priority=="string"?y.priority.toLowerCase():void 0){case"low":A+="; Priority=Low";break;case"medium":A+="; Priority=Medium";break;case"high":A+="; Priority=High";break;default:throw new TypeError(`option priority is invalid: ${y.priority}`)}if(y.sameSite)switch(typeof y.sameSite=="string"?y.sameSite.toLowerCase():y.sameSite){case!0:case"strict":A+="; SameSite=Strict";break;case"lax":A+="; SameSite=Lax";break;case"none":A+="; SameSite=None";break;default:throw new TypeError(`option sameSite is invalid: ${y.sameSite}`)}return A}function p(m){if(m.indexOf("%")===-1)return m;try{return decodeURIComponent(m)}catch{return m}}function g(m){return a.call(m)==="[object Date]"}return Jl}mq();/** * react-router v7.3.0 * * Copyright (c) Remix Software Inc. @@ -55,19 +55,19 @@ Error generating stack: `+f.message+` * LICENSE.md file in the root directory of this source tree. * * @license MIT - */var aC="popstate";function hq(e={}){function t(a,o){let{pathname:s="/",search:u="",hash:c=""}=wi(a.location.hash.substring(1));return!s.startsWith("/")&&!s.startsWith(".")&&(s="/"+s),sk("",{pathname:s,search:u,hash:c},o.state&&o.state.usr||null,o.state&&o.state.key||"default")}function n(a,o){let s=a.document.querySelector("base"),u="";if(s&&s.getAttribute("href")){let c=a.location.href,d=c.indexOf("#");u=d===-1?c:c.slice(0,d)}return u+"#"+(typeof o=="string"?o:Eu(o))}function r(a,o){zr(a.pathname.charAt(0)==="/",`relative pathnames are not supported in hash history.push(${JSON.stringify(o)})`)}return bq(t,n,r,e)}function Gt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function zr(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function mq(){return Math.random().toString(36).substring(2,10)}function oC(e,t){return{usr:e.state,key:e.key,idx:t}}function sk(e,t,n=null,r){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?wi(t):t,state:n,key:t&&t.key||r||mq()}}function Eu({pathname:e="/",search:t="",hash:n=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),n&&n!=="#"&&(e+=n.charAt(0)==="#"?n:"#"+n),e}function wi(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function bq(e,t,n,r={}){let{window:a=document.defaultView,v5Compat:o=!1}=r,s=a.history,u="POP",c=null,d=p();d==null&&(d=0,s.replaceState({...s.state,idx:d},""));function p(){return(s.state||{idx:null}).idx}function g(){u="POP";let x=p(),R=x==null?null:x-d;d=x,c&&c({action:u,location:v.location,delta:R})}function m(x,R){u="PUSH";let k=sk(v.location,x,R);n&&n(k,x),d=p()+1;let A=oC(k,d),O=v.createHref(k);try{s.pushState(A,"",O)}catch(N){if(N instanceof DOMException&&N.name==="DataCloneError")throw N;a.location.assign(O)}o&&c&&c({action:u,location:v.location,delta:1})}function b(x,R){u="REPLACE";let k=sk(v.location,x,R);n&&n(k,x),d=p();let A=oC(k,d),O=v.createHref(k);s.replaceState(A,"",O),o&&c&&c({action:u,location:v.location,delta:0})}function y(x){let R=a.location.origin!=="null"?a.location.origin:a.location.href,k=typeof x=="string"?x:Eu(x);return k=k.replace(/ $/,"%20"),Gt(R,`No window.location.(origin|href) available to create URL for href: ${k}`),new URL(k,R)}let v={get action(){return u},get location(){return e(a,s)},listen(x){if(c)throw new Error("A history only accepts one active listener");return a.addEventListener(aC,g),c=x,()=>{a.removeEventListener(aC,g),c=null}},createHref(x){return t(a,x)},createURL:y,encodeLocation(x){let R=y(x);return{pathname:R.pathname,search:R.search,hash:R.hash}},push:m,replace:b,go(x){return s.go(x)}};return v}function Bz(e,t,n="/"){return yq(e,t,n,!1)}function yq(e,t,n,r){let a=typeof t=="string"?wi(t):t,o=za(a.pathname||"/",n);if(o==null)return null;let s=Uz(e);vq(s);let u=null;for(let c=0;u==null&&c{let c={relativePath:u===void 0?o.path||"":u,caseSensitive:o.caseSensitive===!0,childrenIndex:s,route:o};c.relativePath.startsWith("/")&&(Gt(c.relativePath.startsWith(r),`Absolute route path "${c.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),c.relativePath=c.relativePath.slice(r.length));let d=Pa([r,c.relativePath]),p=n.concat(c);o.children&&o.children.length>0&&(Gt(o.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${d}".`),Uz(o.children,t,p,d)),!(o.path==null&&!o.index)&&t.push({path:d,score:Aq(d,o.index),routesMeta:p})};return e.forEach((o,s)=>{var u;if(o.path===""||!((u=o.path)!=null&&u.includes("?")))a(o,s);else for(let c of jz(o.path))a(o,s,c)}),t}function jz(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,a=n.endsWith("?"),o=n.replace(/\?$/,"");if(r.length===0)return a?[o,""]:[o];let s=jz(r.join("/")),u=[];return u.push(...s.map(c=>c===""?o:[o,c].join("/"))),a&&u.push(...s),u.map(c=>e.startsWith("/")&&c===""?"/":c)}function vq(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:Rq(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}var Sq=/^:[\w-]+$/,Eq=3,wq=2,xq=1,kq=10,Tq=-2,iC=e=>e==="*";function Aq(e,t){let n=e.split("/"),r=n.length;return n.some(iC)&&(r+=Tq),t&&(r+=wq),n.filter(a=>!iC(a)).reduce((a,o)=>a+(Sq.test(o)?Eq:o===""?xq:kq),r)}function Rq(e,t){return e.length===t.length&&e.slice(0,-1).every((r,a)=>r===t[a])?e[e.length-1]-t[t.length-1]:0}function _q(e,t,n=!1){let{routesMeta:r}=e,a={},o="/",s=[];for(let u=0;u{if(p==="*"){let y=u[m]||"";s=o.slice(0,o.length-y.length).replace(/(.)\/+$/,"$1")}const b=u[m];return g&&!b?d[p]=void 0:d[p]=(b||"").replace(/%2F/g,"/"),d},{}),pathname:o,pathnameBase:s,pattern:e}}function Cq(e,t=!1,n=!0){zr(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let r=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(s,u,c)=>(r.push({paramName:u,isOptional:c!=null}),c?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),a+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?a+="\\/*$":e!==""&&e!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),r]}function Nq(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return zr(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function za(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function Oq(e,t="/"){let{pathname:n,search:r="",hash:a=""}=typeof e=="string"?wi(e):e;return{pathname:n?n.startsWith("/")?n:Dq(n,t):t,search:Mq(r),hash:Pq(a)}}function Dq(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(a=>{a===".."?n.length>1&&n.pop():a!=="."&&n.push(a)}),n.length>1?n.join("/"):"/"}function Zh(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Iq(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Gz(e){let t=Iq(e);return t.map((n,r)=>r===t.length-1?n.pathname:n.pathnameBase)}function Hz(e,t,n,r=!1){let a;typeof e=="string"?a=wi(e):(a={...e},Gt(!a.pathname||!a.pathname.includes("?"),Zh("?","pathname","search",a)),Gt(!a.pathname||!a.pathname.includes("#"),Zh("#","pathname","hash",a)),Gt(!a.search||!a.search.includes("#"),Zh("#","search","hash",a)));let o=e===""||a.pathname==="",s=o?"/":a.pathname,u;if(s==null)u=n;else{let g=t.length-1;if(!r&&s.startsWith("..")){let m=s.split("/");for(;m[0]==="..";)m.shift(),g-=1;a.pathname=m.join("/")}u=g>=0?t[g]:"/"}let c=Oq(a,u),d=s&&s!=="/"&&s.endsWith("/"),p=(o||s===".")&&n.endsWith("/");return!c.pathname.endsWith("/")&&(d||p)&&(c.pathname+="/"),c}var Pa=e=>e.join("/").replace(/\/\/+/g,"/"),Lq=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),Mq=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,Pq=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function Fq(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}var $z=["POST","PUT","PATCH","DELETE"];new Set($z);var zq=["GET",...$z];new Set(zq);var Us=w.createContext(null);Us.displayName="DataRouter";var $f=w.createContext(null);$f.displayName="DataRouterState";var qz=w.createContext({isTransitioning:!1});qz.displayName="ViewTransition";var Bq=w.createContext(new Map);Bq.displayName="Fetchers";var Uq=w.createContext(null);Uq.displayName="Await";var ia=w.createContext(null);ia.displayName="Navigation";var Bu=w.createContext(null);Bu.displayName="Location";var Ha=w.createContext({outlet:null,matches:[],isDataRoute:!1});Ha.displayName="Route";var T0=w.createContext(null);T0.displayName="RouteError";function jq(e,{relative:t}={}){Gt(Uu(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:r}=w.useContext(ia),{hash:a,pathname:o,search:s}=ju(e,{relative:t}),u=o;return n!=="/"&&(u=o==="/"?n:Pa([n,o])),r.createHref({pathname:u,search:s,hash:a})}function Uu(){return w.useContext(Bu)!=null}function xi(){return Gt(Uu(),"useLocation() may be used only in the context of a component."),w.useContext(Bu).location}var Vz="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Wz(e){w.useContext(ia).static||w.useLayoutEffect(e)}function A0(){let{isDataRoute:e}=w.useContext(Ha);return e?e9():Gq()}function Gq(){Gt(Uu(),"useNavigate() may be used only in the context of a component.");let e=w.useContext(Us),{basename:t,navigator:n}=w.useContext(ia),{matches:r}=w.useContext(Ha),{pathname:a}=xi(),o=JSON.stringify(Gz(r)),s=w.useRef(!1);return Wz(()=>{s.current=!0}),w.useCallback((c,d={})=>{if(zr(s.current,Vz),!s.current)return;if(typeof c=="number"){n.go(c);return}let p=Hz(c,JSON.parse(o),a,d.relative==="path");e==null&&t!=="/"&&(p.pathname=p.pathname==="/"?t:Pa([t,p.pathname])),(d.replace?n.replace:n.push)(p,d.state,d)},[t,n,o,a,e])}w.createContext(null);function ju(e,{relative:t}={}){let{matches:n}=w.useContext(Ha),{pathname:r}=xi(),a=JSON.stringify(Gz(n));return w.useMemo(()=>Hz(e,JSON.parse(a),r,t==="path"),[e,a,r,t])}function Hq(e,t){return Yz(e,t)}function Yz(e,t,n,r){var k;Gt(Uu(),"useRoutes() may be used only in the context of a component.");let{navigator:a,static:o}=w.useContext(ia),{matches:s}=w.useContext(Ha),u=s[s.length-1],c=u?u.params:{},d=u?u.pathname:"/",p=u?u.pathnameBase:"/",g=u&&u.route;{let A=g&&g.path||"";Kz(d,!g||A.endsWith("*")||A.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${d}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + */var s_="popstate";function bq(e={}){function t(a,o){let{pathname:s="/",search:u="",hash:c=""}=wi(a.location.hash.substring(1));return!s.startsWith("/")&&!s.startsWith(".")&&(s="/"+s),uk("",{pathname:s,search:u,hash:c},o.state&&o.state.usr||null,o.state&&o.state.key||"default")}function n(a,o){let s=a.document.querySelector("base"),u="";if(s&&s.getAttribute("href")){let c=a.location.href,d=c.indexOf("#");u=d===-1?c:c.slice(0,d)}return u+"#"+(typeof o=="string"?o:Su(o))}function r(a,o){Br(a.pathname.charAt(0)==="/",`relative pathnames are not supported in hash history.push(${JSON.stringify(o)})`)}return vq(t,n,r,e)}function Gt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function Br(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function yq(){return Math.random().toString(36).substring(2,10)}function l_(e,t){return{usr:e.state,key:e.key,idx:t}}function uk(e,t,n=null,r){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?wi(t):t,state:n,key:t&&t.key||r||yq()}}function Su({pathname:e="/",search:t="",hash:n=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),n&&n!=="#"&&(e+=n.charAt(0)==="#"?n:"#"+n),e}function wi(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function vq(e,t,n,r={}){let{window:a=document.defaultView,v5Compat:o=!1}=r,s=a.history,u="POP",c=null,d=p();d==null&&(d=0,s.replaceState({...s.state,idx:d},""));function p(){return(s.state||{idx:null}).idx}function g(){u="POP";let x=p(),A=x==null?null:x-d;d=x,c&&c({action:u,location:v.location,delta:A})}function m(x,A){u="PUSH";let k=uk(v.location,x,A);n&&n(k,x),d=p()+1;let R=l_(k,d),O=v.createHref(k);try{s.pushState(R,"",O)}catch(N){if(N instanceof DOMException&&N.name==="DataCloneError")throw N;a.location.assign(O)}o&&c&&c({action:u,location:v.location,delta:1})}function b(x,A){u="REPLACE";let k=uk(v.location,x,A);n&&n(k,x),d=p();let R=l_(k,d),O=v.createHref(k);s.replaceState(R,"",O),o&&c&&c({action:u,location:v.location,delta:0})}function y(x){let A=a.location.origin!=="null"?a.location.origin:a.location.href,k=typeof x=="string"?x:Su(x);return k=k.replace(/ $/,"%20"),Gt(A,`No window.location.(origin|href) available to create URL for href: ${k}`),new URL(k,A)}let v={get action(){return u},get location(){return e(a,s)},listen(x){if(c)throw new Error("A history only accepts one active listener");return a.addEventListener(s_,g),c=x,()=>{a.removeEventListener(s_,g),c=null}},createHref(x){return t(a,x)},createURL:y,encodeLocation(x){let A=y(x);return{pathname:A.pathname,search:A.search,hash:A.hash}},push:m,replace:b,go(x){return s.go(x)}};return v}function Hz(e,t,n="/"){return Sq(e,t,n,!1)}function Sq(e,t,n,r){let a=typeof t=="string"?wi(t):t,o=Ba(a.pathname||"/",n);if(o==null)return null;let s=$z(e);Eq(s);let u=null;for(let c=0;u==null&&c{let c={relativePath:u===void 0?o.path||"":u,caseSensitive:o.caseSensitive===!0,childrenIndex:s,route:o};c.relativePath.startsWith("/")&&(Gt(c.relativePath.startsWith(r),`Absolute route path "${c.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),c.relativePath=c.relativePath.slice(r.length));let d=Fa([r,c.relativePath]),p=n.concat(c);o.children&&o.children.length>0&&(Gt(o.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${d}".`),$z(o.children,t,p,d)),!(o.path==null&&!o.index)&&t.push({path:d,score:Cq(d,o.index),routesMeta:p})};return e.forEach((o,s)=>{var u;if(o.path===""||!((u=o.path)!=null&&u.includes("?")))a(o,s);else for(let c of qz(o.path))a(o,s,c)}),t}function qz(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,a=n.endsWith("?"),o=n.replace(/\?$/,"");if(r.length===0)return a?[o,""]:[o];let s=qz(r.join("/")),u=[];return u.push(...s.map(c=>c===""?o:[o,c].join("/"))),a&&u.push(...s),u.map(c=>e.startsWith("/")&&c===""?"/":c)}function Eq(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:_q(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}var wq=/^:[\w-]+$/,xq=3,kq=2,Tq=1,Aq=10,Rq=-2,u_=e=>e==="*";function Cq(e,t){let n=e.split("/"),r=n.length;return n.some(u_)&&(r+=Rq),t&&(r+=kq),n.filter(a=>!u_(a)).reduce((a,o)=>a+(wq.test(o)?xq:o===""?Tq:Aq),r)}function _q(e,t){return e.length===t.length&&e.slice(0,-1).every((r,a)=>r===t[a])?e[e.length-1]-t[t.length-1]:0}function Nq(e,t,n=!1){let{routesMeta:r}=e,a={},o="/",s=[];for(let u=0;u{if(p==="*"){let y=u[m]||"";s=o.slice(0,o.length-y.length).replace(/(.)\/+$/,"$1")}const b=u[m];return g&&!b?d[p]=void 0:d[p]=(b||"").replace(/%2F/g,"/"),d},{}),pathname:o,pathnameBase:s,pattern:e}}function Oq(e,t=!1,n=!0){Br(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let r=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(s,u,c)=>(r.push({paramName:u,isOptional:c!=null}),c?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),a+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?a+="\\/*$":e!==""&&e!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),r]}function Iq(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return Br(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function Ba(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function Dq(e,t="/"){let{pathname:n,search:r="",hash:a=""}=typeof e=="string"?wi(e):e;return{pathname:n?n.startsWith("/")?n:Lq(n,t):t,search:Fq(r),hash:zq(a)}}function Lq(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(a=>{a===".."?n.length>1&&n.pop():a!=="."&&n.push(a)}),n.length>1?n.join("/"):"/"}function Jh(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Mq(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Vz(e){let t=Mq(e);return t.map((n,r)=>r===t.length-1?n.pathname:n.pathnameBase)}function Wz(e,t,n,r=!1){let a;typeof e=="string"?a=wi(e):(a={...e},Gt(!a.pathname||!a.pathname.includes("?"),Jh("?","pathname","search",a)),Gt(!a.pathname||!a.pathname.includes("#"),Jh("#","pathname","hash",a)),Gt(!a.search||!a.search.includes("#"),Jh("#","search","hash",a)));let o=e===""||a.pathname==="",s=o?"/":a.pathname,u;if(s==null)u=n;else{let g=t.length-1;if(!r&&s.startsWith("..")){let m=s.split("/");for(;m[0]==="..";)m.shift(),g-=1;a.pathname=m.join("/")}u=g>=0?t[g]:"/"}let c=Dq(a,u),d=s&&s!=="/"&&s.endsWith("/"),p=(o||s===".")&&n.endsWith("/");return!c.pathname.endsWith("/")&&(d||p)&&(c.pathname+="/"),c}var Fa=e=>e.join("/").replace(/\/\/+/g,"/"),Pq=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),Fq=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,zq=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function Bq(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}var Yz=["POST","PUT","PATCH","DELETE"];new Set(Yz);var Uq=["GET",...Yz];new Set(Uq);var js=w.createContext(null);js.displayName="DataRouter";var qf=w.createContext(null);qf.displayName="DataRouterState";var Kz=w.createContext({isTransitioning:!1});Kz.displayName="ViewTransition";var jq=w.createContext(new Map);jq.displayName="Fetchers";var Gq=w.createContext(null);Gq.displayName="Await";var sa=w.createContext(null);sa.displayName="Navigation";var zu=w.createContext(null);zu.displayName="Location";var Ha=w.createContext({outlet:null,matches:[],isDataRoute:!1});Ha.displayName="Route";var R0=w.createContext(null);R0.displayName="RouteError";function Hq(e,{relative:t}={}){Gt(Bu(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:r}=w.useContext(sa),{hash:a,pathname:o,search:s}=Uu(e,{relative:t}),u=o;return n!=="/"&&(u=o==="/"?n:Fa([n,o])),r.createHref({pathname:u,search:s,hash:a})}function Bu(){return w.useContext(zu)!=null}function xi(){return Gt(Bu(),"useLocation() may be used only in the context of a component."),w.useContext(zu).location}var Xz="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Zz(e){w.useContext(sa).static||w.useLayoutEffect(e)}function C0(){let{isDataRoute:e}=w.useContext(Ha);return e?n9():$q()}function $q(){Gt(Bu(),"useNavigate() may be used only in the context of a component.");let e=w.useContext(js),{basename:t,navigator:n}=w.useContext(sa),{matches:r}=w.useContext(Ha),{pathname:a}=xi(),o=JSON.stringify(Vz(r)),s=w.useRef(!1);return Zz(()=>{s.current=!0}),w.useCallback((c,d={})=>{if(Br(s.current,Xz),!s.current)return;if(typeof c=="number"){n.go(c);return}let p=Wz(c,JSON.parse(o),a,d.relative==="path");e==null&&t!=="/"&&(p.pathname=p.pathname==="/"?t:Fa([t,p.pathname])),(d.replace?n.replace:n.push)(p,d.state,d)},[t,n,o,a,e])}w.createContext(null);function Uu(e,{relative:t}={}){let{matches:n}=w.useContext(Ha),{pathname:r}=xi(),a=JSON.stringify(Vz(n));return w.useMemo(()=>Wz(e,JSON.parse(a),r,t==="path"),[e,a,r,t])}function qq(e,t){return Qz(e,t)}function Qz(e,t,n,r){var k;Gt(Bu(),"useRoutes() may be used only in the context of a component.");let{navigator:a,static:o}=w.useContext(sa),{matches:s}=w.useContext(Ha),u=s[s.length-1],c=u?u.params:{},d=u?u.pathname:"/",p=u?u.pathnameBase:"/",g=u&&u.route;{let R=g&&g.path||"";Jz(d,!g||R.endsWith("*")||R.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${d}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. -Please change the parent to .`)}let m=xi(),b;if(t){let A=typeof t=="string"?wi(t):t;Gt(p==="/"||((k=A.pathname)==null?void 0:k.startsWith(p)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${p}" but pathname "${A.pathname}" was given in the \`location\` prop.`),b=A}else b=m;let y=b.pathname||"/",v=y;if(p!=="/"){let A=p.replace(/^\//,"").split("/");v="/"+y.replace(/^\//,"").split("/").slice(A.length).join("/")}let x=!o&&n&&n.matches&&n.matches.length>0?n.matches:Bz(e,{pathname:v});zr(g||x!=null,`No routes matched location "${b.pathname}${b.search}${b.hash}" `),zr(x==null||x[x.length-1].route.element!==void 0||x[x.length-1].route.Component!==void 0||x[x.length-1].route.lazy!==void 0,`Matched leaf route at location "${b.pathname}${b.search}${b.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let R=Yq(x&&x.map(A=>Object.assign({},A,{params:Object.assign({},c,A.params),pathname:Pa([p,a.encodeLocation?a.encodeLocation(A.pathname).pathname:A.pathname]),pathnameBase:A.pathnameBase==="/"?p:Pa([p,a.encodeLocation?a.encodeLocation(A.pathnameBase).pathname:A.pathnameBase])})),s,n,r);return t&&R?w.createElement(Bu.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...b},navigationType:"POP"}},R):R}function $q(){let e=Jq(),t=Fq(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:r},o={padding:"2px 4px",backgroundColor:r},s=null;return console.error("Error handled by React Router default ErrorBoundary:",e),s=w.createElement(w.Fragment,null,w.createElement("p",null,"💿 Hey developer 👋"),w.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",w.createElement("code",{style:o},"ErrorBoundary")," or"," ",w.createElement("code",{style:o},"errorElement")," prop on your route.")),w.createElement(w.Fragment,null,w.createElement("h2",null,"Unexpected Application Error!"),w.createElement("h3",{style:{fontStyle:"italic"}},t),n?w.createElement("pre",{style:a},n):null,s)}var qq=w.createElement($q,null),Vq=class extends w.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){console.error("React Router caught the following error during render",e,t)}render(){return this.state.error!==void 0?w.createElement(Ha.Provider,{value:this.props.routeContext},w.createElement(T0.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function Wq({routeContext:e,match:t,children:n}){let r=w.useContext(Us);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),w.createElement(Ha.Provider,{value:e},n)}function Yq(e,t=[],n=null,r=null){if(e==null){if(!n)return null;if(n.errors)e=n.matches;else if(t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let a=e,o=n==null?void 0:n.errors;if(o!=null){let c=a.findIndex(d=>d.route.id&&(o==null?void 0:o[d.route.id])!==void 0);Gt(c>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(o).join(",")}`),a=a.slice(0,Math.min(a.length,c+1))}let s=!1,u=-1;if(n)for(let c=0;c=0?a=a.slice(0,u+1):a=[a[0]];break}}}return a.reduceRight((c,d,p)=>{let g,m=!1,b=null,y=null;n&&(g=o&&d.route.id?o[d.route.id]:void 0,b=d.route.errorElement||qq,s&&(u<0&&p===0?(Kz("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),m=!0,y=null):u===p&&(m=!0,y=d.route.hydrateFallbackElement||null)));let v=t.concat(a.slice(0,p+1)),x=()=>{let R;return g?R=b:m?R=y:d.route.Component?R=w.createElement(d.route.Component,null):d.route.element?R=d.route.element:R=c,w.createElement(Wq,{match:d,routeContext:{outlet:c,matches:v,isDataRoute:n!=null},children:R})};return n&&(d.route.ErrorBoundary||d.route.errorElement||p===0)?w.createElement(Vq,{location:n.location,revalidation:n.revalidation,component:b,error:g,children:x(),routeContext:{outlet:null,matches:v,isDataRoute:!0}}):x()},null)}function R0(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Kq(e){let t=w.useContext(Us);return Gt(t,R0(e)),t}function Xq(e){let t=w.useContext($f);return Gt(t,R0(e)),t}function Zq(e){let t=w.useContext(Ha);return Gt(t,R0(e)),t}function _0(e){let t=Zq(e),n=t.matches[t.matches.length-1];return Gt(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}function Qq(){return _0("useRouteId")}function Jq(){var r;let e=w.useContext(T0),t=Xq("useRouteError"),n=_0("useRouteError");return e!==void 0?e:(r=t.errors)==null?void 0:r[n]}function e9(){let{router:e}=Kq("useNavigate"),t=_0("useNavigate"),n=w.useRef(!1);return Wz(()=>{n.current=!0}),w.useCallback(async(a,o={})=>{zr(n.current,Vz),n.current&&(typeof a=="number"?e.navigate(a):await e.navigate(a,{fromRouteId:t,...o}))},[e,t])}var sC={};function Kz(e,t,n){!t&&!sC[e]&&(sC[e]=!0,zr(!1,n))}w.memo(t9);function t9({routes:e,future:t,state:n}){return Yz(e,void 0,n,t)}function lk(e){Gt(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function n9({basename:e="/",children:t=null,location:n,navigationType:r="POP",navigator:a,static:o=!1}){Gt(!Uu(),"You cannot render a inside another . You should never have more than one in your app.");let s=e.replace(/^\/*/,"/"),u=w.useMemo(()=>({basename:s,navigator:a,static:o,future:{}}),[s,a,o]);typeof n=="string"&&(n=wi(n));let{pathname:c="/",search:d="",hash:p="",state:g=null,key:m="default"}=n,b=w.useMemo(()=>{let y=za(c,s);return y==null?null:{location:{pathname:y,search:d,hash:p,state:g,key:m},navigationType:r}},[s,c,d,p,g,m,r]);return zr(b!=null,` is not able to match the URL "${c}${d}${p}" because it does not start with the basename, so the won't render anything.`),b==null?null:w.createElement(ia.Provider,{value:u},w.createElement(Bu.Provider,{children:t,value:b}))}function r9({children:e,location:t}){return Hq(uk(e),t)}function uk(e,t=[]){let n=[];return w.Children.forEach(e,(r,a)=>{if(!w.isValidElement(r))return;let o=[...t,a];if(r.type===w.Fragment){n.push.apply(n,uk(r.props.children,o));return}Gt(r.type===lk,`[${typeof r.type=="string"?r.type:r.type.name}] is not a component. All component children of must be a or `),Gt(!r.props.index||!r.props.children,"An index route cannot have child routes.");let s={id:r.props.id||o.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,hydrateFallbackElement:r.props.hydrateFallbackElement,HydrateFallback:r.props.HydrateFallback,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.hasErrorBoundary===!0||r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(s.children=uk(r.props.children,o)),n.push(s)}),n}var qd="get",Vd="application/x-www-form-urlencoded";function qf(e){return e!=null&&typeof e.tagName=="string"}function a9(e){return qf(e)&&e.tagName.toLowerCase()==="button"}function o9(e){return qf(e)&&e.tagName.toLowerCase()==="form"}function i9(e){return qf(e)&&e.tagName.toLowerCase()==="input"}function s9(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function l9(e,t){return e.button===0&&(!t||t==="_self")&&!s9(e)}var gd=null;function u9(){if(gd===null)try{new FormData(document.createElement("form"),0),gd=!1}catch{gd=!0}return gd}var c9=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Qh(e){return e!=null&&!c9.has(e)?(zr(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Vd}"`),null):e}function d9(e,t){let n,r,a,o,s;if(o9(e)){let u=e.getAttribute("action");r=u?za(u,t):null,n=e.getAttribute("method")||qd,a=Qh(e.getAttribute("enctype"))||Vd,o=new FormData(e)}else if(a9(e)||i9(e)&&(e.type==="submit"||e.type==="image")){let u=e.form;if(u==null)throw new Error('Cannot submit a + +
+
+ + ) => setConfirmText(e.target.value)} + placeholder={t('documentPanel.clearDocuments.confirmPlaceholder')} + className="w-full" + /> +
+ +
+ setClearCacheOption(checked === true)} + /> + +
+
+ + + + + ) diff --git a/lightrag_webui/src/components/documents/UploadDocumentsDialog.tsx b/lightrag_webui/src/components/documents/UploadDocumentsDialog.tsx index 977c4030..5785a7d3 100644 --- a/lightrag_webui/src/components/documents/UploadDocumentsDialog.tsx +++ b/lightrag_webui/src/components/documents/UploadDocumentsDialog.tsx @@ -17,7 +17,11 @@ import { uploadDocument } from '@/api/lightrag' import { UploadIcon } from 'lucide-react' import { useTranslation } from 'react-i18next' -export default function UploadDocumentsDialog() { +interface UploadDocumentsDialogProps { + onDocumentsUploaded?: () => Promise +} + +export default function UploadDocumentsDialog({ onDocumentsUploaded }: UploadDocumentsDialogProps) { const { t } = useTranslation() const [open, setOpen] = useState(false) const [isUploading, setIsUploading] = useState(false) @@ -55,6 +59,7 @@ export default function UploadDocumentsDialog() { const handleDocumentsUpload = useCallback( async (filesToUpload: File[]) => { setIsUploading(true) + let hasSuccessfulUpload = false // Only clear errors for files that are being uploaded, keep errors for rejected files setFileErrors(prev => { @@ -101,6 +106,9 @@ export default function UploadDocumentsDialog() { ...prev, [file.name]: result.message })) + } else { + // Mark that we had at least one successful upload + hasSuccessfulUpload = true } } catch (err) { console.error(`Upload failed for ${file.name}:`, err) @@ -142,6 +150,16 @@ export default function UploadDocumentsDialog() { } else { toast.success(t('documentPanel.uploadDocuments.batch.success'), { id: toastId }) } + + // Only update if at least one file was uploaded successfully + if (hasSuccessfulUpload) { + // Refresh document list + if (onDocumentsUploaded) { + onDocumentsUploaded().catch(err => { + console.error('Error refreshing documents:', err) + }) + } + } } catch (err) { console.error('Unexpected error during upload:', err) toast.error(t('documentPanel.uploadDocuments.generalError', { error: errorMessage(err) }), { id: toastId }) @@ -149,7 +167,7 @@ export default function UploadDocumentsDialog() { setIsUploading(false) } }, - [setIsUploading, setProgresses, setFileErrors, t] + [setIsUploading, setProgresses, setFileErrors, t, onDocumentsUploaded] ) return ( diff --git a/lightrag_webui/src/components/graph/Settings.tsx b/lightrag_webui/src/components/graph/Settings.tsx index 1989a01e..e4085cef 100644 --- a/lightrag_webui/src/components/graph/Settings.tsx +++ b/lightrag_webui/src/components/graph/Settings.tsx @@ -8,7 +8,7 @@ import Input from '@/components/ui/Input' import { controlButtonVariant } from '@/lib/constants' import { useSettingsStore } from '@/stores/settings' -import { SettingsIcon } from 'lucide-react' +import { SettingsIcon, Undo2 } from 'lucide-react' import { useTranslation } from 'react-i18next'; /** @@ -44,14 +44,17 @@ const LabeledNumberInput = ({ onEditFinished, label, min, - max + max, + defaultValue }: { value: number onEditFinished: (value: number) => void label: string min: number max?: number + defaultValue?: number }) => { + const { t } = useTranslation(); const [currentValue, setCurrentValue] = useState(value) const onValueChange = useCallback( @@ -81,6 +84,13 @@ const LabeledNumberInput = ({ } }, [value, currentValue, onEditFinished]) + const handleReset = useCallback(() => { + if (defaultValue !== undefined && value !== defaultValue) { + setCurrentValue(defaultValue) + onEditFinished(defaultValue) + } + }, [defaultValue, value, onEditFinished]) + return (
- { - if (e.key === 'Enter') { - onBlur() - } - }} - /> +
+ { + if (e.key === 'Enter') { + onBlur() + } + }} + /> + {defaultValue !== undefined && ( + + )} +
) } @@ -121,7 +145,7 @@ export default function Settings() { const enableHideUnselectedEdges = useSettingsStore.use.enableHideUnselectedEdges() const showEdgeLabel = useSettingsStore.use.showEdgeLabel() const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth() - const graphMinDegree = useSettingsStore.use.graphMinDegree() + const graphMaxNodes = useSettingsStore.use.graphMaxNodes() const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations() const enableHealthCheck = useSettingsStore.use.enableHealthCheck() @@ -180,15 +204,14 @@ export default function Settings() { }, 300) }, []) - const setGraphMinDegree = useCallback((degree: number) => { - if (degree < 0) return - useSettingsStore.setState({ graphMinDegree: degree }) + const setGraphMaxNodes = useCallback((nodes: number) => { + if (nodes < 1 || nodes > 1000) return + useSettingsStore.setState({ graphMaxNodes: nodes }) const currentLabel = useSettingsStore.getState().queryLabel useSettingsStore.getState().setQueryLabel('') setTimeout(() => { useSettingsStore.getState().setQueryLabel(currentLabel) }, 300) - }, []) const setGraphLayoutMaxIterations = useCallback((iterations: number) => { @@ -274,19 +297,23 @@ export default function Settings() { label={t('graphPanel.sideBar.settings.maxQueryDepth')} min={1} value={graphQueryMaxDepth} + defaultValue={3} onEditFinished={setGraphQueryMaxDepth} /> diff --git a/lightrag_webui/src/components/graph/SettingsDisplay.tsx b/lightrag_webui/src/components/graph/SettingsDisplay.tsx index dec44c11..93fc0e01 100644 --- a/lightrag_webui/src/components/graph/SettingsDisplay.tsx +++ b/lightrag_webui/src/components/graph/SettingsDisplay.tsx @@ -8,12 +8,12 @@ import { useTranslation } from 'react-i18next' const SettingsDisplay = () => { const { t } = useTranslation() const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth() - const graphMinDegree = useSettingsStore.use.graphMinDegree() + const graphMaxNodes = useSettingsStore.use.graphMaxNodes() return (
{t('graphPanel.sideBar.settings.depth')}: {graphQueryMaxDepth}
-
{t('graphPanel.sideBar.settings.degree')}: {graphMinDegree}
+
{t('graphPanel.sideBar.settings.max')}: {graphMaxNodes}
) } diff --git a/lightrag_webui/src/components/status/StatusCard.tsx b/lightrag_webui/src/components/status/StatusCard.tsx index e67cbd30..c9e64db9 100644 --- a/lightrag_webui/src/components/status/StatusCard.tsx +++ b/lightrag_webui/src/components/status/StatusCard.tsx @@ -4,14 +4,14 @@ import { useTranslation } from 'react-i18next' const StatusCard = ({ status }: { status: LightragStatus | null }) => { const { t } = useTranslation() if (!status) { - return
{t('graphPanel.statusCard.unavailable')}
+ return
{t('graphPanel.statusCard.unavailable')}
} return ( -
+

{t('graphPanel.statusCard.storageInfo')}

-
+
{t('graphPanel.statusCard.workingDirectory')}: {status.working_directory} {t('graphPanel.statusCard.inputDirectory')}: @@ -21,7 +21,7 @@ const StatusCard = ({ status }: { status: LightragStatus | null }) => {

{t('graphPanel.statusCard.llmConfig')}

-
+
{t('graphPanel.statusCard.llmBinding')}: {status.configuration.llm_binding} {t('graphPanel.statusCard.llmBindingHost')}: @@ -35,7 +35,7 @@ const StatusCard = ({ status }: { status: LightragStatus | null }) => {

{t('graphPanel.statusCard.embeddingConfig')}

-
+
{t('graphPanel.statusCard.embeddingBinding')}: {status.configuration.embedding_binding} {t('graphPanel.statusCard.embeddingBindingHost')}: @@ -47,7 +47,7 @@ const StatusCard = ({ status }: { status: LightragStatus | null }) => {

{t('graphPanel.statusCard.storageConfig')}

-
+
{t('graphPanel.statusCard.kvStorage')}: {status.configuration.kv_storage} {t('graphPanel.statusCard.docStatusStorage')}: diff --git a/lightrag_webui/src/components/status/StatusDialog.tsx b/lightrag_webui/src/components/status/StatusDialog.tsx new file mode 100644 index 00000000..48eaa4f7 --- /dev/null +++ b/lightrag_webui/src/components/status/StatusDialog.tsx @@ -0,0 +1,32 @@ +import { LightragStatus } from '@/api/lightrag' +import { useTranslation } from 'react-i18next' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/components/ui/Dialog' +import StatusCard from './StatusCard' + +interface StatusDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + status: LightragStatus | null +} + +const StatusDialog = ({ open, onOpenChange, status }: StatusDialogProps) => { + const { t } = useTranslation() + + return ( + + + + {t('graphPanel.statusDialog.title')} + + + + + ) +} + +export default StatusDialog diff --git a/lightrag_webui/src/components/status/StatusIndicator.tsx b/lightrag_webui/src/components/status/StatusIndicator.tsx index 263bb99e..5a9fc751 100644 --- a/lightrag_webui/src/components/status/StatusIndicator.tsx +++ b/lightrag_webui/src/components/status/StatusIndicator.tsx @@ -1,8 +1,7 @@ import { cn } from '@/lib/utils' import { useBackendState } from '@/stores/state' import { useEffect, useState } from 'react' -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover' -import StatusCard from '@/components/status/StatusCard' +import StatusDialog from './StatusDialog' import { useTranslation } from 'react-i18next' const StatusIndicator = () => { @@ -11,6 +10,7 @@ const StatusIndicator = () => { const lastCheckTime = useBackendState.use.lastCheckTime() const status = useBackendState.use.status() const [animate, setAnimate] = useState(false) + const [dialogOpen, setDialogOpen] = useState(false) // listen to health change useEffect(() => { @@ -21,28 +21,30 @@ const StatusIndicator = () => { return (
- - -
-
- - {health ? t('graphPanel.statusIndicator.connected') : t('graphPanel.statusIndicator.disconnected')} - -
- - - - - +
setDialogOpen(true)} + > +
+ + {health ? t('graphPanel.statusIndicator.connected') : t('graphPanel.statusIndicator.disconnected')} + +
+ +
) } diff --git a/lightrag_webui/src/components/ui/Checkbox.tsx b/lightrag_webui/src/components/ui/Checkbox.tsx index 36ebe6e0..c9d4fafe 100644 --- a/lightrag_webui/src/components/ui/Checkbox.tsx +++ b/lightrag_webui/src/components/ui/Checkbox.tsx @@ -11,7 +11,7 @@ const Checkbox = React.forwardRef< >( { + const sortDocuments = useCallback((documents: DocStatusResponse[]) => { return [...documents].sort((a, b) => { let valueA, valueB; @@ -194,7 +194,7 @@ export default function DocumentManager() { return sortMultiplier * (valueA > valueB ? 1 : valueA < valueB ? -1 : 0); } }); - } + }, [sortField, sortDirection, showFileName]); const filteredAndSortedDocs = useMemo(() => { if (!docs) return null; @@ -223,7 +223,7 @@ export default function DocumentManager() { }, {} as DocsStatusesResponse['statuses']); return { ...filteredDocs, statuses: sortedStatuses }; - }, [docs, sortField, sortDirection, statusFilter]); + }, [docs, sortField, sortDirection, statusFilter, sortDocuments]); // Calculate document counts for each status const documentCounts = useMemo(() => { @@ -435,8 +435,8 @@ export default function DocumentManager() {
- - + + setStatusFilter('all')} + className={cn( + statusFilter === 'all' && 'bg-gray-100 dark:bg-gray-900 font-medium border border-gray-400 dark:border-gray-500 shadow-sm' + )} > {t('documentPanel.documentManager.status.all')} ({documentCounts.all}) @@ -461,7 +464,10 @@ export default function DocumentManager() { size="sm" variant={statusFilter === 'processed' ? 'secondary' : 'outline'} onClick={() => setStatusFilter('processed')} - className={documentCounts.processed > 0 ? 'text-green-600' : 'text-gray-500'} + className={cn( + documentCounts.processed > 0 ? 'text-green-600' : 'text-gray-500', + statusFilter === 'processed' && 'bg-green-100 dark:bg-green-900/30 font-medium border border-green-400 dark:border-green-600 shadow-sm' + )} > {t('documentPanel.documentManager.status.completed')} ({documentCounts.processed || 0}) @@ -469,7 +475,10 @@ export default function DocumentManager() { size="sm" variant={statusFilter === 'processing' ? 'secondary' : 'outline'} onClick={() => setStatusFilter('processing')} - className={documentCounts.processing > 0 ? 'text-blue-600' : 'text-gray-500'} + className={cn( + documentCounts.processing > 0 ? 'text-blue-600' : 'text-gray-500', + statusFilter === 'processing' && 'bg-blue-100 dark:bg-blue-900/30 font-medium border border-blue-400 dark:border-blue-600 shadow-sm' + )} > {t('documentPanel.documentManager.status.processing')} ({documentCounts.processing || 0}) @@ -477,7 +486,10 @@ export default function DocumentManager() { size="sm" variant={statusFilter === 'pending' ? 'secondary' : 'outline'} onClick={() => setStatusFilter('pending')} - className={documentCounts.pending > 0 ? 'text-yellow-600' : 'text-gray-500'} + className={cn( + documentCounts.pending > 0 ? 'text-yellow-600' : 'text-gray-500', + statusFilter === 'pending' && 'bg-yellow-100 dark:bg-yellow-900/30 font-medium border border-yellow-400 dark:border-yellow-600 shadow-sm' + )} > {t('documentPanel.documentManager.status.pending')} ({documentCounts.pending || 0}) @@ -485,7 +497,10 @@ export default function DocumentManager() { size="sm" variant={statusFilter === 'failed' ? 'secondary' : 'outline'} onClick={() => setStatusFilter('failed')} - className={documentCounts.failed > 0 ? 'text-red-600' : 'text-gray-500'} + className={cn( + documentCounts.failed > 0 ? 'text-red-600' : 'text-gray-500', + statusFilter === 'failed' && 'bg-red-100 dark:bg-red-900/30 font-medium border border-red-400 dark:border-red-600 shadow-sm' + )} > {t('documentPanel.documentManager.status.failed')} ({documentCounts.failed || 0}) diff --git a/lightrag_webui/src/features/SiteHeader.tsx b/lightrag_webui/src/features/SiteHeader.tsx index 8077d390..4881e4b6 100644 --- a/lightrag_webui/src/features/SiteHeader.tsx +++ b/lightrag_webui/src/features/SiteHeader.tsx @@ -8,6 +8,7 @@ import { cn } from '@/lib/utils' import { useTranslation } from 'react-i18next' import { navigationService } from '@/services/navigation' import { ZapIcon, GithubIcon, LogOutIcon } from 'lucide-react' +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/Tooltip' interface NavigationTabProps { value: string @@ -55,7 +56,7 @@ function TabsNavigation() { export default function SiteHeader() { const { t } = useTranslation() - const { isGuestMode, coreVersion, apiVersion, username } = useAuthStore() + const { isGuestMode, coreVersion, apiVersion, username, webuiTitle, webuiDescription } = useAuthStore() const versionDisplay = (coreVersion && apiVersion) ? `${coreVersion}/${apiVersion}` @@ -67,17 +68,31 @@ export default function SiteHeader() { return (
-
+
+ {webuiTitle && ( +
+ | + + + + + {webuiTitle} + + + {webuiDescription && ( + + {webuiDescription} + + )} + + +
+ )}
@@ -91,6 +106,11 @@ export default function SiteHeader() {