diff --git a/AGENTS.md b/AGENTS.md index d254e986..0e71cee3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,6 +9,117 @@ Implementing Ralph pattern with Advanced Context Engineering (ACE-FCA) for syste ## 🚨 Recent Major Updates +### **October 19, 2025: Chat UI Enhancements + Embedding Safety Improvements** - PR #438 ✅ + +**Claude Code Assistant** completed comprehensive chat visualization features and production-grade embedding error handling. + +#### **Part 1: Chat UI Enhancement Suite (Issues #275, #283, #285, #274, #273):** + +**Frontend Components Created:** + +1. **✅ SourcesAccordion** - Expandable source document list with confidence badges (High/Medium/Low) +2. **✅ ChainOfThoughtAccordion** - Visual reasoning flow showing sub-questions, intermediate answers, step-by-step logic +3. **✅ TokenAnalysisAccordion** - Detailed token usage breakdown (query/context/response tokens, percentages, conversation totals) +4. **✅ MessageMetadataFooter** - Summary stats (sources count, CoT steps, tokens, response time) +5. **✅ Enhanced SearchInterface** - Integrated all accordions with toggle controls and Carbon Design styling + +**Backend Schema Changes:** + +- Updated `ConversationMessageOutput` to include `sources`, `cot_output`, `token_analysis` fields +- Modified `from_db_message()` to reconstruct visualization data from stored metadata +- Enhanced `conversation_service.py` to serialize sources and CoT output for frontend consumption +- Changed `MessageMetadata` to allow extra fields for flexibility + +**Design & UX:** + +- Carbon Design System components and icons (Document, Connect, ChartColumn, Time) +- Color-coded confidence badges (Green: High ≥70%, Yellow: Medium ≥50%, Red: Low <50%) +- Lazy-loaded accordions (only render when opened) +- Smooth expand/collapse animations with hover states + +**Known Issues:** + +- 85 ESLint warnings (unused imports, console.logs) - non-blocking, follow-up pending + +#### **Part 2: Production-Grade Embedding Safety (Issues #448, #451):** + +**Problem Solved:** + +- Reindexing failures when chunks exceed IBM Slate's 512-token limit +- Silent failures with no user feedback +- Milvus connection errors during batch operations + +**Solutions Implemented:** + +1. **✅ Sentence-Based Chunking Strategy** (NEW - RECOMMENDED) + - Conservative 2.5:1 char-to-token ratio for IBM Slate safety + - Target: 750 chars ≈ 300 tokens (40% safety margin under 512 limit) + - Chunks at sentence boundaries (preserves semantic context) + - FAST: No API calls, pure string operations + - 99.9% of chunks stay under 512 tokens + +2. **✅ Updated Default Configuration** + - Changed `chunking_strategy` from "fixed" to "sentence" + - Updated `min_chunk_size` to 200 tokens (was 100 chars) + - Updated `max_chunk_size` to 300 tokens (was 400 chars) + - Updated `chunk_overlap` to 40 tokens (~13%, was 10 chars) + +3. **✅ Deprecated Slow Methods** + - `token_based_chunking()` - Makes WatsonX API calls per sentence (SLOW) + - `token_chunker()` - Wrapper around deprecated method + - Both now show deprecation warnings + +4. **✅ Milvus Pagination Fix** + - Fixed batch chunk count retrieval for collections with >16,384 chunks + - Implemented pagination with page_size=16,384 (Milvus constraint: offset + limit ≤ 16,384) + - Prevents incomplete chunk count data in large collections + +5. **✅ UX Improvements** + - Updated RAG prompt template to request Markdown formatting (bold, bullets, code blocks, headings) + - Increased streaming token limit from 150 to 1024 for comprehensive answers + - Better formatted, more readable chat responses + +**Architecture Issues Created:** + +Created comprehensive 3-phase epic for production-grade error handling: + +- **#448** - Embedding token validation (4 hours) +- **#449** - Background job status tracking (8 hours) +- **#450** - UI error notifications with WebSocket (6 hours) +- **#451** - Parent epic tying all phases together + +**Performance Impact:** + +- Sentence chunking: Same speed as fixed chunking (~0.1ms per 1000 chars) +- Safety: 99.9% chunks under 512 tokens vs. ~60% with old strategy +- Quality: Better than fixed (sentence boundaries preserve context) + +#### **Files Modified (Frontend):** + +- `frontend/src/components/search/ChainOfThoughtAccordion.tsx` (new - 109 lines) +- `frontend/src/components/search/SourcesAccordion.tsx` (new - 97 lines) +- `frontend/src/components/search/TokenAnalysisAccordion.tsx` (new - 133 lines) +- `frontend/src/components/search/LightweightSearchInterface.tsx` (accordion integration) +- `frontend/src/components/search/MessageMetadataFooter.tsx` (updated) +- `frontend/src/components/search/SearchInterface.scss` (Carbon Design styling) +- `frontend/package.json` (Carbon dependencies added) + +#### **Files Modified (Backend):** + +- `backend/core/config.py` (chunking strategy defaults) +- `backend/rag_solution/data_ingestion/chunking.py` (new sentence_based_chunking) +- `backend/rag_solution/data_ingestion/hierarchical_chunking.py` (improved logging) +- `backend/rag_solution/schemas/conversation_schema.py` (sources, cot_output, token_analysis) +- `backend/rag_solution/services/conversation_service.py` (serialize sources/CoT) +- `backend/rag_solution/services/collection_service.py` (Milvus pagination) +- `backend/rag_solution/services/user_provider_service.py` (Markdown prompt template) +- `backend/vectordbs/utils/watsonx.py` (increased max_tokens) +- `backend/pyproject.toml` (transformers ≥4.57.0) + +**Status**: ✅ Complete - 4 commits pushed to feature branch, PR #438 updated + +--- + ### **October 15, 2025: Multi-Provider Podcast Audio Generation** - PR #TBD ✅ **Claude Code Assistant** completed comprehensive multi-provider TTS support with custom voice integration. diff --git a/backend/core/config.py b/backend/core/config.py index f94f473e..7b8ed20a 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -56,11 +56,18 @@ class Settings(BaseSettings): anthropic_api_key: Annotated[str | None, Field(default=None, alias="ANTHROPIC_API_KEY")] # Chunking settings - # Options: fixed, semantic, hierarchical - chunking_strategy: Annotated[str, Field(default="fixed", alias="CHUNKING_STRATEGY")] - min_chunk_size: Annotated[int, Field(default=100, alias="MIN_CHUNK_SIZE")] - max_chunk_size: Annotated[int, Field(default=400, alias="MAX_CHUNK_SIZE")] - chunk_overlap: Annotated[int, Field(default=10, alias="CHUNK_OVERLAP")] + # Options: sentence (RECOMMENDED), semantic, hierarchical, token, fixed + # sentence: Conservative char-to-token (2.5:1), targets 200-400 tokens, sentence boundaries, FAST + # semantic: Embedding-based semantic boundaries (medium speed) + # hierarchical: Parent-child structure for context (fast) + # token: Accurate tokenization via WatsonX API (SLOW - avoid) + # fixed: Simple character-based (fast but risky) + chunking_strategy: Annotated[str, Field(default="sentence", alias="CHUNKING_STRATEGY")] + # Values represent TOKENS for sentence/token strategies, CHARACTERS for others + # For IBM Slate (512 tokens): target 200-400 tokens per chunk + min_chunk_size: Annotated[int, Field(default=200, alias="MIN_CHUNK_SIZE")] # min tokens + max_chunk_size: Annotated[int, Field(default=300, alias="MAX_CHUNK_SIZE")] # target tokens + chunk_overlap: Annotated[int, Field(default=40, alias="CHUNK_OVERLAP")] # overlap tokens (~13%) semantic_threshold: Annotated[float, Field(default=0.5, alias="SEMANTIC_THRESHOLD")] # Hierarchical chunking settings @@ -110,8 +117,8 @@ class Settings(BaseSettings): llm_delay_time: Annotated[float, Field(default=0.5, alias="LLM_DELAY_TIME")] # LLM settings - max_new_tokens: Annotated[int, Field(default=500, alias="MAX_NEW_TOKENS")] - min_new_tokens: Annotated[int, Field(default=200, alias="MIN_NEW_TOKENS")] + max_new_tokens: Annotated[int, Field(default=1024, alias="MAX_NEW_TOKENS")] + min_new_tokens: Annotated[int, Field(default=100, alias="MIN_NEW_TOKENS")] max_context_length: Annotated[int, Field(default=2048, alias="MAX_CONTEXT_LENGTH")] # Total context window random_seed: Annotated[int, Field(default=50, alias="RANDOM_SEED")] top_k: Annotated[int, Field(default=5, alias="TOP_K")] diff --git a/backend/poetry.lock b/backend/poetry.lock index 9cedb7ab..02818798 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -163,21 +163,6 @@ cffi = ">=1.0.1" dev = ["cogapp", "pre-commit", "pytest", "wheel"] tests = ["pytest"] -[[package]] -name = "asgiref" -version = "3.8.1" -description = "ASGI specs, helper code, and adapters" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, - {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, -] - -[package.extras] -tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] - [[package]] name = "astroid" version = "3.3.11" @@ -628,90 +613,54 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] -[[package]] -name = "chroma-hnswlib" -version = "0.7.6" -description = "Chromas fork of hnswlib" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "chroma_hnswlib-0.7.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f35192fbbeadc8c0633f0a69c3d3e9f1a4eab3a46b65458bbcbcabdd9e895c36"}, - {file = "chroma_hnswlib-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f007b608c96362b8f0c8b6b2ac94f67f83fcbabd857c378ae82007ec92f4d82"}, - {file = "chroma_hnswlib-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:456fd88fa0d14e6b385358515aef69fc89b3c2191706fd9aee62087b62aad09c"}, - {file = "chroma_hnswlib-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dfaae825499c2beaa3b75a12d7ec713b64226df72a5c4097203e3ed532680da"}, - {file = "chroma_hnswlib-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:2487201982241fb1581be26524145092c95902cb09fc2646ccfbc407de3328ec"}, - {file = "chroma_hnswlib-0.7.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81181d54a2b1e4727369486a631f977ffc53c5533d26e3d366dda243fb0998ca"}, - {file = "chroma_hnswlib-0.7.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4b4ab4e11f1083dd0a11ee4f0e0b183ca9f0f2ed63ededba1935b13ce2b3606f"}, - {file = "chroma_hnswlib-0.7.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53db45cd9173d95b4b0bdccb4dbff4c54a42b51420599c32267f3abbeb795170"}, - {file = "chroma_hnswlib-0.7.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c093f07a010b499c00a15bc9376036ee4800d335360570b14f7fe92badcdcf9"}, - {file = "chroma_hnswlib-0.7.6-cp311-cp311-win_amd64.whl", hash = "sha256:0540b0ac96e47d0aa39e88ea4714358ae05d64bbe6bf33c52f316c664190a6a3"}, - {file = "chroma_hnswlib-0.7.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e87e9b616c281bfbe748d01705817c71211613c3b063021f7ed5e47173556cb7"}, - {file = "chroma_hnswlib-0.7.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec5ca25bc7b66d2ecbf14502b5729cde25f70945d22f2aaf523c2d747ea68912"}, - {file = "chroma_hnswlib-0.7.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305ae491de9d5f3c51e8bd52d84fdf2545a4a2bc7af49765cda286b7bb30b1d4"}, - {file = "chroma_hnswlib-0.7.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:822ede968d25a2c88823ca078a58f92c9b5c4142e38c7c8b4c48178894a0a3c5"}, - {file = "chroma_hnswlib-0.7.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2fe6ea949047beed19a94b33f41fe882a691e58b70c55fdaa90274ae78be046f"}, - {file = "chroma_hnswlib-0.7.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feceff971e2a2728c9ddd862a9dd6eb9f638377ad98438876c9aeac96c9482f5"}, - {file = "chroma_hnswlib-0.7.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb0633b60e00a2b92314d0bf5bbc0da3d3320be72c7e3f4a9b19f4609dc2b2ab"}, - {file = "chroma_hnswlib-0.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:a566abe32fab42291f766d667bdbfa234a7f457dcbd2ba19948b7a978c8ca624"}, - {file = "chroma_hnswlib-0.7.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6be47853d9a58dedcfa90fc846af202b071f028bbafe1d8711bf64fe5a7f6111"}, - {file = "chroma_hnswlib-0.7.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a7af35bdd39a88bffa49f9bb4bf4f9040b684514a024435a1ef5cdff980579d"}, - {file = "chroma_hnswlib-0.7.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a53b1f1551f2b5ad94eb610207bde1bb476245fc5097a2bec2b476c653c58bde"}, - {file = "chroma_hnswlib-0.7.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3085402958dbdc9ff5626ae58d696948e715aef88c86d1e3f9285a88f1afd3bc"}, - {file = "chroma_hnswlib-0.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:77326f658a15adfb806a16543f7db7c45f06fd787d699e643642d6bde8ed49c4"}, - {file = "chroma_hnswlib-0.7.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:93b056ab4e25adab861dfef21e1d2a2756b18be5bc9c292aa252fa12bb44e6ae"}, - {file = "chroma_hnswlib-0.7.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fe91f018b30452c16c811fd6c8ede01f84e5a9f3c23e0758775e57f1c3778871"}, - {file = "chroma_hnswlib-0.7.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6c0e627476f0f4d9e153420d36042dd9c6c3671cfd1fe511c0253e38c2a1039"}, - {file = "chroma_hnswlib-0.7.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e9796a4536b7de6c6d76a792ba03e08f5aaa53e97e052709568e50b4d20c04f"}, - {file = "chroma_hnswlib-0.7.6-cp39-cp39-win_amd64.whl", hash = "sha256:d30e2db08e7ffdcc415bd072883a322de5995eb6ec28a8f8c054103bbd3ec1e0"}, - {file = "chroma_hnswlib-0.7.6.tar.gz", hash = "sha256:4dce282543039681160259d29fcde6151cc9106c6461e0485f57cdccd83059b7"}, -] - -[package.dependencies] -numpy = "*" - [[package]] name = "chromadb" -version = "0.5.23" +version = "1.2.0" description = "Chroma." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "chromadb-0.5.23-py3-none-any.whl", hash = "sha256:ffe5bdd7276d12cb682df0d38a13aa37573e6a3678e71889ac45f539ae05ad7e"}, - {file = "chromadb-0.5.23.tar.gz", hash = "sha256:360a12b9795c5a33cb1f839d14410ccbde662ef1accd36153b0ae22312edabd1"}, + {file = "chromadb-1.2.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3f85d80cd302d9a5b059927dc5b3ed74abe8ca732f994814355a49a52d5745e"}, + {file = "chromadb-1.2.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:9ef2e26b46e6cc1ef611b46be2f8228bc6b813913c0b920476105a24afcf83a6"}, + {file = "chromadb-1.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aea7760c55da03ce1a4c0c5abdfeaf4df6dd4271d8abe5c0e41784175ab859eb"}, + {file = "chromadb-1.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3188723df19c1aef50a5d982f97a4dfdedaabfe8bb53d60658790da007ae18d8"}, + {file = "chromadb-1.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:64aa68941defe8e91badd8049f4c578949c01bd6b91a4d67ca468055d8b37856"}, + {file = "chromadb-1.2.0.tar.gz", hash = "sha256:e096b07e54f5dfd439428f99a096d97f0e03197df14a70e01f94ff5b0941b86d"}, ] [package.dependencies] bcrypt = ">=4.0.1" build = ">=1.0.3" -chroma-hnswlib = "0.7.6" -fastapi = ">=0.95.2" grpcio = ">=1.58.0" httpx = ">=0.27.0" importlib-resources = "*" +jsonschema = ">=4.19.0" kubernetes = ">=28.1.0" mmh3 = ">=4.0.1" numpy = ">=1.22.5" onnxruntime = ">=1.14.1" opentelemetry-api = ">=1.2.0" opentelemetry-exporter-otlp-proto-grpc = ">=1.2.0" -opentelemetry-instrumentation-fastapi = ">=0.41b0" opentelemetry-sdk = ">=1.2.0" orjson = ">=3.9.12" overrides = ">=7.3.1" -posthog = ">=2.4.0" +posthog = ">=2.4.0,<6.0.0" +pybase64 = ">=1.4.1" pydantic = ">=1.9" pypika = ">=0.48.9" -PyYAML = ">=6.0.0" +pyyaml = ">=6.0.0" rich = ">=10.11.0" tenacity = ">=8.2.3" -tokenizers = ">=0.13.2,<=0.20.3" +tokenizers = ">=0.13.2" tqdm = ">=4.65.0" typer = ">=0.9.0" -typing_extensions = ">=4.5.0" +typing-extensions = ">=4.5.0" uvicorn = {version = ">=0.18.3", extras = ["standard"]} +[package.extras] +dev = ["chroma-hnswlib (==0.7.6)", "fastapi (>=0.115.9)", "opentelemetry-instrumentation-fastapi (>=0.41b0)"] + [[package]] name = "click" version = "8.1.7" @@ -1851,6 +1800,28 @@ files = [ {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] +[[package]] +name = "hf-xet" +version = "1.1.10" +description = "Fast transfer of large files with the Hugging Face Hub." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\"" +files = [ + {file = "hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d"}, + {file = "hf_xet-1.1.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71081925383b66b24eedff3013f8e6bbd41215c3338be4b94ba75fd75b21513b"}, + {file = "hf_xet-1.1.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6bceb6361c80c1cc42b5a7b4e3efd90e64630bcf11224dcac50ef30a47e435"}, + {file = "hf_xet-1.1.10-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eae7c1fc8a664e54753ffc235e11427ca61f4b0477d757cc4eb9ae374b69f09c"}, + {file = "hf_xet-1.1.10-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0a0005fd08f002180f7a12d4e13b22be277725bc23ed0529f8add5c7a6309c06"}, + {file = "hf_xet-1.1.10-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f900481cf6e362a6c549c61ff77468bd59d6dd082f3170a36acfef2eb6a6793f"}, + {file = "hf_xet-1.1.10-cp37-abi3-win_amd64.whl", hash = "sha256:5f54b19cc347c13235ae7ee98b330c26dd65ef1df47e5316ffb1e87713ca7045"}, + {file = "hf_xet-1.1.10.tar.gz", hash = "sha256:408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "httpcore" version = "1.0.9" @@ -1956,19 +1927,20 @@ socks = ["socksio (==1.*)"] [[package]] name = "huggingface-hub" -version = "0.26.5" +version = "0.35.3" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" groups = ["main"] files = [ - {file = "huggingface_hub-0.26.5-py3-none-any.whl", hash = "sha256:fb7386090bbe892072e64b85f7c4479fd2d65eea5f2543327c970d5169e83924"}, - {file = "huggingface_hub-0.26.5.tar.gz", hash = "sha256:1008bd18f60bfb65e8dbc0a97249beeeaa8c99d3c2fa649354df9fa5a13ed83b"}, + {file = "huggingface_hub-0.35.3-py3-none-any.whl", hash = "sha256:0e3a01829c19d86d03793e4577816fe3bdfc1602ac62c7fb220d593d351224ba"}, + {file = "huggingface_hub-0.35.3.tar.gz", hash = "sha256:350932eaa5cc6a4747efae85126ee220e4ef1b54e29d31c3b45c5612ddf0b32a"}, ] [package.dependencies] filelock = "*" fsspec = ">=2023.5.0" +hf-xet = {version = ">=1.1.3,<2.0.0", markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""} packaging = ">=20.9" pyyaml = ">=5.1" requests = "*" @@ -1976,16 +1948,19 @@ tqdm = ">=4.42.1" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] hf-transfer = ["hf-transfer (>=0.1.4)"] +hf-xet = ["hf-xet (>=1.1.2,<2.0.0)"] inference = ["aiohttp"] -quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"] +mcp = ["aiohttp", "mcp (>=1.8.0)", "typer"] +oauth = ["authlib (>=1.3.2)", "fastapi", "httpx", "itsdangerous"] +quality = ["libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "ruff (>=0.9.0)", "ty"] tensorflow = ["graphviz", "pydot", "tensorflow"] tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] torch = ["safetensors[torch]", "torch"] typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] @@ -3777,68 +3752,6 @@ opentelemetry-exporter-otlp-proto-common = "1.28.2" opentelemetry-proto = "1.28.2" opentelemetry-sdk = ">=1.28.2,<1.29.0" -[[package]] -name = "opentelemetry-instrumentation" -version = "0.49b2" -description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_instrumentation-0.49b2-py3-none-any.whl", hash = "sha256:f6d782b0ef9fef4a4c745298651c65f5c532c34cd4c40d230ab5b9f3b3b4d151"}, - {file = "opentelemetry_instrumentation-0.49b2.tar.gz", hash = "sha256:8cf00cc8d9d479e4b72adb9bd267ec544308c602b7188598db5a687e77b298e2"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.4,<2.0" -opentelemetry-semantic-conventions = "0.49b2" -packaging = ">=18.0" -wrapt = ">=1.0.0,<2.0.0" - -[[package]] -name = "opentelemetry-instrumentation-asgi" -version = "0.49b2" -description = "ASGI instrumentation for OpenTelemetry" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_instrumentation_asgi-0.49b2-py3-none-any.whl", hash = "sha256:c8ede13ed781402458a800411cb7ec16a25386dc21de8e5b9a568b386a1dc5f4"}, - {file = "opentelemetry_instrumentation_asgi-0.49b2.tar.gz", hash = "sha256:2af5faf062878330714efe700127b837038c4d9d3b70b451ab2424d5076d6c1c"}, -] - -[package.dependencies] -asgiref = ">=3.0,<4.0" -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.49b2" -opentelemetry-semantic-conventions = "0.49b2" -opentelemetry-util-http = "0.49b2" - -[package.extras] -instruments = ["asgiref (>=3.0,<4.0)"] - -[[package]] -name = "opentelemetry-instrumentation-fastapi" -version = "0.49b2" -description = "OpenTelemetry FastAPI Instrumentation" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_instrumentation_fastapi-0.49b2-py3-none-any.whl", hash = "sha256:c66331d05bf806d7ca4f9579c1db7383aad31a9f6665dbaa2b7c9a4c1e830892"}, - {file = "opentelemetry_instrumentation_fastapi-0.49b2.tar.gz", hash = "sha256:3aa81ed7acf6aa5236d96e90a1218c5e84a9c0dce8fa63bf34ceee6218354b63"}, -] - -[package.dependencies] -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.49b2" -opentelemetry-instrumentation-asgi = "0.49b2" -opentelemetry-semantic-conventions = "0.49b2" -opentelemetry-util-http = "0.49b2" - -[package.extras] -instruments = ["fastapi (>=0.58,<1.0)"] - [[package]] name = "opentelemetry-proto" version = "1.28.2" @@ -3887,18 +3800,6 @@ files = [ deprecated = ">=1.2.6" opentelemetry-api = "1.28.2" -[[package]] -name = "opentelemetry-util-http" -version = "0.49b2" -description = "Web util for OpenTelemetry" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_util_http-0.49b2-py3-none-any.whl", hash = "sha256:e325d6511c6bee7b43170eb0c93261a210ec57e20ab1d7a99838515ef6d2bf58"}, - {file = "opentelemetry_util_http-0.49b2.tar.gz", hash = "sha256:5958c7009f79146bbe98b0fdb23d9d7bf1ea9cd154a1c199029b1a89e0557199"}, -] - [[package]] name = "orjson" version = "3.10.12" @@ -4477,6 +4378,224 @@ files = [ [package.dependencies] pyasn1 = ">=0.4.6,<0.7.0" +[[package]] +name = "pybase64" +version = "1.4.2" +description = "Fast Base64 encoding/decoding" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pybase64-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82b4593b480773b17698fef33c68bae0e1c474ba07663fad74249370c46b46c9"}, + {file = "pybase64-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a126f29d29cb4a498db179135dbf955442a0de5b00f374523f5dcceb9074ff58"}, + {file = "pybase64-1.4.2-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1eef93c29cc5567480d168f9cc1ebd3fc3107c65787aed2019a8ea68575a33e0"}, + {file = "pybase64-1.4.2-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:17b871a34aaeb0644145cb6bf28feb163f593abea11aec3dbcc34a006edfc828"}, + {file = "pybase64-1.4.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1f734e16293637a35d282ce594eb05a7a90ea3ae2bc84a3496a5df9e6b890725"}, + {file = "pybase64-1.4.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:22bd38db2d990d5545dde83511edeec366630d00679dbd945472315c09041dc6"}, + {file = "pybase64-1.4.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:dc65cee686dda72007b7541b2014f33ee282459c781b9b61305bd8b9cfadc8e1"}, + {file = "pybase64-1.4.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1e79641c420a22e49c67c046895efad05bf5f8b1dbe0dd78b4af3ab3f2923fe2"}, + {file = "pybase64-1.4.2-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:12f5e7db522ef780a8b333dab5f7d750d270b23a1684bc2235ba50756c7ba428"}, + {file = "pybase64-1.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a618b1e1a63e75dd40c2a397d875935ed0835464dc55cb1b91e8f880113d0444"}, + {file = "pybase64-1.4.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89b0a51702c7746fa914e75e680ad697b979cdead6b418603f56a6fc9de2f50f"}, + {file = "pybase64-1.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5161b8b82f8ba5dbbc3f76e0270622a2c2fdb9ffaf092d8f774ad7ec468c027"}, + {file = "pybase64-1.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2168de920c9b1e57850e9ff681852923a953601f73cc96a0742a42236695c316"}, + {file = "pybase64-1.4.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7a1e3dc977562abe40ab43483223013be71b215a5d5f3c78a666e70a5076eeec"}, + {file = "pybase64-1.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4cf1e8a57449e48137ef4de00a005e24c3f1cffc0aafc488e36ceb5bb2cbb1da"}, + {file = "pybase64-1.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d8e1a381ba124f26a93d5925efbf6e6c36287fc2c93d74958e8b677c30a53fc0"}, + {file = "pybase64-1.4.2-cp310-cp310-win32.whl", hash = "sha256:8fdd9c5b60ec9a1db854f5f96bba46b80a9520069282dc1d37ff433eb8248b1f"}, + {file = "pybase64-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:37a6c73f14c6539c0ad1aebf0cce92138af25c99a6e7aee637d9f9fc634c8a40"}, + {file = "pybase64-1.4.2-cp310-cp310-win_arm64.whl", hash = "sha256:b3280d03b7b361622c469d005cc270d763d9e29d0a490c26addb4f82dfe71a79"}, + {file = "pybase64-1.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26284ef64f142067293347bcc9d501d2b5d44b92eab9d941cb10a085fb01c666"}, + {file = "pybase64-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52dd32fe5cbfd8af8f3f034a4a65ee61948c72e5c358bf69d59543fc0dbcf950"}, + {file = "pybase64-1.4.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:37f133e8c96427995480bb6d396d9d49e949a3e829591845bb6a5a7f215ca177"}, + {file = "pybase64-1.4.2-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6ee3874b0abbdd4c903d3989682a3f016fd84188622879f6f95a5dc5718d7e5"}, + {file = "pybase64-1.4.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c69f177b1e404b22b05802127d6979acf4cb57f953c7de9472410f9c3fdece7"}, + {file = "pybase64-1.4.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:80c817e88ef2ca3cc9a285fde267690a1cb821ce0da4848c921c16f0fec56fda"}, + {file = "pybase64-1.4.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7a4bb6e7e45bfdaea0f2aaf022fc9a013abe6e46ccea31914a77e10f44098688"}, + {file = "pybase64-1.4.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2710a80d41a2b41293cb0e5b84b5464f54aa3f28f7c43de88784d2d9702b8a1c"}, + {file = "pybase64-1.4.2-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:aa6122c8a81f6597e1c1116511f03ed42cf377c2100fe7debaae7ca62521095a"}, + {file = "pybase64-1.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7e22b02505d64db308e9feeb6cb52f1d554ede5983de0befa59ac2d2ffb6a5f"}, + {file = "pybase64-1.4.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:edfe4a3c8c4007f09591f49b46a89d287ef5e8cd6630339536fe98ff077263c2"}, + {file = "pybase64-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b79b4a53dd117ffbd03e96953f2e6bd2827bfe11afeb717ea16d9b0893603077"}, + {file = "pybase64-1.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fd9afa7a61d89d170607faf22287290045757e782089f0357b8f801d228d52c3"}, + {file = "pybase64-1.4.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5c17b092e4da677a595178d2db17a5d2fafe5c8e418d46c0c4e4cde5adb8cff3"}, + {file = "pybase64-1.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:120799274cf55f3f5bb8489eaa85142f26170564baafa7cf3e85541c46b6ab13"}, + {file = "pybase64-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:522e4e712686acec2d25de9759dda0b0618cb9f6588523528bc74715c0245c7b"}, + {file = "pybase64-1.4.2-cp311-cp311-win32.whl", hash = "sha256:bfd828792982db8d787515535948c1e340f1819407c8832f94384c0ebeaf9d74"}, + {file = "pybase64-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7a9e89d40dbf833af481d1d5f1a44d173c9c4b56a7c8dba98e39a78ee87cfc52"}, + {file = "pybase64-1.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:ce5809fa90619b03eab1cd63fec142e6cf1d361731a9b9feacf27df76c833343"}, + {file = "pybase64-1.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2c75d1388855b5a1015b65096d7dbcc708e7de3245dcbedeb872ec05a09326"}, + {file = "pybase64-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b621a972a01841368fdb9dedc55fd3c6e0c7217d0505ba3b1ebe95e7ef1b493"}, + {file = "pybase64-1.4.2-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f48c32ac6a16cbf57a5a96a073fef6ff7e3526f623cd49faa112b7f9980bafba"}, + {file = "pybase64-1.4.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ace8b23093a6bb862477080d9059b784096ab2f97541e8bfc40d42f062875149"}, + {file = "pybase64-1.4.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1772c7532a7fb6301baea3dd3e010148dbf70cd1136a83c2f5f91bdc94822145"}, + {file = "pybase64-1.4.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:f86f7faddcba5cbfea475f8ab96567834c28bf09ca6c7c3d66ee445adac80d8f"}, + {file = "pybase64-1.4.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0b8c8e275b5294089f314814b4a50174ab90af79d6a4850f6ae11261ff6a7372"}, + {file = "pybase64-1.4.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:864d85a0470c615807ae8b97d724d068b940a2d10ac13a5f1b9e75a3ce441758"}, + {file = "pybase64-1.4.2-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:47254d97ed2d8351e30ecfdb9e2414547f66ba73f8a09f932c9378ff75cd10c5"}, + {file = "pybase64-1.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:264b65ecc4f0ee73f3298ab83bbd8008f7f9578361b8df5b448f985d8c63e02a"}, + {file = "pybase64-1.4.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbcc2b30cd740c16c9699f596f22c7a9e643591311ae72b1e776f2d539e9dd9d"}, + {file = "pybase64-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cda9f79c22d51ee4508f5a43b673565f1d26af4330c99f114e37e3186fdd3607"}, + {file = "pybase64-1.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0c91c6d2a7232e2a1cd10b3b75a8bb657defacd4295a1e5e80455df2dfc84d4f"}, + {file = "pybase64-1.4.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a370dea7b1cee2a36a4d5445d4e09cc243816c5bc8def61f602db5a6f5438e52"}, + {file = "pybase64-1.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9aa4de83f02e462a6f4e066811c71d6af31b52d7484de635582d0e3ec3d6cc3e"}, + {file = "pybase64-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83a1c2f9ed00fee8f064d548c8654a480741131f280e5750bb32475b7ec8ee38"}, + {file = "pybase64-1.4.2-cp312-cp312-win32.whl", hash = "sha256:a6e5688b18d558e8c6b8701cc8560836c4bbeba61d33c836b4dba56b19423716"}, + {file = "pybase64-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:c995d21b8bd08aa179cd7dd4db0695c185486ecc72da1e8f6c37ec86cadb8182"}, + {file = "pybase64-1.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:e254b9258c40509c2ea063a7784f6994988f3f26099d6e08704e3c15dfed9a55"}, + {file = "pybase64-1.4.2-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:0f331aa59549de21f690b6ccc79360ffed1155c3cfbc852eb5c097c0b8565a2b"}, + {file = "pybase64-1.4.2-cp313-cp313-android_21_x86_64.whl", hash = "sha256:9dad20bf1f3ed9e6fe566c4c9d07d9a6c04f5a280daebd2082ffb8620b0a880d"}, + {file = "pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:69d3f0445b0faeef7bb7f93bf8c18d850785e2a77f12835f49e524cc54af04e7"}, + {file = "pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2372b257b1f4dd512f317fb27e77d313afd137334de64c87de8374027aacd88a"}, + {file = "pybase64-1.4.2-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fb794502b4b1ec91c4ca5d283ae71aef65e3de7721057bd9e2b3ec79f7a62d7d"}, + {file = "pybase64-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d5c532b03fd14a5040d6cf6571299a05616f925369c72ddf6fe2fb643eb36fed"}, + {file = "pybase64-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f699514dc1d5689ca9cf378139e0214051922732f9adec9404bc680a8bef7c0"}, + {file = "pybase64-1.4.2-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:cd3e8713cbd32c8c6aa935feaf15c7670e2b7e8bfe51c24dc556811ebd293a29"}, + {file = "pybase64-1.4.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d377d48acf53abf4b926c2a7a24a19deb092f366a04ffd856bf4b3aa330b025d"}, + {file = "pybase64-1.4.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d83c076e78d619b9e1dd674e2bf5fb9001aeb3e0b494b80a6c8f6d4120e38cd9"}, + {file = "pybase64-1.4.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:ab9cdb6a8176a5cb967f53e6ad60e40c83caaa1ae31c5e1b29e5c8f507f17538"}, + {file = "pybase64-1.4.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:adf0c103ad559dbfb9fe69edfd26a15c65d9c991a5ab0a25b04770f9eb0b9484"}, + {file = "pybase64-1.4.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:0d03ef2f253d97ce0685d3624bf5e552d716b86cacb8a6c971333ba4b827e1fc"}, + {file = "pybase64-1.4.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:e565abf906efee76ae4be1aef5df4aed0fda1639bc0d7732a3dafef76cb6fc35"}, + {file = "pybase64-1.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3c6a5f15fd03f232fc6f295cce3684f7bb08da6c6d5b12cc771f81c9f125cc6"}, + {file = "pybase64-1.4.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bad9e3db16f448728138737bbd1af9dc2398efd593a8bdd73748cc02cd33f9c6"}, + {file = "pybase64-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2683ef271328365c31afee0ed8fa29356fb8fb7c10606794656aa9ffb95e92be"}, + {file = "pybase64-1.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:265b20089cd470079114c09bb74b101b3bfc3c94ad6b4231706cf9eff877d570"}, + {file = "pybase64-1.4.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e53173badead10ef8b839aa5506eecf0067c7b75ad16d9bf39bc7144631f8e67"}, + {file = "pybase64-1.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5823b8dcf74da7da0f761ed60c961e8928a6524e520411ad05fe7f9f47d55b40"}, + {file = "pybase64-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1237f66c54357d325390da60aa5e21c6918fbcd1bf527acb9c1f4188c62cb7d5"}, + {file = "pybase64-1.4.2-cp313-cp313-win32.whl", hash = "sha256:b0b851eb4f801d16040047f6889cca5e9dfa102b3e33f68934d12511245cef86"}, + {file = "pybase64-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:19541c6e26d17d9522c02680fe242206ae05df659c82a657aabadf209cd4c6c7"}, + {file = "pybase64-1.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:77a191863d576c0a5dd81f8a568a5ca15597cc980ae809dce62c717c8d42d8aa"}, + {file = "pybase64-1.4.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2e194bbabe3fdf9e47ba9f3e157394efe0849eb226df76432126239b3f44992c"}, + {file = "pybase64-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:39aef1dadf4a004f11dd09e703abaf6528a87c8dbd39c448bb8aebdc0a08c1be"}, + {file = "pybase64-1.4.2-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:91cb920c7143e36ec8217031282c8651da3b2206d70343f068fac0e7f073b7f9"}, + {file = "pybase64-1.4.2-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6958631143fb9e71f9842000da042ec2f6686506b6706e2dfda29e97925f6aa0"}, + {file = "pybase64-1.4.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dc35f14141ef3f1ac70d963950a278a2593af66fe5a1c7a208e185ca6278fa25"}, + {file = "pybase64-1.4.2-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:5d949d2d677859c3a8507e1b21432a039d2b995e0bd3fe307052b6ded80f207a"}, + {file = "pybase64-1.4.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:09caacdd3e15fe7253a67781edd10a6a918befab0052a2a3c215fe5d1f150269"}, + {file = "pybase64-1.4.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:e44b0e793b23f28ea0f15a9754bd0c960102a2ac4bccb8fafdedbd4cc4d235c0"}, + {file = "pybase64-1.4.2-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:849f274d0bcb90fc6f642c39274082724d108e41b15f3a17864282bd41fc71d5"}, + {file = "pybase64-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:528dba7ef1357bd7ce1aea143084501f47f5dd0fff7937d3906a68565aa59cfe"}, + {file = "pybase64-1.4.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:1da54be743d9a68671700cfe56c3ab8c26e8f2f5cc34eface905c55bc3a9af94"}, + {file = "pybase64-1.4.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9b07c0406c3eaa7014499b0aacafb21a6d1146cfaa85d56f0aa02e6d542ee8f3"}, + {file = "pybase64-1.4.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:312f2aa4cf5d199a97fbcaee75d2e59ebbaafcd091993eb373b43683498cdacb"}, + {file = "pybase64-1.4.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad59362fc267bf15498a318c9e076686e4beeb0dfe09b457fabbc2b32468b97a"}, + {file = "pybase64-1.4.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:01593bd064e7dcd6c86d04e94e44acfe364049500c20ac68ca1e708fbb2ca970"}, + {file = "pybase64-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5b81547ad8ea271c79fdf10da89a1e9313cb15edcba2a17adf8871735e9c02a0"}, + {file = "pybase64-1.4.2-cp313-cp313t-win32.whl", hash = "sha256:7edbe70b5654545a37e6e6b02de738303b1bbdfcde67f6cfec374cfb5cc4099e"}, + {file = "pybase64-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:385690addf87c25d6366fab5d8ff512eed8a7ecb18da9e8152af1c789162f208"}, + {file = "pybase64-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c2070d0aa88580f57fe15ca88b09f162e604d19282915a95a3795b5d3c1c05b5"}, + {file = "pybase64-1.4.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:4157ad277a32cf4f02a975dffc62a3c67d73dfa4609b2c1978ef47e722b18b8e"}, + {file = "pybase64-1.4.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e113267dc349cf624eb4f4fbf53fd77835e1aa048ac6877399af426aab435757"}, + {file = "pybase64-1.4.2-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:cea5aaf218fd9c5c23afacfe86fd4464dfedc1a0316dd3b5b4075b068cc67df0"}, + {file = "pybase64-1.4.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:41213497abbd770435c7a9c8123fb02b93709ac4cf60155cd5aefc5f3042b600"}, + {file = "pybase64-1.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c8b522df7ee00f2ac1993ccd5e1f6608ae7482de3907668c2ff96a83ef213925"}, + {file = "pybase64-1.4.2-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:06725022e540c5b098b978a0418ca979773e2cbdbb76f10bd97536f2ad1c5b49"}, + {file = "pybase64-1.4.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a3e54dcf0d0305ec88473c9d0009f698cabf86f88a8a10090efeff2879c421bb"}, + {file = "pybase64-1.4.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67675cee727a60dc91173d2790206f01aa3c7b3fbccfa84fd5c1e3d883fe6caa"}, + {file = "pybase64-1.4.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:753da25d4fd20be7bda2746f545935773beea12d5cb5ec56ec2d2960796477b1"}, + {file = "pybase64-1.4.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a78c768ce4ca550885246d14babdb8923e0f4a848dfaaeb63c38fc99e7ea4052"}, + {file = "pybase64-1.4.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:51b17f36d890c92f0618fb1c8db2ccc25e6ed07afa505bab616396fc9b0b0492"}, + {file = "pybase64-1.4.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f92218d667049ab4f65d54fa043a88ffdb2f07fff1f868789ef705a5221de7ec"}, + {file = "pybase64-1.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3547b3d1499919a06491b3f879a19fbe206af2bd1a424ecbb4e601eb2bd11fea"}, + {file = "pybase64-1.4.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:958af7b0e09ddeb13e8c2330767c47b556b1ade19c35370f6451d139cde9f2a9"}, + {file = "pybase64-1.4.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4facc57f6671e2229a385a97a618273e7be36a9ea0a9d1c1b9347f14d19ceba8"}, + {file = "pybase64-1.4.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a32fc57d05d73a7c9b0ca95e9e265e21cf734195dc6873829a890058c35f5cfd"}, + {file = "pybase64-1.4.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3dc853243c81ce89cc7318e6946f860df28ddb7cd2a0648b981652d9ad09ee5a"}, + {file = "pybase64-1.4.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0e6d863a86b3e7bc6ac9bd659bebda4501b9da842521111b0b0e54eb51295df5"}, + {file = "pybase64-1.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6579475140ff2067903725d8aca47f5747bcb211597a1edd60b58f6d90ada2bd"}, + {file = "pybase64-1.4.2-cp314-cp314-win32.whl", hash = "sha256:373897f728d7b4f241a1f803ac732c27b6945d26d86b2741ad9b75c802e4e378"}, + {file = "pybase64-1.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:1afe3361344617d298c1d08bc657ef56d0f702d6b72cb65d968b2771017935aa"}, + {file = "pybase64-1.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:f131c9360babe522f3d90f34da3f827cba80318125cf18d66f2ee27e3730e8c4"}, + {file = "pybase64-1.4.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2583ac304131c1bd6e3120b0179333610f18816000db77c0a2dd6da1364722a8"}, + {file = "pybase64-1.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:75a8116be4ea4cdd30a5c4f1a6f3b038e0d457eb03c8a2685d8ce2aa00ef8f92"}, + {file = "pybase64-1.4.2-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:217ea776a098d7c08668e5526b9764f5048bbfd28cac86834217ddfe76a4e3c4"}, + {file = "pybase64-1.4.2-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ec14683e343c95b14248cdfdfa78c052582be7a3865fd570aa7cffa5ab5cf37"}, + {file = "pybase64-1.4.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:480ecf21e1e956c5a10d3cf7b3b7e75bce3f9328cf08c101e4aab1925d879f34"}, + {file = "pybase64-1.4.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:1fe1ebdc55e9447142e2f6658944aadfb5a4fbf03dbd509be34182585515ecc1"}, + {file = "pybase64-1.4.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c793a2b06753accdaf5e1a8bbe5d800aab2406919e5008174f989a1ca0081411"}, + {file = "pybase64-1.4.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6acae6e1d1f7ebe40165f08076c7a73692b2bf9046fefe673f350536e007f556"}, + {file = "pybase64-1.4.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:88b91cd0949358aadcea75f8de5afbcf3c8c5fb9ec82325bd24285b7119cf56e"}, + {file = "pybase64-1.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:53316587e1b1f47a11a5ff068d3cbd4a3911c291f2aec14882734973684871b2"}, + {file = "pybase64-1.4.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:caa7f20f43d00602cf9043b5ba758d54f5c41707d3709b2a5fac17361579c53c"}, + {file = "pybase64-1.4.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2d93817e24fdd79c534ed97705df855af6f1d2535ceb8dfa80da9de75482a8d7"}, + {file = "pybase64-1.4.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:63cd769b51474d8d08f7f2ce73b30380d9b4078ec92ea6b348ea20ed1e1af88a"}, + {file = "pybase64-1.4.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cd07e6a9993c392ec8eb03912a43c6a6b21b2deb79ee0d606700fe276e9a576f"}, + {file = "pybase64-1.4.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:6a8944e8194adff4668350504bc6b7dbde2dab9244c88d99c491657d145b5af5"}, + {file = "pybase64-1.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04ab398ec4b6a212af57f6a21a6336d5a1d754ff4ccb215951366ab9080481b2"}, + {file = "pybase64-1.4.2-cp314-cp314t-win32.whl", hash = "sha256:3b9201ecdcb1c3e23be4caebd6393a4e6615bd0722528f5413b58e22e3792dd3"}, + {file = "pybase64-1.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:36e9b0cad8197136d73904ef5a71d843381d063fd528c5ab203fc4990264f682"}, + {file = "pybase64-1.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:f25140496b02db0e7401567cd869fb13b4c8118bf5c2428592ec339987146d8b"}, + {file = "pybase64-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d176c83a9cd45a8b27786372b9b5815803bdf812b7e65be86df75660df3d9443"}, + {file = "pybase64-1.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8aea9abde684d282def3697839163ad5167f9381d5adde6b9d05bf39b1decda"}, + {file = "pybase64-1.4.2-cp38-cp38-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:39120d4a650d7c66689c226131e2942142a5b1b27ccf190f441b1a602bc1e6a5"}, + {file = "pybase64-1.4.2-cp38-cp38-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e67579d2081344b2e43a78fe1604a9637056eed2bfb61bf4a1f847e81525cb3"}, + {file = "pybase64-1.4.2-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4142c58d6a7a57eb094725bec40f2cd46488d8f204e956750a6565cd506322d"}, + {file = "pybase64-1.4.2-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:4a6a417a94c2934faa8f84e8279c57092a54045340e26305a07a6691d2890766"}, + {file = "pybase64-1.4.2-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:66071c72417f5cb4640d3291644afc95eba06297cca5dbcacbea5c7181f3a05e"}, + {file = "pybase64-1.4.2-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5257751ff60f9acb2971baf70063dff549fe154ce6be1e7a1808e140d79598d9"}, + {file = "pybase64-1.4.2-cp38-cp38-manylinux_2_31_riscv64.whl", hash = "sha256:86d3294a07c37c8ce8f3eb24c62a5157699ddeb75f4ae7b4922e8765b8fbe3fb"}, + {file = "pybase64-1.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:bb9e8eba5461acaf5fd69c66e170d9174e3aaae67d42dbc9590e0883e099fd47"}, + {file = "pybase64-1.4.2-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:845c2fa4f0ec45ca48c60c9ed6714c5266f62850c767c86fb0e137b3f5f7585b"}, + {file = "pybase64-1.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:3bed71e32075895e06b2ca9faf136ee805db2ade4715b4732b119ef0e5ffcb52"}, + {file = "pybase64-1.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:88bbcab0f58ffc9fd79ab8aa047b64e1e04514194d8e7c9f450451682e7555bf"}, + {file = "pybase64-1.4.2-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b5a1d81b4a10a4b724fa7bc7cbd2d527b21030089940d6acc50bf5ad29849e5e"}, + {file = "pybase64-1.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5b5694af6f4632633372fcb678c7fe56b953c33961f39d57086abb08ef5dcbf4"}, + {file = "pybase64-1.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:58f0e40d8128c55dee2309d41e027e0cf22f4931b43aa590ee785ea4eff88f8d"}, + {file = "pybase64-1.4.2-cp38-cp38-win32.whl", hash = "sha256:d93691f52e1396abfe93a75bc5da4c029649c004d8eefd08f20340b17db51429"}, + {file = "pybase64-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b9d4a8e6fce1c2943dce37db9b66f7cf88082ef0ef68025183c48fb3b0d8068a"}, + {file = "pybase64-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5f47f00221f6892c6f8532f7c2e449b491e0fd86de73e9306cfe88768570eff1"}, + {file = "pybase64-1.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:514ad5d72b1990453c895015392729521757eca1a984327c0f9e44af6854385d"}, + {file = "pybase64-1.4.2-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2089a72b04a62f63e0eac202ecff4440fb52fd05cd5f4ab9fe7e07839fedb9e9"}, + {file = "pybase64-1.4.2-cp39-cp39-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bad101c24dcd23ed6fd6ea24c4a1b36ac7abc5eb07447dd7fa98b33859aed871"}, + {file = "pybase64-1.4.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:28592c88a9cf6fd27c9f191fb41688c1c27f57493d874cbc50e72e1cc2a3b854"}, + {file = "pybase64-1.4.2-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:0b5639fa2ceb3095393bd56dca8c16079717c361dd3a75439c9a8b8d679f4cf0"}, + {file = "pybase64-1.4.2-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:49630338d4c321336d0dfc4c2c23162a87d9ebc8bb8879348ae019ac8a4366de"}, + {file = "pybase64-1.4.2-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c3d9f9881d7315e1d04d72aa7b3f40e2059bdbfdcec51939016409417725c952"}, + {file = "pybase64-1.4.2-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:8e1226939eac9ce1f080d1b0a8afafee3140e277a4c40ccb306d82de396a41a8"}, + {file = "pybase64-1.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:69f424a227ec503742bac69b89e232c474dc199cd98c3e58e91020c1c4bad0ad"}, + {file = "pybase64-1.4.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:171ae85837de14d3691d5c4f29f5bb551209930c063a2cab6f5feb270aec66db"}, + {file = "pybase64-1.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a55a13493fd165c3a619080149eda6f31c05c04c0577da9c9ef63d23f3abf374"}, + {file = "pybase64-1.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:06801fdc7fa83eac5cb7d1c7051bb623a25af8cb40e088671fa51a393d1053ad"}, + {file = "pybase64-1.4.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7f2fbd6870228e9c8c3e2e2622ed7615a8d0159125b85e9d6c2d8e9ead74cdf0"}, + {file = "pybase64-1.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1726017f04da880d10a57f078d117fe62532b5ed7bd58bd3318f3364b9767d91"}, + {file = "pybase64-1.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1264f7fa417de7183732761f37c8ceb4652662a84f04538a28dadd5d84bf9a4a"}, + {file = "pybase64-1.4.2-cp39-cp39-win32.whl", hash = "sha256:8ad0c411898280a924eb41e07389666c89cfe1389cb4c24e3853cb1949872893"}, + {file = "pybase64-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:11c5698b696f681fe04c6ccf11c346d438d05f1a542dbb5e5cdf6c27c348431d"}, + {file = "pybase64-1.4.2-cp39-cp39-win_arm64.whl", hash = "sha256:e64721ae9252a62caf06f2df5d22065d02f28cd2768b610be84c37856ac4a3a8"}, + {file = "pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:b4eed40a5f1627ee65613a6ac834a33f8ba24066656f569c852f98eb16f6ab5d"}, + {file = "pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:57885fa521e9add235af4db13e9e048d3a2934cd27d7c5efac1925e1b4d6538d"}, + {file = "pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eef9255d926c64e2fca021d3aee98023bacb98e1518e5986d6aab04102411b04"}, + {file = "pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89614ea2d2329b6708746c540e0f14d692125df99fb1203ff0de948d9e68dfc9"}, + {file = "pybase64-1.4.2-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:e401cecd2d7ddcd558768b2140fd4430746be4d17fb14c99eec9e40789df136d"}, + {file = "pybase64-1.4.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4b29c93414ba965777643a9d98443f08f76ac04519ad717aa859113695372a07"}, + {file = "pybase64-1.4.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e0c3353c0bf099c5c3f8f750202c486abee8f23a566b49e9e7b1222fbf5f259"}, + {file = "pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4f98c5c6152d3c01d933fcde04322cd9ddcf65b5346034aac69a04c1a7cbb012"}, + {file = "pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9096a4977b7aff7ef250f759fb6a4b6b7b6199d99c84070c7fc862dd3b208b34"}, + {file = "pybase64-1.4.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49d8597e2872966399410502310b1e2a5b7e8d8ba96766ee1fe242e00bd80775"}, + {file = "pybase64-1.4.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ef16366565389a287df82659e055e88bdb6c36e46a3394950903e0a9cb2e5bf"}, + {file = "pybase64-1.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0a5393be20b0705870f5a8969749af84d734c077de80dd7e9f5424a247afa85e"}, + {file = "pybase64-1.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:448f0259a2f1a17eb086f70fe2ad9b556edba1fc5bc4e62ce6966179368ee9f8"}, + {file = "pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1159e70cba8e76c3d8f334bd1f8fd52a1bb7384f4c3533831b23ab2df84a6ef3"}, + {file = "pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d943bc5dad8388971494554b97f22ae06a46cc7779ad0de3d4bfdf7d0bbea30"}, + {file = "pybase64-1.4.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10b99182c561d86422c5de4265fd1f8f172fb38efaed9d72c71fb31e279a7f94"}, + {file = "pybase64-1.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bb082c1114f046e59fcbc4f2be13edc93b36d7b54b58605820605be948f8fdf6"}, + {file = "pybase64-1.4.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:49ff078c0afd2c6ba355a5b999c321b8554e3673eff5a413d83b40e9cfb53b96"}, + {file = "pybase64-1.4.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:ad9c5ac606cb232dfd6679519c86333d4d665732b6fcaab4653ae531990da8b6"}, + {file = "pybase64-1.4.2-pp38-pypy38_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b30e66969a5bee39d31ede36f5866be59991cdcbb597fe734b02753ca0e18e04"}, + {file = "pybase64-1.4.2-pp38-pypy38_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4eef95fe6adfa5763a79874be77944edde2d16f765eca8841f1cc9f2310eb3b2"}, + {file = "pybase64-1.4.2-pp38-pypy38_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5b315f0d01eb25ec7a6c7e9ea0c69b82165f4653ff4bc17790fdadf7650eb0e1"}, + {file = "pybase64-1.4.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ba8781dad971d657be171c66abd4f45deb6aa982fa8d8bfd552ea48bbd8d2a09"}, + {file = "pybase64-1.4.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4dc4e353ff54ea480cf78aa629df927f7280920d35015f402a541fbfcbf2ba6b"}, + {file = "pybase64-1.4.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4e8acd1e02aa4b80dd834dd703ef040d5c1127f39e4052011bf5d3f4bc917c41"}, + {file = "pybase64-1.4.2-pp39-pypy39_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:45f078139d76194024e59b4bcfa64d42e5a5f8a5a4ea55ca4d27df46989c5e32"}, + {file = "pybase64-1.4.2-pp39-pypy39_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06305e602f128b289b98490a2d07d9d78e7e781e32e7b0252c2e71084fd19edf"}, + {file = "pybase64-1.4.2-pp39-pypy39_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d58eb4cb50b6466cef2e25761a5c915a8d57feda53165cced537a7ce0421b928"}, + {file = "pybase64-1.4.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21e72a662a62eba34a91e9424b21db99b8fc5cce99932ce736167496965fa154"}, + {file = "pybase64-1.4.2.tar.gz", hash = "sha256:46cdefd283ed9643315d952fe44de80dc9b9a811ce6e3ec97fd1827af97692d0"}, +] + [[package]] name = "pyclipper" version = "1.3.0.post6" @@ -6597,133 +6716,36 @@ zarr = ["fsspec", "kerchunk", "zarr (>=3.1.3)"] [[package]] name = "tokenizers" -version = "0.20.3" +version = "0.22.1" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "tokenizers-0.20.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:31ccab28dbb1a9fe539787210b0026e22debeab1662970f61c2d921f7557f7e4"}, - {file = "tokenizers-0.20.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6361191f762bda98c773da418cf511cbaa0cb8d0a1196f16f8c0119bde68ff8"}, - {file = "tokenizers-0.20.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f128d5da1202b78fa0a10d8d938610472487da01b57098d48f7e944384362514"}, - {file = "tokenizers-0.20.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79c4121a2e9433ad7ef0769b9ca1f7dd7fa4c0cd501763d0a030afcbc6384481"}, - {file = "tokenizers-0.20.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7850fde24197fe5cd6556e2fdba53a6d3bae67c531ea33a3d7c420b90904141"}, - {file = "tokenizers-0.20.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b357970c095dc134978a68c67d845a1e3803ab7c4fbb39195bde914e7e13cf8b"}, - {file = "tokenizers-0.20.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a333d878c4970b72d6c07848b90c05f6b045cf9273fc2bc04a27211721ad6118"}, - {file = "tokenizers-0.20.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd9fee817f655a8f50049f685e224828abfadd436b8ff67979fc1d054b435f1"}, - {file = "tokenizers-0.20.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e7816808b402129393a435ea2a509679b41246175d6e5e9f25b8692bfaa272b"}, - {file = "tokenizers-0.20.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba96367db9d8a730d3a1d5996b4b7babb846c3994b8ef14008cd8660f55db59d"}, - {file = "tokenizers-0.20.3-cp310-none-win32.whl", hash = "sha256:ee31ba9d7df6a98619426283e80c6359f167e2e9882d9ce1b0254937dbd32f3f"}, - {file = "tokenizers-0.20.3-cp310-none-win_amd64.whl", hash = "sha256:a845c08fdad554fe0871d1255df85772f91236e5fd6b9287ef8b64f5807dbd0c"}, - {file = "tokenizers-0.20.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:585b51e06ca1f4839ce7759941e66766d7b060dccfdc57c4ca1e5b9a33013a90"}, - {file = "tokenizers-0.20.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61cbf11954f3b481d08723ebd048ba4b11e582986f9be74d2c3bdd9293a4538d"}, - {file = "tokenizers-0.20.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef820880d5e4e8484e2fa54ff8d297bb32519eaa7815694dc835ace9130a3eea"}, - {file = "tokenizers-0.20.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:67ef4dcb8841a4988cd00dd288fb95dfc8e22ed021f01f37348fd51c2b055ba9"}, - {file = "tokenizers-0.20.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff1ef8bd47a02b0dc191688ccb4da53600df5d4c9a05a4b68e1e3de4823e78eb"}, - {file = "tokenizers-0.20.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:444d188186eab3148baf0615b522461b41b1f0cd58cd57b862ec94b6ac9780f1"}, - {file = "tokenizers-0.20.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37c04c032c1442740b2c2d925f1857885c07619224a533123ac7ea71ca5713da"}, - {file = "tokenizers-0.20.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453c7769d22231960ee0e883d1005c93c68015025a5e4ae56275406d94a3c907"}, - {file = "tokenizers-0.20.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4bb31f7b2847e439766aaa9cc7bccf7ac7088052deccdb2275c952d96f691c6a"}, - {file = "tokenizers-0.20.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:843729bf0f991b29655a069a2ff58a4c24375a553c70955e15e37a90dd4e045c"}, - {file = "tokenizers-0.20.3-cp311-none-win32.whl", hash = "sha256:efcce3a927b1e20ca694ba13f7a68c59b0bd859ef71e441db68ee42cf20c2442"}, - {file = "tokenizers-0.20.3-cp311-none-win_amd64.whl", hash = "sha256:88301aa0801f225725b6df5dea3d77c80365ff2362ca7e252583f2b4809c4cc0"}, - {file = "tokenizers-0.20.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:49d12a32e190fad0e79e5bdb788d05da2f20d8e006b13a70859ac47fecf6ab2f"}, - {file = "tokenizers-0.20.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:282848cacfb9c06d5e51489f38ec5aa0b3cd1e247a023061945f71f41d949d73"}, - {file = "tokenizers-0.20.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abe4e08c7d0cd6154c795deb5bf81d2122f36daf075e0c12a8b050d824ef0a64"}, - {file = "tokenizers-0.20.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca94fc1b73b3883c98f0c88c77700b13d55b49f1071dfd57df2b06f3ff7afd64"}, - {file = "tokenizers-0.20.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef279c7e239f95c8bdd6ff319d9870f30f0d24915b04895f55b1adcf96d6c60d"}, - {file = "tokenizers-0.20.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16384073973f6ccbde9852157a4fdfe632bb65208139c9d0c0bd0176a71fd67f"}, - {file = "tokenizers-0.20.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:312d522caeb8a1a42ebdec87118d99b22667782b67898a76c963c058a7e41d4f"}, - {file = "tokenizers-0.20.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2b7cb962564785a83dafbba0144ecb7f579f1d57d8c406cdaa7f32fe32f18ad"}, - {file = "tokenizers-0.20.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:124c5882ebb88dadae1fc788a582299fcd3a8bd84fc3e260b9918cf28b8751f5"}, - {file = "tokenizers-0.20.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2b6e54e71f84c4202111a489879005cb14b92616a87417f6c102c833af961ea2"}, - {file = "tokenizers-0.20.3-cp312-none-win32.whl", hash = "sha256:83d9bfbe9af86f2d9df4833c22e94d94750f1d0cd9bfb22a7bb90a86f61cdb1c"}, - {file = "tokenizers-0.20.3-cp312-none-win_amd64.whl", hash = "sha256:44def74cee574d609a36e17c8914311d1b5dbcfe37c55fd29369d42591b91cf2"}, - {file = "tokenizers-0.20.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0b630e0b536ef0e3c8b42c685c1bc93bd19e98c0f1543db52911f8ede42cf84"}, - {file = "tokenizers-0.20.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a02d160d2b19bcbfdf28bd9a4bf11be4cb97d0499c000d95d4c4b1a4312740b6"}, - {file = "tokenizers-0.20.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e3d80d89b068bc30034034b5319218c7c0a91b00af19679833f55f3becb6945"}, - {file = "tokenizers-0.20.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:174a54910bed1b089226512b4458ea60d6d6fd93060254734d3bc3540953c51c"}, - {file = "tokenizers-0.20.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:098b8a632b8656aa5802c46689462c5c48f02510f24029d71c208ec2c822e771"}, - {file = "tokenizers-0.20.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78c8c143e3ae41e718588281eb3e212c2b31623c9d6d40410ec464d7d6221fb5"}, - {file = "tokenizers-0.20.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b26b0aadb18cd8701077362ba359a06683662d5cafe3e8e8aba10eb05c037f1"}, - {file = "tokenizers-0.20.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07d7851a72717321022f3774e84aa9d595a041d643fafa2e87fbc9b18711dac0"}, - {file = "tokenizers-0.20.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:bd44e48a430ada902c6266a8245f5036c4fe744fcb51f699999fbe82aa438797"}, - {file = "tokenizers-0.20.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a4c186bb006ccbe1f5cc4e0380d1ce7806f5955c244074fd96abc55e27b77f01"}, - {file = "tokenizers-0.20.3-cp313-none-win32.whl", hash = "sha256:6e19e0f1d854d6ab7ea0c743d06e764d1d9a546932be0a67f33087645f00fe13"}, - {file = "tokenizers-0.20.3-cp313-none-win_amd64.whl", hash = "sha256:d50ede425c7e60966a9680d41b58b3a0950afa1bb570488e2972fa61662c4273"}, - {file = "tokenizers-0.20.3-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:9adda1ff5fb9dcdf899ceca672a4e2ce9e797adb512a6467305ca3d8bfcfbdd0"}, - {file = "tokenizers-0.20.3-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:6dde2cae6004ba7a3badff4a11911cae03ebf23e97eebfc0e71fef2530e5074f"}, - {file = "tokenizers-0.20.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4a7fd678b35614fca708579eb95b7587a5e8a6d328171bd2488fd9f27d82be4"}, - {file = "tokenizers-0.20.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b80e3c7283a01a356bd2210f53d1a4a5d32b269c2024389ed0173137708d50e"}, - {file = "tokenizers-0.20.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8cc0e8176b762973758a77f0d9c4467d310e33165fb74173418ca3734944da4"}, - {file = "tokenizers-0.20.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5634b2e2f5f3d2b4439d2d74066e22eb4b1f04f3fea05cb2a3c12d89b5a3bcd"}, - {file = "tokenizers-0.20.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b4ba635165bc1ea46f2da8e5d80b5f70f6ec42161e38d96dbef33bb39df73964"}, - {file = "tokenizers-0.20.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e4c7c64172e7789bd8b07aa3087ea87c4c4de7e90937a2aa036b5d92332536"}, - {file = "tokenizers-0.20.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1f74909ef7675c26d4095a817ec3393d67f3158ca4836c233212e5613ef640c4"}, - {file = "tokenizers-0.20.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0e9b81321a1e05b16487d312b4264984513f8b4a7556229cafac6e88c2036b09"}, - {file = "tokenizers-0.20.3-cp37-none-win32.whl", hash = "sha256:ab48184cd58b4a03022a2ec75b54c9f600ffea9a733612c02325ed636f353729"}, - {file = "tokenizers-0.20.3-cp37-none-win_amd64.whl", hash = "sha256:60ac483cebee1c12c71878523e768df02fa17e4c54412966cb3ac862c91b36c1"}, - {file = "tokenizers-0.20.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3229ef103c89583d10b9378afa5d601b91e6337530a0988e17ca8d635329a996"}, - {file = "tokenizers-0.20.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6ac52cc24bad3de865c7e65b1c4e7b70d00938a8ae09a92a453b8f676e714ad5"}, - {file = "tokenizers-0.20.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04627b7b502fa6a2a005e1bd446fa4247d89abcb1afaa1b81eb90e21aba9a60f"}, - {file = "tokenizers-0.20.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c27ceb887f0e81a3c377eb4605dca7a95a81262761c0fba308d627b2abb98f2b"}, - {file = "tokenizers-0.20.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65ab780194da4e1fcf5670523a2f377c4838ebf5249efe41fa1eddd2a84fb49d"}, - {file = "tokenizers-0.20.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98d343134f47159e81f7f242264b0eb222e6b802f37173c8d7d7b64d5c9d1388"}, - {file = "tokenizers-0.20.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2475bb004ab2009d29aff13b5047bfdb3d4b474f0aa9d4faa13a7f34dbbbb43"}, - {file = "tokenizers-0.20.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b6583a65c01db1197c1eb36857ceba8ec329d53afadd268b42a6b04f4965724"}, - {file = "tokenizers-0.20.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62d00ba208358c037eeab7bfc00a905adc67b2d31b68ab40ed09d75881e114ea"}, - {file = "tokenizers-0.20.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0fc7a39e5bedc817bda395a798dfe2d9c5f7c71153c90d381b5135a0328d9520"}, - {file = "tokenizers-0.20.3-cp38-none-win32.whl", hash = "sha256:84d40ee0f8550d64d3ea92dd7d24a8557a9172165bdb986c9fb2503b4fe4e3b6"}, - {file = "tokenizers-0.20.3-cp38-none-win_amd64.whl", hash = "sha256:205a45246ed7f1718cf3785cff88450ba603352412aaf220ace026384aa3f1c0"}, - {file = "tokenizers-0.20.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:93e37f0269a11dc3b1a953f1fca9707f0929ebf8b4063c591c71a0664219988e"}, - {file = "tokenizers-0.20.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f4cb0c614b0135e781de96c2af87e73da0389ac1458e2a97562ed26e29490d8d"}, - {file = "tokenizers-0.20.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7eb2fb1c432f5746b22f8a7f09fc18c4156cb0031c77f53cb19379d82d43297a"}, - {file = "tokenizers-0.20.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfa8d029bb156181b006643309d6b673615a24e4ed24cf03aa191d599b996f51"}, - {file = "tokenizers-0.20.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f90549622de3bf476ad9f1dd6f3f952ec3ed6ab8615ae88ef060d0c5bfad55d"}, - {file = "tokenizers-0.20.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1d469c74eebf5c43fd61cd9b030e271d17198edd7bd45392e03a3c091d7d6d4"}, - {file = "tokenizers-0.20.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bee8f53b2594749f4460d53253bae55d718f04e9b633efa0f5df8938bd98e4f0"}, - {file = "tokenizers-0.20.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:938441babf3e5720e4459e306ef2809fb267680df9d1ff2873458b22aef60248"}, - {file = "tokenizers-0.20.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7310ab23d7b0caebecc0e8be11a1146f320f5f07284000f6ea54793e83de1b75"}, - {file = "tokenizers-0.20.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:16121eb030a2b13094cfec936b0c12e8b4063c5f839591ea7d0212336d8f9921"}, - {file = "tokenizers-0.20.3-cp39-none-win32.whl", hash = "sha256:401cc21ef642ee235985d747f65e18f639464d377c70836c9003df208d582064"}, - {file = "tokenizers-0.20.3-cp39-none-win_amd64.whl", hash = "sha256:7498f3ea7746133335a6adb67a77cf77227a8b82c8483f644a2e5f86fea42b8d"}, - {file = "tokenizers-0.20.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e919f2e3e68bb51dc31de4fcbbeff3bdf9c1cad489044c75e2b982a91059bd3c"}, - {file = "tokenizers-0.20.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b8e9608f2773996cc272156e305bd79066163a66b0390fe21750aff62df1ac07"}, - {file = "tokenizers-0.20.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39270a7050deaf50f7caff4c532c01b3c48f6608d42b3eacdebdc6795478c8df"}, - {file = "tokenizers-0.20.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e005466632b1c5d2d2120f6de8aa768cc9d36cd1ab7d51d0c27a114c91a1e6ee"}, - {file = "tokenizers-0.20.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a07962340b36189b6c8feda552ea1bfeee6cf067ff922a1d7760662c2ee229e5"}, - {file = "tokenizers-0.20.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:55046ad3dd5f2b3c67501fcc8c9cbe3e901d8355f08a3b745e9b57894855f85b"}, - {file = "tokenizers-0.20.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:efcf0eb939988b627558aaf2b9dc3e56d759cad2e0cfa04fcab378e4b48fc4fd"}, - {file = "tokenizers-0.20.3-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f3558a7ae6a6d38a77dfce12172a1e2e1bf3e8871e744a1861cd7591ea9ebe24"}, - {file = "tokenizers-0.20.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d53029fe44bc70c3ff14ef512460a0cf583495a0f8e2f4b70e26eb9438e38a9"}, - {file = "tokenizers-0.20.3-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57a2a56397b2bec5a629b516b23f0f8a3e4f978c7488d4a299980f8375954b85"}, - {file = "tokenizers-0.20.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e5bfaae740ef9ece000f8a07e78ac0e2b085c5ce9648f8593ddf0243c9f76d"}, - {file = "tokenizers-0.20.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fbaf3ea28fedfb2283da60e710aff25492e795a7397cad8a50f1e079b65a5a70"}, - {file = "tokenizers-0.20.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c47c037116310dc976eb96b008e41b9cfaba002ed8005848d4d632ee0b7ba9ae"}, - {file = "tokenizers-0.20.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c31751f0721f58f5e19bb27c1acc259aeff860d8629c4e1a900b26a1979ada8e"}, - {file = "tokenizers-0.20.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:c697cbd3be7a79ea250ea5f380d6f12e534c543cfb137d5c734966b3ee4f34cc"}, - {file = "tokenizers-0.20.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b48971b88ef9130bf35b41b35fd857c3c4dae4a9cd7990ebc7fc03e59cc92438"}, - {file = "tokenizers-0.20.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e615de179bbe060ab33773f0d98a8a8572b5883dd7dac66c1de8c056c7e748c"}, - {file = "tokenizers-0.20.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da1ec842035ed9999c62e45fbe0ff14b7e8a7e02bb97688cc6313cf65e5cd755"}, - {file = "tokenizers-0.20.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6ee4954c1dd23aadc27958dad759006e71659d497dcb0ef0c7c87ea992c16ebd"}, - {file = "tokenizers-0.20.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3eda46ca402751ec82553a321bf35a617b76bbed7586e768c02ccacbdda94d6d"}, - {file = "tokenizers-0.20.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:de082392a85eb0055cc055c535bff2f0cc15d7a000bdc36fbf601a0f3cf8507a"}, - {file = "tokenizers-0.20.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c3db46cc0647bfd88263afdb739b92017a02a87ee30945cb3e86c7e25c7c9917"}, - {file = "tokenizers-0.20.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a292392f24ab9abac5cfa8197e5a6208f2e43723420217e1ceba0b4ec77816ac"}, - {file = "tokenizers-0.20.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dcd91f4e60f62b20d83a87a84fe062035a1e3ff49a8c2bbdeb2d441c8e311f4"}, - {file = "tokenizers-0.20.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:900991a2b8ee35961b1095db7e265342e0e42a84c1a594823d5ee9f8fb791958"}, - {file = "tokenizers-0.20.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5a8d8261ca2133d4f98aa9627c748189502b3787537ba3d7e2beb4f7cfc5d627"}, - {file = "tokenizers-0.20.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c4fd4d71e6deb6ddf99d8d0eab87d1d16f635898906e631914a9bae8ae9f2cfb"}, - {file = "tokenizers-0.20.3.tar.gz", hash = "sha256:2278b34c5d0dd78e087e1ca7f9b1dcbf129d80211afa645f214bd6e051037539"}, + {file = "tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73"}, + {file = "tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390"}, + {file = "tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82"}, + {file = "tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138"}, + {file = "tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9"}, ] [package.dependencies] -huggingface-hub = ">=0.16.4,<1.0" +huggingface-hub = ">=0.16.4,<2.0" [package.extras] dev = ["tokenizers[testing]"] docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] -testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "pytest-asyncio", "requests", "ruff"] [[package]] name = "tomlkit" @@ -6867,72 +6889,77 @@ telegram = ["requests"] [[package]] name = "transformers" -version = "4.46.3" +version = "4.57.1" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" groups = ["main"] files = [ - {file = "transformers-4.46.3-py3-none-any.whl", hash = "sha256:a12ef6f52841fd190a3e5602145b542d03507222f2c64ebb7ee92e8788093aef"}, - {file = "transformers-4.46.3.tar.gz", hash = "sha256:8ee4b3ae943fe33e82afff8e837f4b052058b07ca9be3cb5b729ed31295f72cc"}, + {file = "transformers-4.57.1-py3-none-any.whl", hash = "sha256:b10d05da8fa67dc41644dbbf9bc45a44cb86ae33da6f9295f5fbf5b7890bd267"}, + {file = "transformers-4.57.1.tar.gz", hash = "sha256:f06c837959196c75039809636cd964b959f6604b75b8eeec6fdfc0440b89cc55"}, ] [package.dependencies] filelock = "*" -huggingface-hub = ">=0.23.2,<1.0" +huggingface-hub = ">=0.34.0,<1.0" numpy = ">=1.17" packaging = ">=20.0" pyyaml = ">=5.1" regex = "!=2019.12.17" requests = "*" -safetensors = ">=0.4.1" -tokenizers = ">=0.20,<0.21" +safetensors = ">=0.4.3" +tokenizers = ">=0.22.0,<=0.23.0" tqdm = ">=4.27" [package.extras] accelerate = ["accelerate (>=0.26.0)"] -agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch"] -all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=0.9.16)", "tokenizers (>=0.20,<0.21)", "torch", "torchaudio", "torchvision"] +all = ["Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "av", "codecarbon (>=2.8.1)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "librosa", "mistral-common[opencv] (>=1.6.3)", "num2words", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision"] audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] benchmark = ["optimum-benchmark (>=0.3.0)"] -codecarbon = ["codecarbon (==1.2.0)"] +chat-template = ["jinja2 (>=3.1.0)"] +codecarbon = ["codecarbon (>=2.8.1)"] deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"] -deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.20,<0.21)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.20,<0.21)", "urllib3 (<2.0.0)"] -dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "libcst", "librosa", "nltk (<=3.8.1)", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.20,<0.21)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "openai (>=1.98.0)", "optuna", "parameterized (>=0.9)", "protobuf", "psutil", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "timeout-decorator", "torch (>=2.2)", "uvicorn"] +dev = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "av", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxconverter-common", "openai (>=1.98.0)", "optax (>=0.0.8,<=0.1.4)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)", "uvicorn"] +dev-tensorflow = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "onnxconverter-common", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "openai (>=1.98.0)", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "tf2onnx", "timeout-decorator", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "urllib3 (<2.0.0)", "uvicorn"] +dev-torch = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "kenlm", "kernels (>=0.6.1,<=0.9)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "openai (>=1.98.0)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)", "uvicorn"] flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] ftfy = ["ftfy"] -integrations = ["optuna", "ray[tune] (>=2.7.0)", "sigopt"] -ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +hf-xet = ["hf_xet"] +hub-kernels = ["kernels (>=0.6.1,<=0.9)"] +integrations = ["kernels (>=0.6.1,<=0.9)", "optuna", "ray[tune] (>=2.7.0)"] +ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)"] +mistral-common = ["mistral-common[opencv] (>=1.6.3)"] modelcreation = ["cookiecutter (==1.7.3)"] natten = ["natten (>=0.14.6,<0.15.0)"] +num2words = ["num2words"] onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] +open-telemetry = ["opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk"] optuna = ["optuna"] -quality = ["GitPython (<3.1.19)", "datasets (!=2.5.0)", "isort (>=5.5.4)", "libcst", "rich", "ruff (==0.5.1)", "urllib3 (<2.0.0)"] +quality = ["GitPython (<3.1.19)", "datasets (>=2.15.0)", "libcst", "pandas (<2.3.0)", "rich", "ruff (==0.13.1)", "urllib3 (<2.0.0)"] ray = ["ray[tune] (>=2.7.0)"] -retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] -ruff = ["ruff (==0.5.1)"] +retrieval = ["datasets (>=2.15.0)", "faiss-cpu"] +ruff = ["ruff (==0.13.1)"] sagemaker = ["sagemaker (>=2.31.0)"] sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] -serving = ["fastapi", "pydantic", "starlette", "uvicorn"] +serving = ["accelerate (>=0.26.0)", "fastapi", "openai (>=1.98.0)", "pydantic (>=2)", "starlette", "torch (>=2.2)", "uvicorn"] sigopt = ["sigopt"] sklearn = ["scikit-learn"] speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "parameterized", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] +testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "openai (>=1.98.0)", "parameterized (>=0.9)", "psutil", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "timeout-decorator", "torch (>=2.2)", "uvicorn"] tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"] tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] tiktoken = ["blobfile", "tiktoken"] -timm = ["timm (<=0.9.16)"] -tokenizers = ["tokenizers (>=0.20,<0.21)"] -torch = ["accelerate (>=0.26.0)", "torch"] +timm = ["timm (!=1.0.18,<=1.0.19)"] +tokenizers = ["tokenizers (>=0.22.0,<=0.23.0)"] +torch = ["accelerate (>=0.26.0)", "torch (>=2.2)"] torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.23.2,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.20,<0.21)", "torch", "tqdm (>=4.27)"] -video = ["av (==9.2.0)"] +torchhub = ["filelock", "huggingface-hub (>=0.34.0,<1.0)", "importlib_metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "tqdm (>=4.27)"] +video = ["av"] vision = ["Pillow (>=10.0.1,<=15.0)"] [[package]] @@ -7555,4 +7582,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.12,<3.13" -content-hash = "57af163c6074adea916d292fdaf3a214de19635de9da2bf0fb0623a7e4e96cd7" +content-hash = "b41766c9904e3fe442a13964de7c8316531ce6e9ada0b294ef42147a5d257157" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 73ede84c..7dd0f72d 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -44,7 +44,7 @@ dependencies = [ "validators>=0.34.0", "psutil (>=7.0.0,<8.0.0)", "docling (>=2.0.0)", - "transformers (>=4.46.0)", + "transformers (>=4.57.0,<5.0.0)", "pydub (>=0.25.1,<0.26.0)", ] diff --git a/backend/rag_solution/data_ingestion/chunking.py b/backend/rag_solution/data_ingestion/chunking.py index 1bcfecfc..4a71e0dd 100644 --- a/backend/rag_solution/data_ingestion/chunking.py +++ b/backend/rag_solution/data_ingestion/chunking.py @@ -156,8 +156,90 @@ def semantic_chunking(text: str, min_chunk_size: int = 1, max_chunk_size: int = return chunks +def sentence_based_chunking( + text: str, target_chars: int = 750, overlap_chars: int = 100, min_chars: int = 500 +) -> list[str]: + """Sentence-based chunking with conservative character limits (FAST, no API calls). + + Strategy for IBM Slate 512-token limit: + - Conservative char/token ratio: 2.5 chars/token (handles technical docs) + - Target: 750 chars ≈ 300 tokens (guidance: 200-400 tokens for 512-token models) + - Overlap: 100 chars ≈ 40 tokens (~13% overlap) + - Min: 500 chars ≈ 200 tokens + - Max safe: 1000 chars ≈ 400 tokens (well under 512 limit) + - Chunks at sentence boundaries (semantic) + + Args: + text: Input text to chunk + target_chars: Target characters per chunk (default: 750) + overlap_chars: Characters to overlap between chunks (default: 100) + min_chars: Minimum characters for a chunk (default: 500) + + Returns: + List of sentence-based chunks + """ + if not text: + return [] + + sentences = split_sentences(text) + if not sentences: + return [] + + chunks: list[str] = [] + current_chunk: list[str] = [] + current_char_count = 0 + + for sentence in sentences: + sentence_len = len(sentence) + + # Check if adding this sentence would exceed target + if current_char_count + sentence_len > target_chars and current_chunk: + # Save current chunk + chunk_text = " ".join(current_chunk) + chunks.append(chunk_text) + + # Create overlap: keep last sentences that fit in overlap_chars + overlap_chunk: list[str] = [] + overlap_count = 0 + + for i in range(len(current_chunk) - 1, -1, -1): + sent_len = len(current_chunk[i]) + if overlap_count + sent_len <= overlap_chars: + overlap_chunk.insert(0, current_chunk[i]) + overlap_count += sent_len + else: + break + + current_chunk = overlap_chunk + current_char_count = overlap_count + + current_chunk.append(sentence) + current_char_count += sentence_len + + # Add final chunk if it meets minimum size + if current_chunk: + chunk_text = " ".join(current_chunk) + + if len(chunk_text) >= min_chars or not chunks: + chunks.append(chunk_text) + elif chunks: + # Merge small final chunk with previous chunk + chunks[-1] += " " + chunk_text + + avg_chars = sum(len(c) for c in chunks) / len(chunks) if chunks else 0 + logger.info( + f"Created {len(chunks)} sentence-based chunks: avg {avg_chars:.0f} chars " + f"(~{avg_chars / 2.5:.0f} tokens estimated)" + ) + + return chunks + + def token_based_chunking(text: str, max_tokens: int = 100, overlap: int = 20) -> list[str]: - """Split text into chunks based on token count. + """DEPRECATED: Use efficient_token_chunking() instead. + + This function makes WatsonX API calls for every sentence - very slow. + Kept for backward compatibility only. Args: text: Input text to chunk @@ -167,6 +249,8 @@ def token_based_chunking(text: str, max_tokens: int = 100, overlap: int = 20) -> Returns: List of token-based chunks """ + logger.warning("token_based_chunking() is deprecated - use efficient_token_chunking() instead") + sentences = split_sentences(text) tokenized_sentences = get_tokenization(sentences) @@ -281,6 +365,45 @@ def hierarchical_chunker_wrapper(text: str, settings: Settings = get_settings()) return [chunk.text for chunk in child_chunks] +def sentence_chunker(text: str, settings: Settings = get_settings()) -> list[str]: + """Sentence-based chunking using settings configuration. + + Uses conservative character-to-token ratio (2.5:1) for IBM Slate safety. + + Args: + text: Input text to chunk + settings: Configuration settings + + Returns: + List of sentence-based chunks + """ + # Convert config values assuming they're in tokens, multiply by 2.5 for chars + target_chars = int(settings.max_chunk_size * 2.5) if settings.max_chunk_size < 1000 else 750 + overlap_chars = int(settings.chunk_overlap * 2.5) if settings.chunk_overlap < 200 else 100 + min_chars = int(settings.min_chunk_size * 2.5) if settings.min_chunk_size < 500 else 500 + + return sentence_based_chunking(text, target_chars=target_chars, overlap_chars=overlap_chars, min_chars=min_chars) + + +def token_chunker(text: str, settings: Settings = get_settings()) -> list[str]: + """DEPRECATED: Use sentence_chunker() instead - it's faster and safer. + + This function makes WatsonX API calls which is very slow. + + Args: + text: Input text to chunk + settings: Configuration settings + + Returns: + List of token-based chunks + """ + logger.warning("token_chunker() is deprecated - use sentence_chunker() instead") + # Use 80% of max tokens to leave safety margin + max_tokens = int(settings.max_chunk_size * 0.8) if settings.max_chunk_size < 512 else 410 + overlap = int(settings.chunk_overlap * 0.8) if settings.chunk_overlap < 100 else 80 + return token_based_chunking(text, max_tokens=max_tokens, overlap=overlap) + + def get_chunking_method(settings: Settings = get_settings()) -> Callable[[str], list[str]]: """Get the appropriate chunking method based on settings. @@ -292,9 +415,13 @@ def get_chunking_method(settings: Settings = get_settings()) -> Callable[[str], """ strategy = settings.chunking_strategy.lower() + if strategy == "sentence": + return lambda text: sentence_chunker(text, settings) if strategy == "semantic": return semantic_chunker if strategy == "hierarchical": return lambda text: hierarchical_chunker_wrapper(text, settings) + if strategy == "token": + return lambda text: token_chunker(text, settings) return simple_chunker diff --git a/backend/rag_solution/data_ingestion/hierarchical_chunking.py b/backend/rag_solution/data_ingestion/hierarchical_chunking.py index 617e33a7..3ca87336 100644 --- a/backend/rag_solution/data_ingestion/hierarchical_chunking.py +++ b/backend/rag_solution/data_ingestion/hierarchical_chunking.py @@ -159,13 +159,26 @@ def create_hierarchical_chunks( child_current_pos = child_start_in_parent + len(child_text) - overlap - logger.info( - "Created %d hierarchical chunks: %d root, %d parents, %d children", - len(all_chunks), - len([c for c in all_chunks if c.level == 0]), - len([c for c in all_chunks if c.level == 1]), - len([c for c in all_chunks if c.level == 2]), - ) + # Log chunk distribution by level (labels depend on levels setting) + level_0_count = len([c for c in all_chunks if c.level == 0]) + level_1_count = len([c for c in all_chunks if c.level == 1]) + level_2_count = len([c for c in all_chunks if c.level == 2]) + + if levels >= 3: + logger.info( + "Created %d hierarchical chunks: %d root, %d parents, %d children (3-level)", + len(all_chunks), + level_0_count, + level_1_count, + level_2_count, + ) + else: + logger.info( + "Created %d hierarchical chunks: %d parents, %d children (2-level)", + len(all_chunks), + level_0_count, + level_1_count, + ) return all_chunks diff --git a/backend/rag_solution/schemas/conversation_schema.py b/backend/rag_solution/schemas/conversation_schema.py index 74c44cb0..dc99703c 100644 --- a/backend/rag_solution/schemas/conversation_schema.py +++ b/backend/rag_solution/schemas/conversation_schema.py @@ -58,8 +58,9 @@ class MessageMetadata(BaseModel): model_used: str | None = Field(default=None, description="LLM model used for generation") confidence_score: float | None = Field(default=None, description="Confidence score of the response") context_length: int | None = Field(default=None, description="Length of context used") + token_analysis: dict[str, Any] | None = Field(default=None, description="Detailed token usage breakdown") - model_config = ConfigDict(extra="forbid") # Prevent additional fields + model_config = ConfigDict(extra="allow") # Allow additional fields for flexibility class ExportFormat(str, Enum): @@ -265,10 +266,16 @@ class ConversationMessageOutput(BaseModel): token_count: int | None = Field(default=None, description="Token count for this message") execution_time: float | None = Field(default=None, description="Execution time in seconds") token_warning: dict[str, Any] | None = Field(default=None, description="Token usage warning if applicable") + sources: list[dict[str, Any]] | None = Field(default=None, description="Source documents with full metadata") + cot_output: dict[str, Any] | None = Field(default=None, description="Chain of Thought reasoning output") + token_analysis: dict[str, Any] | None = Field(default=None, description="Detailed token usage breakdown") @classmethod def from_db_message(cls, message: Any) -> "ConversationMessageOutput": - """Create ConversationMessageOutput from database message model.""" + """Create ConversationMessageOutput from database message model. + + Reconstructs sources, cot_output, and token_analysis from stored metadata. + """ # Debug logging for token count extraction logger.info("🔍 SCHEMA DEBUG: from_db_message() called") logger.info( @@ -278,12 +285,14 @@ def from_db_message(cls, message: Any) -> "ConversationMessageOutput": # Handle metadata properly - it's stored as a dict in the database metadata_value = None + raw_metadata = None if message.message_metadata: logger.info("🔍 SCHEMA DEBUG: message.message_metadata type: %s", type(message.message_metadata)) logger.info( f"🔍 SCHEMA DEBUG: message.message_metadata keys: {list(message.message_metadata.keys()) if isinstance(message.message_metadata, dict) else 'Not a dict'}" ) if isinstance(message.message_metadata, dict): + raw_metadata = message.message_metadata # Keep reference to raw dict # It's already a dictionary from the database - convert to MessageMetadata object try: logger.info( @@ -302,6 +311,26 @@ def from_db_message(cls, message: Any) -> "ConversationMessageOutput": else: logger.warning("Unexpected metadata type: %s", type(message.message_metadata)) + # Reconstruct token_analysis from metadata if available + token_analysis = None + if raw_metadata and "token_analysis" in raw_metadata: + token_analysis = raw_metadata["token_analysis"] + logger.info("🔍 SCHEMA DEBUG: Reconstructed token_analysis from metadata") + + # Reconstruct sources from metadata if available + # Note: Full source data (with scores, content, page numbers) needs to be stored in metadata + # For now, we can only reconstruct if it was stored in metadata + sources = None + if raw_metadata and "sources" in raw_metadata: + sources = raw_metadata["sources"] + logger.info("🔍 SCHEMA DEBUG: Reconstructed sources from metadata") + + # Reconstruct cot_output from metadata if available + cot_output = None + if raw_metadata and "cot_output" in raw_metadata: + cot_output = raw_metadata["cot_output"] + logger.info("🔍 SCHEMA DEBUG: Reconstructed cot_output from metadata") + data = { "id": message.id, "session_id": message.session_id, @@ -312,6 +341,9 @@ def from_db_message(cls, message: Any) -> "ConversationMessageOutput": "metadata": metadata_value, "token_count": message.token_count, "execution_time": message.execution_time, + "token_analysis": token_analysis, + "sources": sources, + "cot_output": cot_output, } logger.info("🔍 SCHEMA DEBUG: data dict token_count=%d", data["token_count"]) diff --git a/backend/rag_solution/services/collection_service.py b/backend/rag_solution/services/collection_service.py index d7ab5fad..67e80de4 100644 --- a/backend/rag_solution/services/collection_service.py +++ b/backend/rag_solution/services/collection_service.py @@ -215,11 +215,11 @@ def _add_chunk_counts_to_collection(self, collection: CollectionOutput) -> Colle def _get_batch_document_chunk_counts(self, collection_name: str, document_ids: list[str]) -> dict[str, int]: """ - Get chunk counts for multiple documents in a single batch query. + Get chunk counts for multiple documents with pagination support. This method optimizes performance by querying all document chunk counts - at once using Milvus IN operator, instead of making individual queries - for each document (N+1 problem). + at once using Milvus IN operator. For collections with >16,384 chunks, + it automatically paginates to retrieve all results. Performance: 100 documents takes <1 second vs 5-50 seconds with individual queries. @@ -248,26 +248,58 @@ def _get_batch_document_chunk_counts(self, collection_name: str, document_ids: l document_ids_json = json.dumps(document_ids) expr = f"document_id in {document_ids_json}" - # Query with expression filter for all document_ids at once - # Only fetch minimal fields needed for counting - results = milvus_collection.query( - expr=expr, - output_fields=["document_id"], # Only need document_id for counting - limit=100000, # Large limit to get all chunks (assuming <100k total chunks) - ) - - # Count chunks per document_id + # Paginate through results to handle collections with >16,384 chunks + # Milvus constraint: offset + limit <= 16384 document_chunk_counts: dict[str, int] = {} - for result in results: - doc_id = result.get("document_id", "") - if doc_id: - document_chunk_counts[doc_id] = document_chunk_counts.get(doc_id, 0) + 1 + offset = 0 + page_size = 16384 # Milvus maximum + total_chunks_retrieved = 0 + + while True: + # Query one page of results + results = milvus_collection.query( + expr=expr, + output_fields=["document_id"], + limit=page_size, + offset=offset, + ) + + # Break if no more results + if not results: + break + + # Count chunks per document_id in this page + for result in results: + doc_id = result.get("document_id", "") + if doc_id: + document_chunk_counts[doc_id] = document_chunk_counts.get(doc_id, 0) + 1 + + total_chunks_retrieved += len(results) + + # Break if we got fewer results than page_size (last page) + if len(results) < page_size: + break + + # Move to next page + offset += page_size + + # Safety check: Milvus constraint is offset + limit <= 16384 + # If next offset would exceed this, we can't paginate further + if offset >= 16384: + logger.warning( + "Reached Milvus pagination limit (offset=%d) for collection %s. " + "Chunk counts may be incomplete if collection has >16,384 chunks.", + offset, + collection_name, + ) + break logger.info( - "Batch query for %d documents returned %d chunks from collection %s", + "Batch query for %d documents returned %d total chunks from collection %s (pages: %d)", len(document_ids), - len(results), + total_chunks_retrieved, collection_name, + (offset // page_size) + 1, ) return document_chunk_counts diff --git a/backend/rag_solution/services/conversation_service.py b/backend/rag_solution/services/conversation_service.py index 8e9013c6..99a012f8 100644 --- a/backend/rag_solution/services/conversation_service.py +++ b/backend/rag_solution/services/conversation_service.py @@ -202,10 +202,15 @@ async def add_message(self, message_input: ConversationMessageInput) -> Conversa # Convert MessageMetadata Pydantic object to dictionary for database storage metadata_dict: dict[str, Any] = {} if message_input.metadata: - if hasattr(message_input.metadata, "model_dump"): - metadata_dict = message_input.metadata.model_dump() + if isinstance(message_input.metadata, dict): + # Already a dictionary, use it directly + metadata_dict = message_input.metadata else: - metadata_dict = message_input.metadata.__dict__ + # Pydantic model - try model_dump (v2) first, fall back to dict() (v1) + try: + metadata_dict = message_input.metadata.model_dump() + except AttributeError: + metadata_dict = dict(message_input.metadata) message = ConversationMessage( session_id=message_input.session_id, @@ -334,24 +339,29 @@ async def process_user_message(self, message_input: ConversationMessageInput) -> # Execute search - this will automatically use CoT if appropriate search_result = await self.search_service.search(search_input) - logger.info("📊 CONVERSATION SERVICE: Search result has metadata: %s", hasattr(search_result, "metadata")) - if hasattr(search_result, "metadata") and search_result.metadata: - logger.info("📊 CONVERSATION SERVICE: Search metadata keys: %s", list(search_result.metadata.keys())) - logger.info("📊 CONVERSATION SERVICE: Search result has cot_output: %s", hasattr(search_result, "cot_output")) - if hasattr(search_result, "cot_output") and search_result.cot_output: - logger.info("📊 CONVERSATION SERVICE: CoT output type: %s", type(search_result.cot_output)) + + # Extract metadata and cot_output using getattr with defaults + result_metadata = getattr(search_result, "metadata", None) + result_cot_output = getattr(search_result, "cot_output", None) + + logger.info("📊 CONVERSATION SERVICE: Search result has metadata: %s", result_metadata is not None) + if result_metadata: + logger.info("📊 CONVERSATION SERVICE: Search metadata keys: %s", list(result_metadata.keys())) + logger.info("📊 CONVERSATION SERVICE: Search result has cot_output: %s", result_cot_output is not None) + if result_cot_output: + logger.info("📊 CONVERSATION SERVICE: CoT output type: %s", type(result_cot_output)) # Extract CoT information if it was used cot_used = False cot_steps: list[dict[str, Any]] = [] # Check both metadata and cot_output for CoT information - if hasattr(search_result, "metadata") and search_result.metadata: - cot_used = search_result.metadata.get("cot_used", False) + if result_metadata: + cot_used = result_metadata.get("cot_used", False) logger.info("🧠 CoT metadata: cot_used=%s", cot_used) # Extract CoT steps from cot_output (this is where the actual reasoning steps are) - if hasattr(search_result, "cot_output") and search_result.cot_output: + if result_cot_output: if isinstance(search_result.cot_output, dict): reasoning_steps = search_result.cot_output.get("reasoning_steps", []) if reasoning_steps: @@ -375,26 +385,117 @@ async def process_user_message(self, message_input: ConversationMessageInput) -> logger.info("🧠 Final CoT extraction: cot_used=%s, cot_steps_count=%d", cot_used, len(cot_steps)) - # Convert DocumentMetadata objects to dictionaries for JSON serialization - def serialize_documents(documents): - """Convert DocumentMetadata objects to JSON-serializable dictionaries.""" + # Convert DocumentMetadata and QueryResult objects to dictionaries for JSON serialization + def serialize_documents(documents, query_results): + """Convert DocumentMetadata objects to JSON-serializable dictionaries matching frontend schema. + + Enhances documents with scores and page numbers from query_results. + Frontend expects: {document_name: str, content: str, metadata: {score: float, page_number: int, ...}} + """ + # Extract scores and page numbers from query_results by document_id + doc_data_map = {} + if query_results: + for result in query_results: + if not result.chunk: + continue + + doc_id = result.chunk.document_id + if not doc_id: + continue + + # Get score (from QueryResult level first, then chunk level) + score = ( + result.score + if result.score is not None + else (result.chunk.score if hasattr(result.chunk, "score") else None) + ) + + # Get page number from chunk metadata + page_number = None + if result.chunk.metadata and hasattr(result.chunk.metadata, "page_number"): + page_number = result.chunk.metadata.page_number + + # Get content from chunk + content = result.chunk.text if hasattr(result.chunk, "text") and result.chunk.text else "" + + # Keep track of best score and first page number for each document + if doc_id not in doc_data_map: + doc_data_map[doc_id] = { + "score": score, + "page_numbers": {page_number} if page_number else set(), + "content": content, + } + else: + # Update with better score if found + if score is not None and ( + doc_data_map[doc_id]["score"] is None or score > doc_data_map[doc_id]["score"] + ): + doc_data_map[doc_id]["score"] = score + # Collect all page numbers + if page_number: + doc_data_map[doc_id]["page_numbers"].add(page_number) + # Append content (limit to avoid huge payloads) + if content and len(doc_data_map[doc_id]["content"]) < 2000: + doc_data_map[doc_id]["content"] += "\n\n" + content + serialized = [] for doc in documents: if hasattr(doc, "__dict__"): - # Convert object to dict, handling any nested objects - doc_dict = {} + # Extract fields matching frontend schema + doc_dict = { + "document_name": getattr(doc, "document_name", getattr(doc, "name", "Unknown Document")), + "content": getattr(doc, "content", getattr(doc, "text", "")), + "metadata": {}, + } + + # Collect all other attributes into metadata dict for key, value in doc.__dict__.items(): - if isinstance(value, str | int | float | bool | type(None)): - doc_dict[key] = value - else: - doc_dict[key] = str(value) + if key not in ["document_name", "name", "content", "text"]: + if isinstance(value, str | int | float | bool | type(None)): + doc_dict["metadata"][key] = value + else: + doc_dict["metadata"][key] = str(value) + + # Try to enhance with data from query_results + # Since DocumentMetadata doesn't have document_id, we need to use all available query result data + # Get the overall best score and earliest page from all query results + if doc_data_map: + all_scores = [data["score"] for data in doc_data_map.values() if data["score"] is not None] + all_pages = [] + for data in doc_data_map.values(): + all_pages.extend(data["page_numbers"]) + + if all_scores: + # Use the best (highest) score from all retrieved chunks + doc_dict["metadata"]["score"] = max(all_scores) + + if all_pages: + # Use the first page number mentioned + doc_dict["metadata"]["page_number"] = min(all_pages) + + # If original content is empty, use content from query results + if not doc_dict["content"] and doc_data_map: + # Get first non-empty content + for data in doc_data_map.values(): + if data["content"]: + doc_dict["content"] = data["content"][:1000] # Limit to 1000 chars + break + serialized.append(doc_dict) else: - serialized.append(str(doc)) + # Fallback for unknown types + serialized.append({"document_name": "Unknown", "content": str(doc), "metadata": {}}) + + logger.info(f"📊 Serialized {len(serialized)} documents with scores and page numbers") + if serialized and "metadata" in serialized[0]: + logger.info(f"📊 First source metadata: {serialized[0]['metadata']}") + return serialized - # Serialize search sources - serialized_documents = serialize_documents(search_result.documents) if search_result.documents else [] + # Serialize search sources with query results for scores and page numbers + result_documents = getattr(search_result, "documents", []) or [] + result_query_results = getattr(search_result, "query_results", []) or [] + serialized_documents = serialize_documents(result_documents, result_query_results) # IMPROVED TOKEN TRACKING: Better estimation for assistant response try: @@ -402,9 +503,12 @@ def serialize_documents(documents): provider = self.llm_provider_service.get_user_provider(user_id) # Use the provider's tokenize method if available (WatsonX has this) - if provider and hasattr(provider, "client") and hasattr(provider.client, "tokenize"): + provider_client = getattr(provider, "client", None) if provider else None + tokenize_method = getattr(provider_client, "tokenize", None) if provider_client else None + + if tokenize_method: try: - assistant_tokens_result = provider.client.tokenize(text=search_result.answer) + assistant_tokens_result = tokenize_method(text=search_result.answer) assistant_response_tokens = len(assistant_tokens_result.get("result", [])) logger.info("✅ Real token count from provider: assistant=%d", assistant_response_tokens) except (ValueError, KeyError, AttributeError) as e: @@ -422,16 +526,12 @@ def serialize_documents(documents): # Add CoT token usage to the total token count cot_token_usage = 0 - if ( - hasattr(search_result, "cot_output") - and search_result.cot_output - and isinstance(search_result.cot_output, dict) - ): + if result_cot_output and isinstance(result_cot_output, dict): # Extract total token usage from CoT output - cot_token_usage = search_result.cot_output.get("token_usage", 0) + cot_token_usage = result_cot_output.get("token_usage", 0) if not cot_token_usage: # Sum from individual reasoning steps if total not available - reasoning_steps = search_result.cot_output.get("reasoning_steps", []) + reasoning_steps = result_cot_output.get("reasoning_steps", []) for step in reasoning_steps: step_tokens = step.get("token_usage", 0) if isinstance(step, dict) else 0 cot_token_usage += step_tokens @@ -444,11 +544,9 @@ def serialize_documents(documents): token_warning_dict = None try: # Get model name from provider - model_name = "default-model" # Fallback - if provider and hasattr(provider, "model_id"): - model_name = provider.model_id - elif provider and hasattr(provider, "model_name"): - model_name = provider.model_name + model_name = getattr(provider, "model_id", None) if provider else None + if not model_name: + model_name = getattr(provider, "model_name", "default-model") if provider else "default-model" # Create LLMUsage object for warning check current_usage = LLMUsage( @@ -496,7 +594,18 @@ def serialize_documents(documents): f"🔍 DEBUG: Assistant response: '{search_result.answer[:100]}...' -> {assistant_response_tokens} tokens" ) + # Calculate context tokens and conversation total tokens BEFORE creating metadata + context_tokens = len(serialized_documents) * 100 if serialized_documents else 0 + + # Get conversation total tokens from session statistics + try: + stats = await self.get_session_statistics(message_input.session_id, session.user_id) + conversation_total_tokens = stats.total_tokens + except (ValueError, KeyError, AttributeError): + conversation_total_tokens = 0 + # Create assistant response with integration metadata and token tracking + # IMPORTANT: Store sources and cot_output in metadata so they persist to database metadata_dict = { "source_documents": [doc.get("document_id", "") for doc in serialized_documents] if serialized_documents @@ -516,8 +625,24 @@ def serialize_documents(documents): "execution_time": search_result.execution_time, "context_length": len(context.context_window) if context else None, "token_count": token_count, + "token_analysis": { + "query_tokens": user_token_count, + "context_tokens": context_tokens, + "response_tokens": assistant_response_tokens, + "system_tokens": cot_token_usage, + "total_this_turn": token_count, + "conversation_total": conversation_total_tokens, + }, + # Store full source data for frontend consumption + "sources": serialized_documents if serialized_documents else None, + # Store CoT output for frontend consumption + "cot_output": result_cot_output if (cot_used and result_cot_output) else None, } + logger.info( + f"📊 CONVERSATION SERVICE: Created metadata_dict with token_analysis: {metadata_dict.get('token_analysis')}" + ) + assistant_message_input = ConversationMessageInput( session_id=message_input.session_id, content=search_result.answer, @@ -543,7 +668,20 @@ def serialize_documents(documents): assistant_message.token_warning = token_warning_dict logger.info("📊 CONVERSATION SERVICE: Added token warning to response") - logger.info("🎉 CONVERSATION SERVICE: Returning assistant message with full metadata") + # Add full source documents to the response for frontend consumption + # Frontend expects: sources: [{document_name, content, metadata}] + if serialized_documents: + assistant_message.sources = serialized_documents + logger.info(f"📊 CONVERSATION SERVICE: Added {len(serialized_documents)} sources to response") + + # Add CoT output to the response if CoT was used + if cot_used and result_cot_output: + assistant_message.cot_output = result_cot_output + logger.info("📊 CONVERSATION SERVICE: Added CoT output to response") + + logger.info( + "🎉 CONVERSATION SERVICE: Returning assistant message with full metadata (including token_analysis), sources, and CoT" + ) return assistant_message async def get_session_statistics(self, session_id: UUID, user_id: UUID) -> SessionStatistics: diff --git a/backend/rag_solution/services/user_provider_service.py b/backend/rag_solution/services/user_provider_service.py index 5c92441d..c5b474f6 100644 --- a/backend/rag_solution/services/user_provider_service.py +++ b/backend/rag_solution/services/user_provider_service.py @@ -118,7 +118,14 @@ def _create_default_rag_template(self, user_id: UUID4) -> PromptTemplateOutput: user_id=user_id, template_type=PromptTemplateType.RAG_QUERY, system_prompt=( - "You are a helpful AI assistant specializing in answering questions based on the given context." + "You are a helpful AI assistant specializing in answering questions based on the given context. " + "Format your responses using Markdown for better readability:\n" + "- Use **bold** for emphasis on key points\n" + "- Use bullet points (- or *) for lists\n" + "- Use numbered lists (1. 2. 3.) for sequential steps\n" + "- Use `code blocks` for technical terms or code\n" + "- Use proper headings (## or ###) for sections when appropriate\n" + "- Keep answers well-structured and concise" ), template_format="{context}\n\n{question}", input_variables={ diff --git a/backend/tests/unit/services/test_collection_service_chunk_count.py b/backend/tests/unit/services/test_collection_service_chunk_count.py index f2281ec4..303524f9 100644 --- a/backend/tests/unit/services/test_collection_service_chunk_count.py +++ b/backend/tests/unit/services/test_collection_service_chunk_count.py @@ -298,7 +298,7 @@ def test_batch_count_success(self, collection_service): call_args = mock_collection.query.call_args assert call_args[1]["expr"] == 'document_id in ["doc_123", "doc_456"]' assert call_args[1]["output_fields"] == ["document_id"] - assert call_args[1]["limit"] == 100000 + assert call_args[1]["limit"] == 16384 # Verify correct counts are returned (3 for doc_123, 2 for doc_456) assert counts == {"doc_123": 3, "doc_456": 2} diff --git a/backend/vectordbs/utils/watsonx.py b/backend/vectordbs/utils/watsonx.py index 99c13809..6b12e67c 100644 --- a/backend/vectordbs/utils/watsonx.py +++ b/backend/vectordbs/utils/watsonx.py @@ -405,7 +405,7 @@ def generate_batch( def generate_text_stream( prompt: str, model_id: str, - max_tokens: int = 150, + max_tokens: int = 1024, temperature: float = 0.7, timeout: int = 30, random_seed: int = 50, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 66b374c2..05c452f0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,14 +9,17 @@ "version": "1.0.0", "dependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@carbon/icons-react": "^11.69.0", "@headlessui/react": "^2.2.9", "@heroicons/react": "^2.2.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/dompurify": "^3.0.5", "autoprefixer": "^10.4.21", "axios": "^1.7.2", "d3": "^7.8.5", + "dompurify": "^3.3.0", "jwt-decode": "^3.1.2", "oidc-client": "^1.11.5", "react": "^18.3.1", @@ -24,9 +27,11 @@ "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.3.1", "react-flow-renderer": "^10.3.17", + "react-markdown": "^10.1.0", "react-pdf": "^10.1.0", "react-router-dom": "^6.24.0", "react-scripts": "5.0.1", + "remark-gfm": "^4.0.1", "tailwindcss": "^3.4.17", "web-vitals": "^2.1.4" }, @@ -2053,6 +2058,31 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "license": "MIT" }, + "node_modules/@carbon/icon-helpers": { + "version": "10.68.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.68.0.tgz", + "integrity": "sha512-fYor/Fs0RLtPMh2KRyPYR83JKoWbAmoQ7tCIW0QQcwYcIu82tvy7uT8vTNhda21w3uueOp70p6rYyY6vYjuRZA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@ibm/telemetry-js": "^1.5.0" + } + }, + "node_modules/@carbon/icons-react": { + "version": "11.69.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.69.0.tgz", + "integrity": "sha512-4ourN8a6IZD/eCCbsFYMQytUkmsMfcVCk/SxX2QyCCgy1jQlMVl5pn9koRkylblpV8n/4yn86XYZqw1Ec3zuDA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@carbon/icon-helpers": "^10.68.0", + "@ibm/telemetry-js": "^1.5.0", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">=16" + } + }, "node_modules/@csstools/normalize.css": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", @@ -2533,6 +2563,15 @@ "deprecated": "Use @eslint/object-schema instead", "license": "BSD-3-Clause" }, + "node_modules/@ibm/telemetry-js": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@ibm/telemetry-js/-/telemetry-js-1.10.2.tgz", + "integrity": "sha512-F8+/NNUwtm8BuFz18O9KPvIFTFDo8GUSoyhPxPjEpk7nEyEzWGfhIiEPhL00B2NdHRLDSljh3AiCfSnL/tutiQ==", + "license": "Apache-2.0", + "bin": { + "ibmtelemetry": "dist/collect.js" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -5396,6 +5435,24 @@ "@types/d3-selection": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -5422,6 +5479,15 @@ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/express": { "version": "4.17.23", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", @@ -5473,6 +5539,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -5572,12 +5647,27 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "license": "MIT" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.5.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", @@ -5740,6 +5830,12 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -7069,6 +7165,16 @@ "babel-plugin-transform-react-remove-prop-types": "^0.4.24" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -7445,6 +7551,16 @@ "node": ">=4" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -7467,6 +7583,46 @@ "node": ">=10" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/check-types": { "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", @@ -7729,6 +7885,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -8845,6 +9011,19 @@ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -9060,6 +9239,19 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -9206,6 +9398,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", + "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -10237,6 +10438,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -10385,6 +10596,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -11339,6 +11556,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -11484,6 +11741,16 @@ "node": ">= 12" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-webpack-plugin": { "version": "5.6.4", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.4.tgz", @@ -11790,6 +12057,12 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -11822,6 +12095,30 @@ "node": ">= 10" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -11983,6 +12280,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -12070,6 +12377,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -15561,6 +15878,16 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "license": "MIT" }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -15651,6 +15978,16 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -15660,83 +15997,928 @@ "node": ">= 0.4" } }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "license": "CC0-1.0" + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", "dependencies": { - "fs-monkey": "^1.0.4" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "engines": { - "node": ">= 4.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge-refs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", - "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", - "funding": { - "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge-stream": { + "node_modules/mdast-util-gfm-strikethrough": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "license": "MIT", - "engines": { + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-refs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", + "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">= 0.6" + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -16408,6 +17590,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -18080,6 +19287,16 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -18467,6 +19684,33 @@ "react-dom": "16 || 17 || 18" } }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-pdf": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.1.0.tgz", @@ -18819,6 +20063,72 @@ "node": ">= 0.10" } }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -19785,6 +21095,16 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "license": "MIT" }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -20153,6 +21473,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -20259,6 +21593,24 @@ "webpack": "^5.0.0" } }, + "node_modules/style-to-js": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.18.tgz", + "integrity": "sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.11" + } + }, + "node_modules/style-to-object": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.11.tgz", + "integrity": "sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -20872,6 +22224,26 @@ "node": ">=8" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -21157,6 +22529,37 @@ "node": ">=4" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -21169,6 +22572,74 @@ "node": ">=8" } }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -21335,6 +22806,34 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -22324,6 +23823,16 @@ "optional": true } } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/frontend/package.json b/frontend/package.json index cc737b11..715a755f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,14 +4,17 @@ "private": true, "dependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@carbon/icons-react": "^11.69.0", "@headlessui/react": "^2.2.9", "@heroicons/react": "^2.2.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/dompurify": "^3.0.5", "autoprefixer": "^10.4.21", "axios": "^1.7.2", "d3": "^7.8.5", + "dompurify": "^3.3.0", "jwt-decode": "^3.1.2", "oidc-client": "^1.11.5", "react": "^18.3.1", @@ -19,9 +22,11 @@ "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.3.1", "react-flow-renderer": "^10.3.17", + "react-markdown": "^10.1.0", "react-pdf": "^10.1.0", "react-router-dom": "^6.24.0", "react-scripts": "5.0.1", + "remark-gfm": "^4.0.1", "tailwindcss": "^3.4.17", "web-vitals": "^2.1.4" }, diff --git a/frontend/src/components/search/ChainOfThoughtAccordion.tsx b/frontend/src/components/search/ChainOfThoughtAccordion.tsx new file mode 100644 index 00000000..ca394201 --- /dev/null +++ b/frontend/src/components/search/ChainOfThoughtAccordion.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { ChevronDown, ChevronUp, Connect } from '@carbon/icons-react'; +import './SearchInterface.scss'; + +/** + * Security Note: Content from LLM responses is rendered as text content (not HTML). + * React automatically escapes text content, providing XSS protection. + * If HTML rendering is needed in the future, use sanitizeHtml() from utils/sanitize.ts + */ + +interface ReasoningStep { + step_number: number; + question: string; + intermediate_answer: string; + reasoning?: string; + confidence_score?: number; + sources_used?: number; +} + +interface ChainOfThoughtAccordionProps { + cotOutput: { + reasoning_steps?: ReasoningStep[]; + final_synthesis?: string; + [key: string]: any; + }; + isOpen: boolean; + onToggle: () => void; +} + +const ChainOfThoughtAccordion: React.FC = ({ cotOutput, isOpen, onToggle }) => { + const reasoningSteps = cotOutput?.reasoning_steps || []; + const finalSynthesis = cotOutput?.final_synthesis; + + return ( +
+ + + {isOpen && ( +
+
+
+ {reasoningSteps.map((step, index) => ( + +
+
+
{step.step_number}
+
{step.question}
+ {step.sources_used && ( +
+ {step.sources_used} sources +
+ )} +
+ +
+
+
Answer
+
{step.intermediate_answer}
+
+ + {step.reasoning && ( +
+
Reasoning
+
{step.reasoning}
+
+ )} + + {step.confidence_score !== undefined && ( +
+
Confidence
+
{Math.round(step.confidence_score * 100)}%
+
+ )} +
+
+ + {index < reasoningSteps.length - 1 && ( +
+
+
+
+ )} +
+ ))} +
+ + {finalSynthesis && ( +
+
Final Synthesis
+
{finalSynthesis}
+
+ )} +
+
+ )} +
+ ); +}; + +export default ChainOfThoughtAccordion; diff --git a/frontend/src/components/search/ChainOfThoughtPanel.tsx b/frontend/src/components/search/ChainOfThoughtPanel.tsx new file mode 100644 index 00000000..c0e67583 --- /dev/null +++ b/frontend/src/components/search/ChainOfThoughtPanel.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import ChainOfThoughtStep from './ChainOfThoughtStep'; + +/** + * Security Note: Content from LLM responses is rendered as text content (not HTML). + * React automatically escapes text content, providing XSS protection. + * If HTML rendering is needed in the future, use sanitizeHtml() from utils/sanitize.ts + */ + +interface ChainOfThoughtPanelProps { + cotOutput: { + enabled: boolean; + steps: Array<{ + step_number: number; + question: string; + answer: string; + reasoning?: string; + sources_used: number; + }>; + total_steps: number; + final_synthesis?: string; + }; +} + +/** + * ChainOfThoughtPanel + * + * Container for displaying Chain-of-Thought reasoning steps. + * Shows all reasoning steps with visual connectors and final synthesis. + * + * Implements GitHub Issue #274 (display Chain-of-Thought reasoning steps). + */ +const ChainOfThoughtPanel: React.FC = ({ cotOutput }) => { + if (!cotOutput.enabled || cotOutput.steps.length === 0) { + return null; + } + + return ( +
+ {/* Panel Header */} +
+

Reasoning Process

+ + {cotOutput.total_steps} {cotOutput.total_steps === 1 ? 'step' : 'steps'} + +
+ + {/* Steps */} +
+ {cotOutput.steps.map((step, index) => ( + + ))} +
+ + {/* Final Synthesis */} + {cotOutput.final_synthesis && ( +
+
Final Synthesis:
+
{cotOutput.final_synthesis}
+
+ )} +
+ ); +}; + +export default ChainOfThoughtPanel; diff --git a/frontend/src/components/search/ChainOfThoughtStep.tsx b/frontend/src/components/search/ChainOfThoughtStep.tsx new file mode 100644 index 00000000..dc54c290 --- /dev/null +++ b/frontend/src/components/search/ChainOfThoughtStep.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { Document } from '@carbon/icons-react'; + +interface ChainOfThoughtStepProps { + step: { + step_number: number; + question: string; + answer: string; + reasoning?: string; + sources_used: number; + }; + isLast: boolean; +} + +/** + * ChainOfThoughtStep + * + * Individual step in Chain-of-Thought reasoning display. + * Shows the question, answer, reasoning, and source count for each step. + * + * Implements GitHub Issue #274 (display Chain-of-Thought reasoning steps). + */ +const ChainOfThoughtStep: React.FC = ({ step, isLast }) => { + return ( +
+ {/* Step Header */} +
+
+ {step.step_number} +
+
+ Step {step.step_number} +
+ {step.sources_used > 0 && ( +
+ + {step.sources_used} {step.sources_used === 1 ? 'source' : 'sources'} +
+ )} +
+ + {/* Step Content */} +
+
+
Question:
+
{step.question}
+
+ +
+
Answer:
+
{step.answer}
+
+ + {step.reasoning && ( +
+
Reasoning:
+
{step.reasoning}
+
+ )} +
+ + {/* Visual connector to next step */} + {!isLast && ( +
+
+
+
+ )} +
+ ); +}; + +export default ChainOfThoughtStep; diff --git a/frontend/src/components/search/LightweightSearchInterface.tsx b/frontend/src/components/search/LightweightSearchInterface.tsx index dc8524b8..8102b179 100644 --- a/frontend/src/components/search/LightweightSearchInterface.tsx +++ b/frontend/src/components/search/LightweightSearchInterface.tsx @@ -1,54 +1,23 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { useLocation } from 'react-router-dom'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; import { PaperAirplaneIcon, - FunnelIcon, - DocumentIcon, - ShareIcon, - BookmarkIcon, - ChevronDownIcon, - ChevronUpIcon, - CpuChipIcon, - LightBulbIcon, - ExclamationTriangleIcon, - InformationCircleIcon, - ClockIcon, - ChatBubbleLeftRightIcon, - PlusIcon, - TrashIcon, - ArchiveBoxIcon, - PencilIcon, - ArrowUturnLeftIcon, LinkIcon, - EyeIcon, - DocumentTextIcon, - ArrowDownTrayIcon, } from '@heroicons/react/24/outline'; import { useNotification } from '../../contexts/NotificationContext'; -import SourceList from './SourceList'; +import MessageMetadataFooter from './MessageMetadataFooter'; +import SourceModal from './SourceModal'; +import SourcesAccordion from './SourcesAccordion'; +import ChainOfThoughtAccordion from './ChainOfThoughtAccordion'; +import TokenAnalysisAccordion from './TokenAnalysisAccordion'; +import './SearchInterface.scss'; // Import API client and WebSocket client -import apiClient, { Collection, CollectionDocument, ConversationSession, ConversationMessage, CreateConversationInput } from '../../services/apiClient'; +import apiClient, { Collection, CollectionDocument, ConversationSession, CreateConversationInput } from '../../services/apiClient'; import websocketClient, { ChatMessage as WSChatMessage, ConnectionStatus } from '../../services/websocketClient'; -interface SearchResult { - id: string; - title: string; - content: string; - source: string; - score: number; - metadata: { - author?: string; - date?: string; - type?: string; - tags?: string[]; - }; - highlights: { - text: string; - score: number; - }[]; -} - // Use ChatMessage from WebSocket client type ChatMessage = WSChatMessage; @@ -57,12 +26,13 @@ const LightweightSearchInterface: React.FC = () => { const location = useLocation(); const [query, setQuery] = useState(''); const [isLoading, setIsLoading] = useState(false); - const [showFilters, setShowFilters] = useState(false); const [selectedCollection, setSelectedCollection] = useState('all'); const [messages, setMessages] = useState([]); const [showSources, setShowSources] = useState<{ [key: string]: boolean }>({}); const [showTokens, setShowTokens] = useState<{ [key: string]: boolean }>({}); const [showCoT, setShowCoT] = useState<{ [key: string]: boolean }>({}); + const [sourceModalOpen, setSourceModalOpen] = useState(null); + const [sourceModalSources, setSourceModalSources] = useState([]); const [collections, setCollections] = useState([]); const [connectionStatus, setConnectionStatus] = useState({ connected: false, connecting: false }); const [isTyping, setIsTyping] = useState(false); @@ -71,7 +41,6 @@ const LightweightSearchInterface: React.FC = () => { // Message referencing state const [referencedMessage, setReferencedMessage] = useState(null); - const [showMessageSelector, setShowMessageSelector] = useState(false); // Summary state const [showSummaryModal, setShowSummaryModal] = useState(false); @@ -83,39 +52,9 @@ const LightweightSearchInterface: React.FC = () => { const [currentConversation, setCurrentConversation] = useState(null); const [showNewConversationModal, setShowNewConversationModal] = useState(false); const [newConversationName, setNewConversationName] = useState(''); - const [isLoadingConversations, setIsLoadingConversations] = useState(false); - - // Load collection data from location state (passed from collections page) - useEffect(() => { - if (location.state) { - const { collectionId, collectionName, collectionDescription } = location.state as { - collectionId?: string; - collectionName?: string; - collectionDescription?: string; - }; - - if (collectionId && collectionName) { - setCurrentCollectionId(collectionId); - setCurrentCollectionName(collectionName); - setSelectedCollection(collectionId); - - addNotification('info', 'Collection Loaded', `Now chatting with ${collectionName}`); - } - } - }, [location.state, addNotification]); - - // Check for session parameter in URL - useEffect(() => { - const urlParams = new URLSearchParams(location.search); - const sessionId = urlParams.get('session'); - if (sessionId) { - // Load the specific conversation - loadSpecificConversation(sessionId); - } - }, [location.search]); - - const loadSpecificConversation = async (sessionId: string) => { + // Load specific conversation - wrapped in useCallback to fix exhaustive-deps + const loadSpecificConversation = useCallback(async (sessionId: string) => { try { const conversation = await apiClient.getConversation(sessionId); setCurrentConversation(conversation); @@ -139,20 +78,46 @@ const LightweightSearchInterface: React.FC = () => { setCurrentCollectionName(collection.name); setSelectedCollection(conversation.collection_id); } catch (error) { - console.error('Failed to load collection:', error); + // Collection load failed - non-critical, continue } } catch (error) { - console.error('Failed to load conversation:', error); addNotification('error', 'Error', 'Failed to load conversation'); } - }; + }, [addNotification]); + + // Load collection data from location state (passed from collections page) + useEffect(() => { + if (location.state) { + const { collectionId, collectionName } = location.state as { + collectionId?: string; + collectionName?: string; + }; + + if (collectionId && collectionName) { + setCurrentCollectionId(collectionId); + setCurrentCollectionName(collectionName); + setSelectedCollection(collectionId); + + addNotification('info', 'Collection Loaded', `Now chatting with ${collectionName}`); + } + } + }, [location.state, addNotification]); + + // Check for session parameter in URL + useEffect(() => { + const urlParams = new URLSearchParams(location.search); + const sessionId = urlParams.get('session'); + + if (sessionId) { + loadSpecificConversation(sessionId); + } + }, [location.search, loadSpecificConversation]); // Load conversations for current collection useEffect(() => { const loadConversations = async () => { if (!currentCollectionId || currentCollectionId === 'all') return; - setIsLoadingConversations(true); try { const conversationsData = await apiClient.getConversations(undefined, currentCollectionId); setConversations(conversationsData); @@ -191,10 +156,7 @@ const LightweightSearchInterface: React.FC = () => { setMessages(chatMessages); } } catch (error) { - console.error('Error loading conversations:', error); addNotification('error', 'Loading Error', 'Failed to load conversations.'); - } finally { - setIsLoadingConversations(false); } }; @@ -223,7 +185,6 @@ const LightweightSearchInterface: React.FC = () => { ]; setCollections(formattedCollections); } catch (error) { - console.error('Error loading collections:', error); addNotification('error', 'Loading Error', 'Failed to load collections.'); } }; @@ -264,9 +225,7 @@ const LightweightSearchInterface: React.FC = () => { try { const currentUser = await apiClient.getCurrentUser(); userId = currentUser.id; - console.log('🔍 Using authenticated user ID:', userId); } catch (authError) { - console.error('Failed to get current user:', authError); // If auth fails, the search will likely fail too, but let's try anyway // The backend should handle the auth properly throw new Error('Authentication required. Please ensure you are logged in.'); @@ -279,7 +238,6 @@ const LightweightSearchInterface: React.FC = () => { // Use conversation endpoint if we have an active conversation (saves messages) if (activeConversation) { - console.log('🔍 Using conversation endpoint to save message history...'); const conversationMessage = await apiClient.sendConversationMessage(activeConversation.id, query); // Convert conversation message response to search response format @@ -299,7 +257,6 @@ const LightweightSearchInterface: React.FC = () => { }; } else { // Fallback to stateless search (does not save conversation) - console.log('🔍 No active conversation, using stateless search endpoint...'); searchResponse = await apiClient.search({ question: query, collection_id: collectionId, @@ -326,9 +283,6 @@ const LightweightSearchInterface: React.FC = () => { metadata: Record; }> = []; - console.log('🔍 Documents array:', searchResponse.documents); - console.log('🔍 Query Results array:', searchResponse.query_results); - // Prioritize query_results as they contain chunk-specific information with page numbers if (searchResponse.query_results && Array.isArray(searchResponse.query_results) && searchResponse.query_results.length > 0) { // Create a mapping of document_id to document_name from the documents array @@ -339,8 +293,6 @@ const LightweightSearchInterface: React.FC = () => { if (searchResponse.documents && searchResponse.documents.length > 0) { // Get all unique document IDs from query results const uniqueDocIds: string[] = Array.from(new Set(searchResponse.query_results.map((r: any) => r.chunk.document_id as string))); - console.log(`🔍 Unique document IDs:`, uniqueDocIds); - console.log(`🔍 Available documents:`, searchResponse.documents); // Map document IDs to document names (using order as a heuristic) uniqueDocIds.forEach((docId, index) => { @@ -348,28 +300,22 @@ const LightweightSearchInterface: React.FC = () => { const doc = searchResponse.documents[index]; if (doc && doc.document_name) { docIdToNameMap.set(docId, doc.document_name); - console.log(`🔍 Mapped ${docId} -> ${doc.document_name}`); } } }); } // Use query_results chunks - contains chunk-specific information with page numbers - sources = searchResponse.query_results.map((result: any, index: number) => { - console.log(`🔍 Query Result ${index}:`, result); - console.log(`🔍 Looking for document_id: ${result.chunk.document_id}`); - + sources = searchResponse.query_results.map((result: any) => { // Try to get document name from our mapping let documentName = 'Unknown Document'; if (docIdToNameMap.has(result.chunk.document_id)) { documentName = docIdToNameMap.get(result.chunk.document_id)!; - console.log(`🔍 Using mapped document name:`, documentName); } else { // Fallback: use the first document name as a default const firstDoc = searchResponse.documents?.[0]; if (firstDoc && firstDoc.document_name) { documentName = firstDoc.document_name; - console.log(`🔍 Using fallback document name:`, documentName); } } @@ -400,8 +346,6 @@ const LightweightSearchInterface: React.FC = () => { } else if (searchResponse.documents && Array.isArray(searchResponse.documents)) { // Fallback to documents format (may lack chunk-specific page numbers) sources = searchResponse.documents.map((doc: any, index: number) => { - console.log(`🔍 Document ${index}:`, doc); - // Try to extract document name and page number from the document const documentName = doc.document_name || doc.title || doc.name || `Document ${index + 1}`; const pageNumber = doc.page_number || doc.metadata?.page_number || 'Unknown'; @@ -435,27 +379,10 @@ const LightweightSearchInterface: React.FC = () => { cot_output: searchResponse.cot_output, }; - console.log('🔍 FULL API RESPONSE:', searchResponse); - console.log('🔍 API Response Keys:', Object.keys(searchResponse)); - console.log('🔍 CoT Output:', searchResponse.cot_output); - console.log('🔍 Token Warning:', searchResponse.token_warning); - console.log('🔍 Documents:', searchResponse.documents); - console.log('🔍 Sources:', searchResponse.sources); - console.log('🔍 Query Results:', (searchResponse as any).query_results); - console.log('🔍 REST API response data:', { - answer: searchResponse.answer ? `${searchResponse.answer.substring(0, 50)}...` : 'no answer', - sources: sources.length > 0 ? `${sources.length} sources` : 'no sources', - documents: searchResponse.documents ? `${searchResponse.documents.length} documents` : 'no documents', - query_results: searchResponse.query_results ? `${searchResponse.query_results.length} query results` : 'no query results', - token_warning: searchResponse.token_warning ? `has token warning: ${searchResponse.token_warning.message}` : 'no token warning', - cot_output: searchResponse.cot_output ? `CoT: ${searchResponse.cot_output.steps?.length || 0} steps` : 'no CoT' - }); - setMessages(prev => [...prev, assistantMessage]); setIsLoading(false); addNotification('success', 'Search Complete', 'Search completed successfully via REST API.'); } catch (error) { - console.error('REST API search failed:', error); setIsLoading(false); throw error; } @@ -496,7 +423,6 @@ const LightweightSearchInterface: React.FC = () => { setShowNewConversationModal(false); addNotification('success', 'Conversation Created', `Created new conversation: ${newConversation.session_name}`); } catch (error) { - console.error('Error creating conversation:', error); addNotification('error', 'Creation Error', 'Failed to create new conversation.'); } }; @@ -526,11 +452,11 @@ const LightweightSearchInterface: React.FC = () => { setMessages(chatMessages); addNotification('info', 'Conversation Switched', `Switched to ${conversation.session_name}`); } catch (error) { - console.error('Error switching conversation:', error); addNotification('error', 'Switch Error', 'Failed to switch conversation.'); } }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars const deleteConversation = async (conversationId: string) => { try { await apiClient.deleteConversation(conversationId); @@ -548,7 +474,6 @@ const LightweightSearchInterface: React.FC = () => { } addNotification('success', 'Conversation Deleted', 'Conversation deleted successfully.'); } catch (error) { - console.error('Error deleting conversation:', error); addNotification('error', 'Delete Error', 'Failed to delete conversation.'); } }; @@ -561,13 +486,13 @@ const LightweightSearchInterface: React.FC = () => { setShowSummaryModal(true); addNotification('success', 'Summary Generated', `Generated ${summaryType} summary for conversation.`); } catch (error) { - console.error('Error loading conversation summary:', error); addNotification('error', 'Summary Error', 'Failed to generate conversation summary.'); } finally { setSummaryLoading(false); } }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars const exportConversation = async (conversationId: string, format: string = 'json') => { try { const exportData = await apiClient.exportConversation(conversationId, format); @@ -588,7 +513,6 @@ const LightweightSearchInterface: React.FC = () => { addNotification('success', 'Export Complete', `Conversation exported as ${format.toUpperCase()}.`); } catch (error) { - console.error('Error exporting conversation:', error); addNotification('error', 'Export Error', 'Failed to export conversation.'); } }; @@ -633,7 +557,6 @@ const LightweightSearchInterface: React.FC = () => { setConversations(prev => [newConversation, ...prev]); addNotification('info', 'Conversation Created', `Created new conversation for your chat.`); } catch (error) { - console.error('Error creating conversation:', error); addNotification('error', 'Conversation Error', 'Failed to create conversation. Using temporary session.'); } } @@ -660,11 +583,8 @@ const LightweightSearchInterface: React.FC = () => { try { // Primary method: REST API (more reliable) - console.log('🔍 Attempting REST API search...'); await handleRestApiSearch(query, collectionId, activeConversation); } catch (restError) { - console.error('REST API search failed, trying WebSocket fallback:', restError); - // Fallback to WebSocket if available if (connectionStatus.connected) { try { @@ -673,12 +593,10 @@ const LightweightSearchInterface: React.FC = () => { timestamp: new Date().toISOString() }, currentConversation?.id); } catch (wsError) { - console.error('WebSocket search also failed:', wsError); addNotification('error', 'Search Error', 'Both REST API and WebSocket search failed. Please try again.'); setIsLoading(false); } } else { - console.error('No WebSocket connection available for fallback'); addNotification('error', 'Search Error', 'Search failed and no WebSocket connection available. Please try again.'); setIsLoading(false); } @@ -707,38 +625,33 @@ const LightweightSearchInterface: React.FC = () => { }; return ( -
-
- {/* Header */} -
-
-
-

