diff --git a/Dockerfile b/Dockerfile index 03f322cb8..057f347c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,8 @@ RUN --mount=type=cache,id=ragflow_apt,target=/var/cache/apt,sharing=locked \ apt install -y nginx unzip curl wget git vim less && \ apt install -y ghostscript && \ apt install -y pandoc && \ - apt install -y texlive + apt install -y texlive && \ + apt install -y fonts-freefont-ttf fonts-noto-cjk # Install uv RUN --mount=type=bind,from=infiniflow/ragflow_deps:latest,source=/,target=/deps \ diff --git a/agent/component/docs_generator.py b/agent/component/docs_generator.py index 765654834..e2e48488a 100644 --- a/agent/component/docs_generator.py +++ b/agent/component/docs_generator.py @@ -3,6 +3,7 @@ import json import os import re import base64 +import unicodedata from datetime import datetime from abc import ABC from io import BytesIO @@ -15,6 +16,9 @@ from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle, PageBreak, LongTable from reportlab.lib import colors from reportlab.pdfgen import canvas as pdf_canvas +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.ttfonts import TTFont +from reportlab.pdfbase.cidfonts import UnicodeCIDFont, CIDFont from jinja2 import Template as Jinja2Template from agent.component.base import ComponentParamBase @@ -97,6 +101,145 @@ class PDFGeneratorParam(ComponentParamBase): class PDFGenerator(Message, ABC): component_name = "PDFGenerator" + + # Track if Unicode fonts have been registered + _unicode_fonts_registered = False + _unicode_font_name = None + _unicode_font_bold_name = None + + @classmethod + def _reset_font_cache(cls): + """Reset font registration cache - useful for testing""" + cls._unicode_fonts_registered = False + cls._unicode_font_name = None + cls._unicode_font_bold_name = None + + @classmethod + def _register_unicode_fonts(cls): + """Register Unicode-compatible fonts for multi-language support. + + Uses CID fonts (STSong-Light) for reliable CJK rendering as TTF fonts + have issues with glyph mapping in some ReportLab versions. + """ + # If already registered successfully, return True + if cls._unicode_fonts_registered and cls._unicode_font_name is not None: + return True + + # Reset and try again if previous registration failed + cls._unicode_fonts_registered = True + cls._unicode_font_name = None + cls._unicode_font_bold_name = None + + # Use CID fonts for reliable CJK support + # These are built into ReportLab and work reliably across all platforms + cid_fonts = [ + 'STSong-Light', # Simplified Chinese + 'HeiseiMin-W3', # Japanese + 'HYSMyeongJo-Medium', # Korean + ] + + for cid_font in cid_fonts: + try: + pdfmetrics.registerFont(UnicodeCIDFont(cid_font)) + cls._unicode_font_name = cid_font + cls._unicode_font_bold_name = cid_font # CID fonts don't have bold variants + print(f"Registered CID font: {cid_font}") + break + except Exception as e: + print(f"Failed to register CID font {cid_font}: {e}") + continue + + # If CID fonts fail, try TTF fonts as fallback + if not cls._unicode_font_name: + font_paths = [ + '/usr/share/fonts/truetype/freefont/FreeSans.ttf', + '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', + ] + + for font_path in font_paths: + if os.path.exists(font_path): + try: + pdfmetrics.registerFont(TTFont('UnicodeFont', font_path)) + cls._unicode_font_name = 'UnicodeFont' + cls._unicode_font_bold_name = 'UnicodeFont' + print(f"Registered TTF font from: {font_path}") + + # Register font family + from reportlab.pdfbase.pdfmetrics import registerFontFamily + registerFontFamily('UnicodeFont', normal='UnicodeFont', bold='UnicodeFont') + break + except Exception as e: + print(f"Failed to register TTF font {font_path}: {e}") + continue + + return cls._unicode_font_name is not None + + @staticmethod + def _needs_unicode_font(text: str) -> bool: + """Check if text contains CJK or other complex scripts that need special fonts. + + Standard PDF fonts (Helvetica, Times, Courier) support: + - Basic Latin, Extended Latin, Cyrillic, Greek + + CID fonts are needed for: + - CJK (Chinese, Japanese, Korean) + - Arabic, Hebrew (RTL scripts) + - Thai, Hindi, and other Indic scripts + """ + if not text: + return False + + for char in text: + code = ord(char) + + # CJK Unified Ideographs and related ranges + if 0x4E00 <= code <= 0x9FFF: # CJK Unified Ideographs + return True + if 0x3400 <= code <= 0x4DBF: # CJK Extension A + return True + if 0x3000 <= code <= 0x303F: # CJK Symbols and Punctuation + return True + if 0x3040 <= code <= 0x309F: # Hiragana + return True + if 0x30A0 <= code <= 0x30FF: # Katakana + return True + if 0xAC00 <= code <= 0xD7AF: # Hangul Syllables + return True + if 0x1100 <= code <= 0x11FF: # Hangul Jamo + return True + + # Arabic and Hebrew (RTL scripts) + if 0x0600 <= code <= 0x06FF: # Arabic + return True + if 0x0590 <= code <= 0x05FF: # Hebrew + return True + + # Indic scripts + if 0x0900 <= code <= 0x097F: # Devanagari (Hindi) + return True + if 0x0E00 <= code <= 0x0E7F: # Thai + return True + + return False + + def _get_font_for_content(self, content: str) -> tuple: + """Get appropriate font based on content, returns (regular_font, bold_font)""" + if self._needs_unicode_font(content): + if self._register_unicode_fonts() and self._unicode_font_name: + return (self._unicode_font_name, self._unicode_font_bold_name or self._unicode_font_name) + else: + print("Warning: Content contains non-Latin characters but no Unicode font available") + + # Fall back to configured font + return (self._param.font_family, self._get_bold_font_name()) + + def _get_active_font(self) -> str: + """Get the currently active font (Unicode or configured)""" + return getattr(self, '_active_font', self._param.font_family) + + def _get_active_bold_font(self) -> str: + """Get the currently active bold font (Unicode or configured)""" + return getattr(self, '_active_bold_font', self._get_bold_font_name()) def _get_bold_font_name(self) -> str: """Get the correct bold variant of the current font family""" @@ -136,8 +279,8 @@ class PDFGenerator(Message, ABC): title = self._param.title or "" subtitle = self._param.subtitle or "" - # Add debug logging - print(f"Starting PDF generation for title: {title}") + # Log PDF generation start + print(f"Starting PDF generation for title: {title}, content length: {len(content)} chars") # Resolve variable references in content using canvas if content and self._canvas.is_reff(content): @@ -205,9 +348,6 @@ class PDFGenerator(Message, ABC): if not content: content = kwargs.get("content", "") - # Add debug logging - print(f"Starting PDF generation with content length: {len(content)} characters") - # Generate document based on format try: output_format = self._param.output_format or "pdf" @@ -317,7 +457,15 @@ class PDFGenerator(Message, ABC): # Build story (content elements) story = [] - styles = self._create_styles() + # Combine all text content for Unicode font detection + all_text = f"{title} {subtitle} {content}" + + # IMPORTANT: Register Unicode fonts BEFORE creating any styles or Paragraphs + # This ensures the font family is available for ReportLab's HTML parser + if self._needs_unicode_font(all_text): + self._register_unicode_fonts() + + styles = self._create_styles(all_text) # Add logo if provided if self._param.logo_image: @@ -397,13 +545,41 @@ class PDFGenerator(Message, ABC): except Exception as close_error: print(f"Error closing buffer: {close_error}") - def _create_styles(self): - """Create custom paragraph styles""" + def _create_styles(self, content: str = ""): + """Create custom paragraph styles with Unicode font support if needed""" + # Check if content contains CJK characters that need special fonts + needs_cjk = self._needs_unicode_font(content) + + if needs_cjk: + # Use CID fonts for CJK content + if self._register_unicode_fonts() and self._unicode_font_name: + regular_font = self._unicode_font_name + bold_font = self._unicode_font_bold_name or self._unicode_font_name + print(f"Using CID font for CJK content: {regular_font}") + else: + # Fall back to configured font if CID fonts unavailable + regular_font = self._param.font_family + bold_font = self._get_bold_font_name() + print(f"Warning: CJK content detected but no CID font available, using {regular_font}") + else: + # Use user-selected font for Latin-only content + regular_font = self._param.font_family + bold_font = self._get_bold_font_name() + print(f"Using configured font: {regular_font}") + + # Store active fonts as instance variables for use in other methods + self._active_font = regular_font + self._active_bold_font = bold_font + + # Get fresh style sheet styles = getSampleStyleSheet() # Helper function to get the correct bold font name def get_bold_font(font_family): """Get the correct bold variant of a font family""" + # If using Unicode font, return the Unicode bold + if font_family in ('UnicodeFont', self._unicode_font_name): + return bold_font font_map = { 'Helvetica': 'Helvetica-Bold', 'Times-Roman': 'Times-Bold', @@ -413,6 +589,10 @@ class PDFGenerator(Message, ABC): return font_family return font_map.get(font_family, 'Helvetica-Bold') + # Use detected font instead of configured font for non-Latin content + active_font = regular_font + active_bold_font = bold_font + # Helper function to add or update style def add_or_update_style(name, **kwargs): if name in styles: @@ -424,13 +604,23 @@ class PDFGenerator(Message, ABC): # Add new style styles.add(ParagraphStyle(name=name, **kwargs)) + # IMPORTANT: Update base styles to use Unicode font for non-Latin content + # This ensures ALL text uses the correct font, not just our custom styles + add_or_update_style('Normal', fontName=active_font) + add_or_update_style('BodyText', fontName=active_font) + add_or_update_style('Bullet', fontName=active_font) + add_or_update_style('Heading1', fontName=active_bold_font) + add_or_update_style('Heading2', fontName=active_bold_font) + add_or_update_style('Heading3', fontName=active_bold_font) + add_or_update_style('Title', fontName=active_bold_font) + # Title style add_or_update_style( 'PDFTitle', parent=styles['Heading1'], fontSize=self._param.title_font_size, textColor=colors.HexColor(self._param.title_color), - fontName=get_bold_font(self._param.font_family), + fontName=active_bold_font, alignment=TA_CENTER, spaceAfter=12 ) @@ -441,7 +631,7 @@ class PDFGenerator(Message, ABC): parent=styles['Heading2'], fontSize=self._param.heading2_font_size, textColor=colors.HexColor(self._param.text_color), - fontName=self._param.font_family, + fontName=active_font, alignment=TA_CENTER, spaceAfter=12 ) @@ -451,7 +641,7 @@ class PDFGenerator(Message, ABC): 'CustomHeading1', parent=styles['Heading1'], fontSize=self._param.heading1_font_size, - fontName=get_bold_font(self._param.font_family), + fontName=active_bold_font, textColor=colors.HexColor(self._param.text_color), spaceAfter=12, spaceBefore=12 @@ -461,7 +651,7 @@ class PDFGenerator(Message, ABC): 'CustomHeading2', parent=styles['Heading2'], fontSize=self._param.heading2_font_size, - fontName=get_bold_font(self._param.font_family), + fontName=active_bold_font, textColor=colors.HexColor(self._param.text_color), spaceAfter=10, spaceBefore=10 @@ -471,7 +661,7 @@ class PDFGenerator(Message, ABC): 'CustomHeading3', parent=styles['Heading3'], fontSize=self._param.heading3_font_size, - fontName=get_bold_font(self._param.font_family), + fontName=active_bold_font, textColor=colors.HexColor(self._param.text_color), spaceAfter=8, spaceBefore=8 @@ -482,7 +672,7 @@ class PDFGenerator(Message, ABC): 'CustomBody', parent=styles['BodyText'], fontSize=self._param.font_size, - fontName=self._param.font_family, + fontName=active_font, textColor=colors.HexColor(self._param.text_color), leading=self._param.font_size * self._param.line_spacing, alignment=TA_JUSTIFY @@ -493,13 +683,13 @@ class PDFGenerator(Message, ABC): 'CustomBullet', parent=styles['BodyText'], fontSize=self._param.font_size, - fontName=self._param.font_family, + fontName=active_font, textColor=colors.HexColor(self._param.text_color), leftIndent=20, bulletIndent=10 ) - # Code style + # Code style (keep Courier for code blocks) add_or_update_style( 'PDFCode', parent=styles.get('Code', styles['Normal']), @@ -516,7 +706,7 @@ class PDFGenerator(Message, ABC): 'Italic', parent=styles['Normal'], fontSize=self._param.font_size, - fontName=self._param.font_family, + fontName=active_font, textColor=colors.HexColor(self._param.text_color) ) @@ -571,7 +761,8 @@ class PDFGenerator(Message, ABC): bullet_items = [] while i < len(lines) and (lines[i].strip().startswith('- ') or lines[i].strip().startswith('* ')): item_text = lines[i].strip()[2:].strip() - bullet_items.append(f"• {self._format_inline(item_text)}") + formatted = self._format_inline(item_text) + bullet_items.append(f"• {formatted}") i += 1 for item in bullet_items: elements.append(Paragraph(item, styles['CustomBullet'])) @@ -754,7 +945,7 @@ class PDFGenerator(Message, ABC): 'TableHeader', parent=styles['Normal'], fontSize=self._param.font_size, - fontName=get_bold_font(self._param.font_family), + fontName=self._get_active_bold_font(), textColor=colors.whitesmoke, alignment=TA_CENTER, leading=self._param.font_size * 1.2, @@ -766,7 +957,7 @@ class PDFGenerator(Message, ABC): 'TableCell', parent=styles['Normal'], fontSize=font_size, - fontName=self._param.font_family, + fontName=self._get_active_font(), textColor=colors.black, alignment=TA_LEFT, leading=font_size * 1.15, @@ -795,7 +986,7 @@ class PDFGenerator(Message, ABC): 'TableHeader', parent=styles['Normal'], fontSize=base_font_size + 1, - fontName=self._get_bold_font_name(), + fontName=self._get_active_bold_font(), textColor=colors.HexColor('#2c3e50'), spaceAfter=6, backColor=colors.HexColor('#f8f9fa'), @@ -810,7 +1001,7 @@ class PDFGenerator(Message, ABC): 'TableBody', parent=styles['Normal'], fontSize=base_font_size, - fontName=getattr(self._param, 'font_family', 'Helvetica'), + fontName=self._get_active_font(), textColor=colors.HexColor(getattr(self._param, 'text_color', '#000000')), spaceAfter=6, leading=base_font_size * 1.2 @@ -820,7 +1011,7 @@ class PDFGenerator(Message, ABC): label_style = ParagraphStyle( 'LabelStyle', parent=body_style, - fontName=self._get_bold_font_name(), + fontName=self._get_active_bold_font(), textColor=colors.HexColor('#2c3e50'), fontSize=base_font_size, spaceAfter=4, @@ -1121,7 +1312,8 @@ class PDFGenerator(Message, ABC): font_size = self._param.font_size - 1 if row_idx > 0 else self._param.font_size try: style = self._get_cell_style(row_idx, is_header=(row_idx == 0), font_size=font_size) - processed_row.append(Paragraph(self._escape_html(cell_text), style)) + escaped_text = self._escape_html(cell_text) + processed_row.append(Paragraph(escaped_text, style)) except Exception as e: processed_row.append(self._escape_html(cell_text)) @@ -1146,7 +1338,7 @@ class PDFGenerator(Message, ABC): ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2c3e50')), # Darker header ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), ('ALIGN', (0, 0), (-1, 0), 'CENTER'), - ('FONTNAME', (0, 0), (-1, 0), self._get_bold_font_name()), + ('FONTNAME', (0, 0), (-1, 0), self._get_active_bold_font()), ('FONTSIZE', (0, 0), (-1, -1), self._param.font_size - 1), ('BOTTOMPADDING', (0, 0), (-1, 0), 12), ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor('#f8f9fa')), # Lighter background @@ -1232,9 +1424,12 @@ class PDFGenerator(Message, ABC): """Add header, footer, page numbers, watermark""" canvas.saveState() + # Get active font for decorations + active_font = self._get_active_font() + # Add watermark if self._param.watermark_text: - canvas.setFont(self._param.font_family, 60) + canvas.setFont(active_font, 60) canvas.setFillColorRGB(0.9, 0.9, 0.9, alpha=0.3) canvas.saveState() canvas.translate(doc.pagesize[0] / 2, doc.pagesize[1] / 2) @@ -1244,13 +1439,13 @@ class PDFGenerator(Message, ABC): # Add header if self._param.header_text: - canvas.setFont(self._param.font_family, 9) + canvas.setFont(active_font, 9) canvas.setFillColorRGB(0.5, 0.5, 0.5) canvas.drawString(doc.leftMargin, doc.pagesize[1] - 0.5 * inch, self._param.header_text) # Add footer if self._param.footer_text: - canvas.setFont(self._param.font_family, 9) + canvas.setFont(active_font, 9) canvas.setFillColorRGB(0.5, 0.5, 0.5) canvas.drawString(doc.leftMargin, 0.5 * inch, self._param.footer_text) @@ -1258,7 +1453,7 @@ class PDFGenerator(Message, ABC): if self._param.add_page_numbers: page_num = canvas.getPageNumber() text = f"Page {page_num}" - canvas.setFont(self._param.font_family, 9) + canvas.setFont(active_font, 9) canvas.setFillColorRGB(0.5, 0.5, 0.5) canvas.drawRightString(doc.pagesize[0] - doc.rightMargin, 0.5 * inch, text) diff --git a/docs/guides/agent/agent_component_reference/docs_generator.md b/docs/guides/agent/agent_component_reference/docs_generator.md index df01e7d40..9834364a5 100644 --- a/docs/guides/agent/agent_component_reference/docs_generator.md +++ b/docs/guides/agent/agent_component_reference/docs_generator.md @@ -71,6 +71,10 @@ The font used throughout the document: - **Times-Roman** - **Courier** +:::tip NOTE +When the document contains CJK (Chinese, Japanese, Korean) or other non-Latin characters, the system automatically switches to a compatible Unicode font (STSong-Light) to ensure proper rendering. The selected font family is used for Latin-only content. +::: + ### Font size The base font size in points. Defaults to `12`. @@ -132,3 +136,13 @@ To display a download button in the chat, add a **Message** component after the 1. Connect the **Docs Generator** output to a **Message** component. 2. In the **Message** component's content field, type `/` and select `{Docs Generator_0@download}`. 3. When the agent runs, a download button will appear in the chat, allowing users to download the generated document. + +## Multi-language support + +The **Docs Generator** automatically detects non-Latin characters (Chinese, Japanese, Korean, Arabic, Hebrew, Cyrillic, etc.) and uses appropriate Unicode fonts when available on the server. + +:::tip NOTE +For full multi-language support, ensure Unicode fonts are installed on the RAGFlow server: +- **Linux**: `fonts-freefont-ttf`, `fonts-noto-cjk`, or `fonts-droid-fallback` +- **Docker**: Add font packages to the Dockerfile if needed +::: diff --git a/pyproject.toml b/pyproject.toml index dbe407937..a3358d3f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -154,6 +154,8 @@ dependencies = [ "exceptiongroup>=1.3.0,<2.0.0", "ffmpeg-python>=0.2.0", "imageio-ffmpeg>=0.6.0", + "reportlab>=4.4.1", + "jinja2>=3.1.0", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 06132ed51..cb357c758 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.12, <3.15" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'darwin'", @@ -3257,7 +3257,7 @@ wheels = [ [[package]] name = "jupyter-client" -version = "8.6.3" +version = "8.7.0" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "jupyter-core" }, @@ -3266,9 +3266,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/27/d10de45e8ad4ce872372c4a3a37b7b35b6b064f6f023a5c14ffcced4d59d/jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d", size = 344691, upload-time = "2025-12-09T18:37:01.953Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0", size = 106215, upload-time = "2025-12-09T18:37:00.024Z" }, ] [[package]] @@ -5955,6 +5955,7 @@ dependencies = [ { name = "infinity-emb" }, { name = "infinity-sdk" }, { name = "itsdangerous" }, + { name = "jinja2" }, { name = "jira" }, { name = "json-repair" }, { name = "langfuse" }, @@ -6012,6 +6013,7 @@ dependencies = [ { name = "ranx" }, { name = "readability-lxml" }, { name = "replicate" }, + { name = "reportlab" }, { name = "requests" }, { name = "roman-numbers" }, { name = "ruamel-base" }, @@ -6122,6 +6124,7 @@ requires-dist = [ { name = "infinity-emb", specifier = ">=0.0.66,<0.0.67" }, { name = "infinity-sdk", specifier = "==0.6.11" }, { name = "itsdangerous", specifier = "==2.1.2" }, + { name = "jinja2", specifier = ">=3.1.0" }, { name = "jira", specifier = "==3.10.5" }, { name = "json-repair", specifier = "==0.35.0" }, { name = "langfuse", specifier = ">=2.60.0" }, @@ -6179,6 +6182,7 @@ requires-dist = [ { name = "ranx", specifier = "==0.3.20" }, { name = "readability-lxml", specifier = ">=0.8.4,<1.0.0" }, { name = "replicate", specifier = "==0.31.0" }, + { name = "reportlab", specifier = ">=4.4.1" }, { name = "requests", specifier = ">=2.32.3,<3.0.0" }, { name = "roman-numbers", specifier = "==1.0.2" }, { name = "ruamel-base", specifier = "==1.0.0" }, @@ -7383,21 +7387,21 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.2" +version = "6.5.3" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/2e/3d22d478f27cb4b41edd4db7f10cd7846d0a28ea443342de3dba97035166/tornado-6.5.3.tar.gz", hash = "sha256:16abdeb0211796ffc73765bc0a20119712d68afeeaf93d1a3f2edf6b3aee8d5a", size = 513348, upload-time = "2025-12-11T04:16:42.225Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/e9/bf22f66e1d5d112c0617974b5ce86666683b32c09b355dfcd59f8d5c8ef6/tornado-6.5.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2dd7d7e8d3e4635447a8afd4987951e3d4e8d1fb9ad1908c54c4002aabab0520", size = 443860, upload-time = "2025-12-11T04:16:26.638Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/9c/594b631f0b8dc5977080c7093d1e96f1377c10552577d2c31bb0208c9362/tornado-6.5.3-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5977a396f83496657779f59a48c38096ef01edfe4f42f1c0634b791dde8165d0", size = 442118, upload-time = "2025-12-11T04:16:28.32Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/f6/685b869f5b5b9d9547571be838c6106172082751696355b60fc32a4988ed/tornado-6.5.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f72ac800be2ac73ddc1504f7aa21069a4137e8d70c387172c063d363d04f2208", size = 445700, upload-time = "2025-12-11T04:16:29.64Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/4c/f0d19edf24912b7f21ae5e941f7798d132ad4d9b71441c1e70917a297265/tornado-6.5.3-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43c4fc4f5419c6561cfb8b884a8f6db7b142787d47821e1a0e1296253458265", size = 445041, upload-time = "2025-12-11T04:16:30.799Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/2b/e02da94f4a4aef2bb3b923c838ef284a77548a5f06bac2a8682b36b4eead/tornado-6.5.3-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de8b3fed4b3afb65d542d7702ac8767b567e240f6a43020be8eaef59328f117b", size = 445270, upload-time = "2025-12-11T04:16:32.316Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/e2/7a7535d23133443552719dba526dacbb7415f980157da9f14950ddb88ad6/tornado-6.5.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dbc4b4c32245b952566e17a20d5c1648fbed0e16aec3fc7e19f3974b36e0e47c", size = 445957, upload-time = "2025-12-11T04:16:33.913Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/1f/9ff92eca81ff17a86286ec440dcd5eab0400326eb81761aa9a4eecb1ffb9/tornado-6.5.3-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:db238e8a174b4bfd0d0238b8cfcff1c14aebb4e2fcdafbf0ea5da3b81caceb4c", size = 445371, upload-time = "2025-12-11T04:16:35.093Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/b1/1d03ae4526a393b0b839472a844397337f03c7f3a1e6b5c82241f0e18281/tornado-6.5.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:892595c100cd9b53a768cbfc109dfc55dec884afe2de5290611a566078d9692d", size = 445348, upload-time = "2025-12-11T04:16:36.679Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/7d/7c181feadc8941f418d0d26c3790ee34ffa4bd0a294bc5201d44ebd19c1e/tornado-6.5.3-cp39-abi3-win32.whl", hash = "sha256:88141456525fe291e47bbe1ba3ffb7982549329f09b4299a56813923af2bd197", size = 446433, upload-time = "2025-12-11T04:16:38.332Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/98/4f7f938606e21d0baea8c6c39a7c8e95bdf8e50b0595b1bb6f0de2af7a6e/tornado-6.5.3-cp39-abi3-win_amd64.whl", hash = "sha256:ba4b513d221cc7f795a532c1e296f36bcf6a60e54b15efd3f092889458c69af1", size = 446842, upload-time = "2025-12-11T04:16:39.867Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/27/0e3fca4c4edf33fb6ee079e784c63961cd816971a45e5e4cacebe794158d/tornado-6.5.3-cp39-abi3-win_arm64.whl", hash = "sha256:278c54d262911365075dd45e0b6314308c74badd6ff9a54490e7daccdd5ed0ea", size = 445863, upload-time = "2025-12-11T04:16:41.099Z" }, ] [[package]] diff --git a/web/src/pages/agent/constant/index.tsx b/web/src/pages/agent/constant/index.tsx index f9b68e197..f1c4bab1b 100644 --- a/web/src/pages/agent/constant/index.tsx +++ b/web/src/pages/agent/constant/index.tsx @@ -996,6 +996,7 @@ export const initialPDFGeneratorValues = { success: { type: 'boolean', value: false }, }, }; + export enum WebhookMethod { Post = 'POST', Get = 'GET', diff --git a/web/src/pages/agents/hooks/use-selelct-filters.ts b/web/src/pages/agents/hooks/use-selelct-filters.ts index e1ea755ee..aa4f4f4dd 100644 --- a/web/src/pages/agents/hooks/use-selelct-filters.ts +++ b/web/src/pages/agents/hooks/use-selelct-filters.ts @@ -7,11 +7,15 @@ export function useSelectFilters() { const { data } = useFetchAgentList({}); const canvasCategory = useMemo(() => { - return groupListByType(data.canvas, 'canvas_category', 'canvas_category'); - }, [data.canvas]); + return groupListByType( + data?.canvas ?? [], + 'canvas_category', + 'canvas_category', + ); + }, [data?.canvas]); const filters: FilterCollection[] = [ - buildOwnersFilter(data.canvas), + buildOwnersFilter(data?.canvas ?? []), { field: 'canvasCategory', list: canvasCategory,