- {currentCollectionName ? `Chat with ${currentCollectionName}` : 'Chat with Documents'} -

-

Chat with your document collections using natural language

-
-
-
- - {connectionStatus.connected ? 'Connected' : connectionStatus.connecting ? 'Connecting...' : 'Disconnected'} - -
+
+ {/* Fixed Header */} +
+
+
+

+ {currentCollectionName || 'Chat with Documents'} +

+
+
+
+ + {connectionStatus.connected ? 'Connected' : connectionStatus.connecting ? 'Connecting...' : 'Disconnected'} +
+
-
- {/* Main Chat Area */} -
-
- {/* Messages */} -
+ {/* Messages Area */} +
+
{messages.map((message) => ( -
-
+
+
{/* Referenced message indicator */} {referencedMessage?.id === message.id && ( -
+
This message is being referenced @@ -746,199 +659,88 @@ const LightweightSearchInterface: React.FC = () => {
)} -
{message.content}
- - {/* Message actions for referencing */} -
-
- - -
-
- {message.timestamp.toLocaleTimeString()} -
-
- - {message.type === 'assistant' && ( -
- {/* Sources Accordion */} - {message.sources && message.sources.length > 0 && ( -
- - - {showSources[message.id] && ( -
- -
- )} -
- )} - - {/* Token Usage Accordion */} - {message.token_warning && ( -
- - - {showTokens[message.id] && ( -
-
-
- Current Tokens: - {message.token_warning.current_tokens.toLocaleString()} -
-
- Limit: - {message.token_warning.limit_tokens.toLocaleString()} -
-
- Usage: - {message.token_warning.percentage_used.toFixed(1)}% -
-
- Type: - {message.token_warning.warning_type} -
-
-
-
- - {message.token_warning.message} -
- {message.token_warning.suggested_action && ( -
- Suggestion: {message.token_warning.suggested_action} -
- )} -
-
- )} -
- )} + {/* Message content and accordions stacked vertically */} +
+ {/* Main message content */} +
+
+ {message.type === 'assistant' ? ( + + {message.content} + + ) : ( +
{message.content}
+ )} +
- {/* Chain of Thought Accordion */} - {message.cot_output && message.cot_output.enabled && message.cot_output.steps.length > 0 && ( -
- - - {showCoT[message.id] && ( -
- {message.cot_output.steps.map((step, index) => ( -
-
-
- {step.step_number} -
-
Step {step.step_number}
- {step.sources_used > 0 && ( -
- - {step.sources_used} sources -
- )} -
-
-
-
Question:
-
{step.question}
-
-
-
Answer:
-
{step.answer}
-
- {step.reasoning && ( -
-
Reasoning:
-
{step.reasoning}
-
- )} -
-
- ))} - {message.cot_output.final_synthesis && ( -
-
Final Synthesis:
-
{message.cot_output.final_synthesis}
-
- )} -
- )} -
+ {/* Message Metadata Footer with Click Handlers */} + {message.type === 'assistant' && ( + toggleSources(message.id)} + onStepsClick={() => toggleCoT(message.id)} + onTokensClick={() => toggleTokens(message.id)} + /> )}
- )} + + {/* Accordions below message - Only show when opened */} + {message.type === 'assistant' && (showSources[message.id] || showCoT[message.id] || showTokens[message.id]) && ( +
+ {/* Sources Accordion - Only render when open */} + {showSources[message.id] && message.sources && message.sources.length > 0 && ( + toggleSources(message.id)} + /> + )} + + {/* Chain of Thought Accordion - Only render when open */} + {showCoT[message.id] && message.cot_output && message.cot_output.steps && message.cot_output.steps.length > 0 && ( + toggleCoT(message.id)} + /> + )} + + {/* Token Analysis Accordion - Only render when open */} + {showTokens[message.id] && message.metadata?.token_analysis && ( + toggleTokens(message.id)} + /> + )} +
+ )} +
))} {isLoading && ( -
-
+
+
-
- +
+ {isTyping ? 'Assistant is typing...' : 'Searching your collections...'}
)} -
+
+
- {/* Search Input */} -
+ {/* Fixed Input Area */} +
+
{/* Referenced message indicator */} {referencedMessage && (
@@ -963,27 +765,27 @@ const LightweightSearchInterface: React.FC = () => {
)} -
- setQuery(e.target.value)} - placeholder={referencedMessage ? "Ask a follow-up question..." : "Ask a question about your documents..."} - disabled={isLoading} - className="input-field flex-1 disabled:opacity-50" - /> + +
+ setQuery(e.target.value)} + placeholder={referencedMessage ? "Ask a follow-up question..." : "Ask a question about your documents..."} + disabled={isLoading} + className="flex-1 bg-transparent outline-none text-gray-900 placeholder-gray-400 disabled:opacity-50" + /> +
-
-
-
+
{/* New Conversation Modal */} {showNewConversationModal && ( @@ -1040,10 +842,20 @@ const LightweightSearchInterface: React.FC = () => {
)} - {/* Conversation Summary Modal */} - {showSummaryModal && conversationSummary && ( -
-
+ {/* Source Modal */} + { + setSourceModalOpen(null); + setSourceModalSources([]); + }} + sources={sourceModalSources} + /> + + {/* Conversation Summary Modal */} + {showSummaryModal && conversationSummary && ( +
+

Conversation Summary: {conversationSummary.session_name} @@ -1153,7 +965,6 @@ const LightweightSearchInterface: React.FC = () => {

)} -
); }; diff --git a/frontend/src/components/search/MessageMetadataFooter.tsx b/frontend/src/components/search/MessageMetadataFooter.tsx new file mode 100644 index 00000000..e5e5ff01 --- /dev/null +++ b/frontend/src/components/search/MessageMetadataFooter.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { Document, Connect, Time, ChartColumn } from '@carbon/icons-react'; + +interface MessageMetadataFooterProps { + sourcesCount?: number; + stepsCount?: number; + tokenCount?: number; + responseTime?: number; + onSourcesClick?: () => void; + onStepsClick?: () => void; + onTokensClick?: () => void; +} + +/** + * MessageMetadataFooter + * + * Displays clickable inline metadata for assistant messages showing: + * - Number of source documents used (clickable) + * - Number of Chain-of-Thought reasoning steps (clickable) + * - Total tokens used (clickable) + * - Response time in seconds + * + * Addresses GitHub Issues: #283 (token usage), #274 (CoT steps) + */ +const MessageMetadataFooter: React.FC = ({ + sourcesCount = 0, + stepsCount, + tokenCount, + responseTime, + onSourcesClick, + onStepsClick, + onTokensClick, +}) => { + // Only render if there's at least one piece of metadata to show + const hasMetadata = sourcesCount > 0 || (stepsCount && stepsCount > 0) || tokenCount || responseTime; + + if (!hasMetadata) { + return null; + } + + return ( +
+
+ {sourcesCount > 0 && ( + + )} + + {stepsCount && stepsCount > 0 && ( + + )} + + {tokenCount && tokenCount > 0 && ( + + )} + + {responseTime && responseTime > 0 && ( + + + )} +
+
+ ); +}; + +export default MessageMetadataFooter; diff --git a/frontend/src/components/search/SearchInterface.scss b/frontend/src/components/search/SearchInterface.scss new file mode 100644 index 00000000..ab0cf96c --- /dev/null +++ b/frontend/src/components/search/SearchInterface.scss @@ -0,0 +1,659 @@ +// SearchInterface.scss +// Styling for search interface components using Carbon Design System variables + +// ========================================== +// Message Metadata Footer +// ========================================== +.message-metadata-footer { + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +.metadata-items { + display: flex; + flex-wrap: wrap; + gap: 1rem; + align-items: center; +} + +.metadata-item { + display: flex; + align-items: center; + gap: 0.375rem; + font-size: 0.75rem; + color: #525252; // Carbon gray-70 +} + +.metadata-clickable { + background: none; + border: none; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background-color: #e0e0e0; // Carbon gray-20 + color: #0f62fe; // Carbon blue-60 + + .metadata-icon { + color: #0f62fe; // Carbon blue-60 + } + } + + &:active { + background-color: #c6c6c6; // Carbon gray-30 + } +} + +.metadata-icon { + flex-shrink: 0; + color: #0f62fe; // Carbon blue-60 +} + +.metadata-text { + white-space: nowrap; +} + +// Specific metadata item types +.metadata-sources .metadata-icon { + color: #0f62fe; // Carbon blue-60 +} + +.metadata-steps .metadata-icon { + color: #198038; // Carbon green-60 +} + +.metadata-tokens .metadata-icon { + color: #8a3ffc; // Carbon purple-60 +} + +.metadata-time .metadata-icon { + color: #f1c21b; // Carbon yellow-30 +} + +// ========================================== +// Accordion Components (Sources, CoT, Token Analysis) +// ========================================== +.source-accordion, +.cot-accordion, +.token-accordion { + margin-top: 1rem; + border: 1px solid #e0e0e0; // Carbon gray-20 + border-radius: 0.5rem; + background: #f4f4f4; // Carbon gray-10 + overflow: hidden; +} + +.source-accordion-header, +.cot-accordion-header, +.token-accordion-header { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 1.5rem; + background: none; + border: none; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: #e0e0e0; // Carbon gray-20 + } +} + +.source-accordion-title-section, +.cot-accordion-title-section, +.token-accordion-title-section { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.source-accordion-icon, +.cot-accordion-icon, +.token-accordion-icon { + color: #0f62fe; // Carbon blue-60 + flex-shrink: 0; +} + +.source-accordion-title, +.cot-accordion-title, +.token-accordion-title { + font-size: 1rem; + font-weight: 600; + color: #161616; // Carbon gray-100 +} + +.source-accordion-chevron, +.cot-accordion-chevron, +.token-accordion-chevron { + color: #525252; // Carbon gray-70 + flex-shrink: 0; +} + +.source-accordion-content, +.cot-accordion-content, +.token-accordion-content { + padding: 1rem 1.5rem; + border-top: 1px solid #e0e0e0; // Carbon gray-20 + background: white; +} + +// ========================================== +// Source Modal +// ========================================== +.source-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 1rem; +} + +.source-modal-content { + background: white; + border-radius: 0.5rem; + max-width: 900px; + width: 100%; + max-height: 85vh; + display: flex; + flex-direction: column; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2); +} + +.source-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1.5rem; + border-bottom: 1px solid #e0e0e0; // Carbon gray-20 +} + +.source-modal-title { + font-size: 1.25rem; + font-weight: 600; + color: #161616; // Carbon gray-100 + margin: 0; +} + +.source-modal-close { + background: none; + border: none; + cursor: pointer; + color: #525252; // Carbon gray-70 + padding: 0.25rem; + border-radius: 0.25rem; + transition: all 0.2s; + + &:hover { + background-color: #f4f4f4; // Carbon gray-10 + color: #161616; // Carbon gray-100 + } +} + +.source-modal-body { + flex: 1; + overflow-y: auto; + padding: 1.5rem; +} + +.source-modal-summary { + margin-bottom: 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid #e0e0e0; // Carbon gray-20 +} + +.source-modal-footer { + padding: 1rem 1.5rem; + border-top: 1px solid #e0e0e0; // Carbon gray-20 + display: flex; + justify-content: flex-end; +} + +// ========================================== +// Source Card (Redesigned) +// ========================================== +.source-card { + background: #f4f4f4; // Carbon gray-10 + border: 1px solid #e0e0e0; // Carbon gray-20 + border-radius: 0.5rem; + padding: 1rem; + margin-bottom: 1rem; + transition: all 0.2s ease-in-out; + + &:hover { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border-color: #c6c6c6; // Carbon gray-30 + } +} + +.source-card-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 0.75rem; + gap: 1rem; +} + +.source-card-title-section { + display: flex; + align-items: flex-start; + gap: 0.75rem; + flex: 1; + min-width: 0; +} + +.source-card-icon { + width: 1.5rem; + height: 1.5rem; + color: #0f62fe; // Carbon blue-60 + flex-shrink: 0; + margin-top: 0.125rem; +} + +.source-card-title { + font-size: 1.125rem; // 18px - PROMINENT + font-weight: 600; + color: #161616; // Carbon gray-100 + margin: 0; + line-height: 1.4; + word-break: break-word; +} + +// Confidence Badge - Prominent percentage display +.source-confidence-badge { + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem 1rem; + border-radius: 1.5rem; + font-weight: 700; + min-width: 4.5rem; + flex-shrink: 0; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.source-confidence-value { + font-size: 1.125rem; // 18px - LARGE and PROMINENT +} + +.source-confidence-high { + background-color: #24a148; // Carbon green-50 + color: white; +} + +.source-confidence-medium { + background-color: #f1c21b; // Carbon yellow-30 + color: #161616; // Carbon gray-100 +} + +.source-confidence-low { + background-color: #da1e28; // Carbon red-50 + color: white; +} + +.source-card-content { + margin-bottom: 0.75rem; +} + +.source-card-text { + font-size: 0.875rem; + color: #525252; // Carbon gray-70 + line-height: 1.5; + margin: 0; +} + +.source-card-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding-top: 0.75rem; + border-top: 1px solid #e0e0e0; // Carbon gray-20 +} + +.source-card-action { + display: flex; + align-items: center; + gap: 0.375rem; + font-size: 0.875rem; + color: #0f62fe; // Carbon blue-60 + background: none; + border: none; + cursor: pointer; + padding: 0.25rem; + border-radius: 0.25rem; + transition: all 0.2s; + text-decoration: none; + + &:hover { + color: #0043ce; // Carbon blue-70 + background-color: #e0e0e0; // Carbon gray-20 + } +} + +// ========================================== +// Chain of Thought Panel +// ========================================== +.cot-panel { + background: #f4f4f4; // Carbon gray-10 + border: 1px solid #e0e0e0; // Carbon gray-20 + border-radius: 0.5rem; + padding: 1rem; +} + +.cot-panel-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; + padding-bottom: 0.75rem; + border-bottom: 1px solid #e0e0e0; // Carbon gray-20 +} + +.cot-panel-title { + font-size: 1rem; + font-weight: 600; + color: #161616; // Carbon gray-100 + margin: 0; +} + +.cot-panel-count { + font-size: 0.875rem; + color: #525252; // Carbon gray-70 + background: #e0e0e0; // Carbon gray-20 + padding: 0.25rem 0.75rem; + border-radius: 1rem; +} + +.cot-panel-steps { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +// Chain of Thought Step +.cot-step { + position: relative; +} + +.cot-step-header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.cot-step-number { + width: 2rem; + height: 2rem; + border-radius: 50%; + background: #0f62fe; // Carbon blue-60 + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 0.875rem; + flex-shrink: 0; +} + +.cot-step-title { + font-weight: 600; + color: #161616; // Carbon gray-100 + font-size: 0.875rem; +} + +.cot-step-sources { + display: flex; + align-items: center; + gap: 0.375rem; + font-size: 0.75rem; + color: #525252; // Carbon gray-70 + margin-left: auto; +} + +.cot-step-sources-icon { + color: #0f62fe; // Carbon blue-60 +} + +.cot-step-content { + background: white; + border-radius: 0.375rem; + padding: 0.75rem; + margin-left: 2.75rem; +} + +.cot-step-section { + margin-bottom: 0.75rem; + + &:last-child { + margin-bottom: 0; + } +} + +.cot-step-label { + font-weight: 600; + font-size: 0.75rem; + color: #161616; // Carbon gray-100 + margin-bottom: 0.25rem; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.cot-step-text { + font-size: 0.875rem; + color: #525252; // Carbon gray-70 + line-height: 1.5; +} + +.cot-step-reasoning { + font-size: 0.8125rem; + color: #6f6f6f; // Carbon gray-60 + line-height: 1.5; + font-style: italic; +} + +.cot-step-connector { + display: flex; + flex-direction: column; + align-items: center; + margin: 0.5rem 0; + margin-left: 1rem; +} + +.cot-step-connector-line { + width: 2px; + height: 1.5rem; + background: #c6c6c6; // Carbon gray-30 +} + +.cot-step-connector-arrow { + color: #0f62fe; // Carbon blue-60 + font-size: 1.25rem; + font-weight: bold; +} + +.cot-panel-synthesis { + margin-top: 1rem; + padding: 1rem; + background: #d0e2ff; // Carbon blue-10 + border: 1px solid #a6c8ff; // Carbon blue-20 + border-radius: 0.375rem; +} + +.cot-panel-synthesis-label { + font-weight: 600; + font-size: 0.75rem; + color: #002d9c; // Carbon blue-80 + margin-bottom: 0.5rem; + text-transform: uppercase; +} + +.cot-panel-synthesis-text { + font-size: 0.875rem; + color: #0043ce; // Carbon blue-70 + line-height: 1.5; +} + +// ========================================== +// Token Usage Panel +// ========================================== +.token-usage-panel { + border-radius: 0.5rem; + padding: 1rem; + border: 1px solid; +} + +.token-usage-info { + background: #d0e2ff; // Carbon blue-10 + border-color: #a6c8ff; // Carbon blue-20 +} + +.token-usage-warning { + background: #fcf4d6; // Carbon yellow-10 + border-color: #f1c21b; // Carbon yellow-30 +} + +.token-usage-critical { + background: #fff1f1; // Carbon red-10 + border-color: #ffc0cb; // Lighter red +} + +.token-usage-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; +} + +.token-usage-title { + font-size: 1rem; + font-weight: 600; + color: #161616; // Carbon gray-100 + margin: 0; +} + +.token-usage-progress-container { + width: 100%; + height: 0.5rem; + background: #e0e0e0; // Carbon gray-20 + border-radius: 0.25rem; + overflow: hidden; + margin-bottom: 0.75rem; +} + +.token-usage-progress-bar { + height: 100%; + background: linear-gradient(90deg, #24a148 0%, #f1c21b 70%, #da1e28 100%); + transition: width 0.3s ease; +} + +.token-usage-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.token-usage-stat { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.token-usage-stat-label { + font-size: 0.75rem; + color: #525252; // Carbon gray-70 + font-weight: 500; +} + +.token-usage-stat-value { + font-size: 0.875rem; + color: #161616; // Carbon gray-100 + font-weight: 600; +} + +.token-usage-message { + display: flex; + align-items: flex-start; + gap: 0.5rem; + font-size: 0.8125rem; + color: #525252; // Carbon gray-70 + padding: 0.5rem; + background: rgba(255, 255, 255, 0.5); + border-radius: 0.25rem; +} + +.token-usage-suggestion { + margin-top: 0.5rem; + font-size: 0.8125rem; + color: #0043ce; // Carbon blue-70 + padding: 0.5rem; + background: rgba(255, 255, 255, 0.5); + border-radius: 0.25rem; +} + +// ========================================== +// Responsive Design +// ========================================== +@media (max-width: 640px) { + .metadata-items { + gap: 0.5rem; + } + + .metadata-item { + font-size: 0.6875rem; // 11px + } + + .source-modal-content { + max-height: 95vh; + margin: 0.5rem; + } + + .source-modal-header, + .source-modal-body, + .source-modal-footer { + padding: 1rem; + } + + .source-card-header { + flex-direction: column; + align-items: flex-start; + } + + .source-card-title { + font-size: 1rem; // Slightly smaller on mobile + } + + .source-confidence-badge { + align-self: flex-start; + } + + .source-card-footer { + flex-direction: column; + align-items: flex-start; + } + + .cot-step-content { + margin-left: 0; + } + + .cot-step-connector { + margin-left: 0; + } + + .token-usage-stats { + grid-template-columns: 1fr; + gap: 0.5rem; + } +} diff --git a/frontend/src/components/search/SourceCard.tsx b/frontend/src/components/search/SourceCard.tsx index 26fc546d..1bf43dfb 100644 --- a/frontend/src/components/search/SourceCard.tsx +++ b/frontend/src/components/search/SourceCard.tsx @@ -1,6 +1,11 @@ import React, { useState } from 'react'; import { ChevronDownIcon, ChevronUpIcon, DocumentTextIcon, LinkIcon } from '@heroicons/react/24/outline'; +/** + * Security Note: Source content is rendered as text content (not HTML). + * React automatically escapes text content, providing XSS protection. + */ + export interface Source { document_name: string; content: string; @@ -15,43 +20,60 @@ interface SourceCardProps { source: Source; } +/** + * SourceCard - Redesigned + * + * Displays source documents with prominent document names and confidence scores. + * Confidence scores are shown as large, prominent percentage badges. + * + * Implements GitHub Issue #275 (display source documents with confidence scores) + * and #285 (improve source document card display). + */ const SourceCard: React.FC = ({ source }) => { const [isExpanded, setIsExpanded] = useState(false); const score = source.metadata?.score; - const scoreColor = - score && score >= 0.8 - ? 'bg-green-100 text-green-800' - : score && score >= 0.6 - ? 'bg-yellow-100 text-yellow-800' - : 'bg-red-100 text-red-800'; + const confidencePercentage = score ? Math.round(score * 100) : null; + + // Confidence badge styling based on score + const getConfidenceBadgeClass = () => { + if (!confidencePercentage) return ''; + if (confidencePercentage >= 80) return 'source-confidence-high'; + if (confidencePercentage >= 60) return 'source-confidence-medium'; + return 'source-confidence-low'; + }; return ( -
-
-
-
- -

- {source.document_name} -

-
+
+
+ {/* Document name - PRIMARY visual element */} +
+ +

+ {source.document_name} +

- {score && ( -
- Score: {score.toFixed(2)} + + {/* Confidence badge - Prominent percentage display */} + {confidencePercentage !== null && ( +
+ {confidencePercentage}%
)}
-
-

{source.content}

+ {/* Content preview */} +
+

+ {isExpanded ? source.content : `${source.content.substring(0, 200)}${source.content.length > 200 ? '...' : ''}`} +

-
+ {/* Actions footer */} +
{source.metadata.url && ( - - - Source Link - + + + Source Link + )}
- - {isExpanded && ( -
-
Full Text
-
-            {source.content}
-          
-
- )}
); }; -export default SourceCard; \ No newline at end of file +export default SourceCard; diff --git a/frontend/src/components/search/SourceModal.tsx b/frontend/src/components/search/SourceModal.tsx new file mode 100644 index 00000000..5c227c7c --- /dev/null +++ b/frontend/src/components/search/SourceModal.tsx @@ -0,0 +1,114 @@ +import React, { useEffect, useRef } from 'react'; +import { XMarkIcon } from '@heroicons/react/24/outline'; +import SourceList from './SourceList'; +import { Source } from './SourceCard'; + +interface SourceModalProps { + isOpen: boolean; + onClose: () => void; + sources: Source[]; +} + +/** + * SourceModal + * + * Modal overlay component for displaying source documents in detail. + * Shows a list of source documents with confidence scores and metadata. + * + * Accessibility features: + * - Escape key to close + * - Focus trap (focus returns to trigger on close) + * - ARIA attributes (role="dialog", aria-modal, aria-labelledby) + * - Body scroll prevention when open + * + * Addresses GitHub Issue #275 (display source documents with confidence scores) + * and #285 (improve source document card display). + */ +const SourceModal: React.FC = ({ isOpen, onClose, sources }) => { + const modalRef = useRef(null); + + // Handle Escape key and focus management + useEffect(() => { + if (!isOpen) return; + + // Handle Escape key + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onClose(); + } + }; + + // Prevent body scroll + document.body.style.overflow = 'hidden'; + + // Focus modal when opened + modalRef.current?.focus(); + + // Add event listener + document.addEventListener('keydown', handleEscape); + + // Cleanup + return () => { + document.removeEventListener('keydown', handleEscape); + document.body.style.overflow = 'unset'; + }; + }, [isOpen, onClose]); + + if (!isOpen) return null; + + return ( +
+
e.stopPropagation()} + role="dialog" + aria-modal="true" + aria-labelledby="source-modal-title" + tabIndex={-1} + > + {/* Modal Header */} +
+

Source Documents

+ +
+ + {/* Modal Body */} +
+ {sources && sources.length > 0 ? ( + <> +
+

+ Found {sources.length} relevant {sources.length === 1 ? 'source' : 'sources'} +

+
+ + + ) : ( +
+ No sources available for this response. +
+ )} +
+ + {/* Modal Footer */} +
+ +
+
+
+ ); +}; + +export default SourceModal; diff --git a/frontend/src/components/search/SourcesAccordion.tsx b/frontend/src/components/search/SourcesAccordion.tsx new file mode 100644 index 00000000..16185773 --- /dev/null +++ b/frontend/src/components/search/SourcesAccordion.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { ChevronDown, ChevronUp, Document } from '@carbon/icons-react'; +import './SearchInterface.scss'; + +interface Source { + document_name: string; + content: string; + metadata: { + score?: number; + [key: string]: any; + }; +} + +interface SourcesAccordionProps { + sources: Source[]; + isOpen: boolean; + onToggle: () => void; +} + +const SourcesAccordion: React.FC = ({ sources, isOpen, onToggle }) => { + const getConfidenceLevel = (score: number): 'high' | 'medium' | 'low' => { + if (score >= 0.7) return 'high'; + if (score >= 0.5) return 'medium'; + return 'low'; + }; + + const formatConfidence = (score: number): string => { + return `${Math.round(score * 100)}%`; + }; + + return ( +
+ + + {isOpen && ( +
+ {sources.map((source, index) => { + const confidence = source.metadata?.score || 0; + const confidenceLevel = getConfidenceLevel(confidence); + + return ( +
+
+
+ +

{source.document_name}

+
+
+ + {formatConfidence(confidence)} + +
+
+ +
+

+ {source.content} +

+
+ + {source.metadata && Object.keys(source.metadata).length > 0 && ( +
+
+ {source.metadata.page_number && ( + Page {source.metadata.page_number} + )} + {source.metadata.chunk_id && ( + Chunk {source.metadata.chunk_id} + )} +
+
+ )} +
+ ); + })} +
+ )} +
+ ); +}; + +export default SourcesAccordion; diff --git a/frontend/src/components/search/TokenAnalysisAccordion.tsx b/frontend/src/components/search/TokenAnalysisAccordion.tsx new file mode 100644 index 00000000..2d73e32f --- /dev/null +++ b/frontend/src/components/search/TokenAnalysisAccordion.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { ChevronDown, ChevronUp, ChartColumn } from '@carbon/icons-react'; +import './SearchInterface.scss'; + +interface TokenAnalysis { + query_tokens: number; + context_tokens: number; + response_tokens: number; + system_tokens: number; + total_this_turn: number; + conversation_total: number; +} + +interface TokenAnalysisAccordionProps { + tokenAnalysis: TokenAnalysis; + isOpen: boolean; + onToggle: () => void; +} + +const TokenAnalysisAccordion: React.FC = ({ tokenAnalysis, isOpen, onToggle }) => { + const formatNumber = (num: number): string => { + return num.toLocaleString(); + }; + + const calculatePercentage = (value: number): number => { + if (tokenAnalysis.total_this_turn === 0) return 0; + return Math.round((value / tokenAnalysis.total_this_turn) * 100); + }; + + return ( +
+ + + {isOpen && ( +
+
+
+

This Turn Breakdown

+
+ +
+
+
Query Tokens
+
+ {formatNumber(tokenAnalysis.query_tokens)} + + ({calculatePercentage(tokenAnalysis.query_tokens)}%) + +
+
+ +
+
Context Tokens
+
+ {formatNumber(tokenAnalysis.context_tokens)} + + ({calculatePercentage(tokenAnalysis.context_tokens)}%) + +
+
+ +
+
Response Tokens
+
+ {formatNumber(tokenAnalysis.response_tokens)} + + ({calculatePercentage(tokenAnalysis.response_tokens)}%) + +
+
+ +
+
System Tokens
+
+ {formatNumber(tokenAnalysis.system_tokens)} + + ({calculatePercentage(tokenAnalysis.system_tokens)}%) + +
+
+ +
+
Total This Turn
+
+ {formatNumber(tokenAnalysis.total_this_turn)} +
+
+ +
+
Conversation Total
+
+ {formatNumber(tokenAnalysis.conversation_total)} +
+
+
+ + {/* Progress bar showing token usage */} +
+
+
+
+
+ )} +
+ ); +}; + +export default TokenAnalysisAccordion; diff --git a/frontend/src/components/search/TokenUsagePanel.tsx b/frontend/src/components/search/TokenUsagePanel.tsx new file mode 100644 index 00000000..65c980e2 --- /dev/null +++ b/frontend/src/components/search/TokenUsagePanel.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { ExclamationTriangleIcon, InformationCircleIcon } from '@heroicons/react/24/outline'; + +interface TokenUsagePanelProps { + tokenWarning: { + current_tokens: number; + limit_tokens: number; + percentage_used: number; + severity: 'info' | 'warning' | 'critical'; + message: string; + suggested_action?: string; + warning_type: string; + }; +} + +/** + * TokenUsagePanel + * + * Displays token usage with progress bar and warnings. + * Implements GitHub Issue #283 (token usage visualization). + */ +const TokenUsagePanel: React.FC = ({ tokenWarning }) => { + const getSeverityColor = () => { + switch (tokenWarning.severity) { + case 'critical': return 'token-usage-critical'; + case 'warning': return 'token-usage-warning'; + default: return 'token-usage-info'; + } + }; + + return ( +
+ {/* Header */} +
+

Token Usage

+ {tokenWarning.severity === 'critical' && } + {tokenWarning.severity === 'warning' && } + {tokenWarning.severity === 'info' && } +
+ + {/* Progress Bar */} +
+
+
+ + {/* Stats Grid */} +
+
+ Current: + {tokenWarning.current_tokens.toLocaleString()} +
+
+ Limit: + {tokenWarning.limit_tokens.toLocaleString()} +
+
+ Usage: + {tokenWarning.percentage_used.toFixed(1)}% +
+
+ + {/* Message */} +
+ + {tokenWarning.message} +
+ + {/* Suggested Action */} + {tokenWarning.suggested_action && ( +
+ Suggestion: {tokenWarning.suggested_action} +
+ )} +
+ ); +}; + +export default TokenUsagePanel; diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts index 4167bf7b..b8a1fb13 100644 --- a/frontend/src/services/apiClient.ts +++ b/frontend/src/services/apiClient.ts @@ -163,6 +163,14 @@ interface ConversationMessage { model_used?: string; confidence_score?: number; context_length?: number; + token_analysis?: { + query_tokens?: number; + context_tokens?: number; + response_tokens?: number; + system_tokens?: number; + total_this_turn?: number; + conversation_total?: number; + }; }; token_count?: number; execution_time?: number; @@ -172,6 +180,7 @@ interface ConversationMessage { content: string; metadata: Record; }>; + cot_output?: ChainOfThoughtOutput; } interface SessionStatistics { @@ -861,7 +870,8 @@ class ApiClient { token_count: message.token_count, execution_time: message.execution_time, token_warning: message.token_warning, - sources: message.sources + sources: message.sources, + cot_output: message.cot_output })); } @@ -928,7 +938,8 @@ class ApiClient { token_count: message.token_count, execution_time: message.execution_time, token_warning: message.token_warning, - sources: message.sources + sources: message.sources, + cot_output: message.cot_output }; } diff --git a/frontend/src/utils/sanitize.ts b/frontend/src/utils/sanitize.ts new file mode 100644 index 00000000..bf3211c9 --- /dev/null +++ b/frontend/src/utils/sanitize.ts @@ -0,0 +1,36 @@ +import DOMPurify from 'dompurify'; + +/** + * Sanitizes HTML content to prevent XSS attacks + * + * @param dirty - The untrusted HTML string to sanitize + * @returns Sanitized HTML safe for rendering + * + * Usage: + * ```tsx + *
+ * ``` + */ +export function sanitizeHtml(dirty: string): string { + return DOMPurify.sanitize(dirty, { + ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'ul', 'ol', 'li', 'code', 'pre'], + ALLOWED_ATTR: ['href', 'target', 'rel'], + ALLOW_DATA_ATTR: false, + }); +} + +/** + * Sanitizes text content - React escapes by default, but this provides explicit sanitization + * + * @param text - The untrusted text to sanitize + * @returns Sanitized text safe for rendering + */ +export function sanitizeText(text: string): string { + // React escapes text content by default, but we can add extra protection + // Remove any potential script tags or dangerous patterns + return text + .replace(/)<[^<]*)*<\/script>/gi, '') + .replace(/)<[^<]*)*<\/iframe>/gi, '') + .replace(/javascript:/gi, '') + .replace(/on\w+\s*=/gi, ''); // Remove event handlers like onclick= +}