diff --git a/common/constants.py b/common/constants.py
index 6a939cf4cf..cbc2f534c9 100644
--- a/common/constants.py
+++ b/common/constants.py
@@ -138,6 +138,7 @@ class FileSource(StrEnum):
SEAFILE = "seafile"
MYSQL = "mysql"
POSTGRESQL = "postgresql"
+ DINGTALK_AI_TABLE = "dingtalk_ai_table"
class PipelineTaskType(StrEnum):
diff --git a/common/data_source/__init__.py b/common/data_source/__init__.py
index 099f3d7b3b..022a107613 100644
--- a/common/data_source/__init__.py
+++ b/common/data_source/__init__.py
@@ -36,6 +36,7 @@ from .sharepoint_connector import SharePointConnector
from .teams_connector import TeamsConnector
from .moodle_connector import MoodleConnector
from .airtable_connector import AirtableConnector
+from .dingtalk_ai_table_connector import DingTalkAITableConnector
from .asana_connector import AsanaConnector
from .imap_connector import ImapConnector
from .zendesk_connector import ZendeskConnector
@@ -83,4 +84,5 @@ __all__ = [
"SeaFileConnector",
"RDBMSConnector",
"WebDAVConnector",
+ "DingTalkAITableConnector",
]
diff --git a/common/data_source/config.py b/common/data_source/config.py
index b05d8af24a..65338f34a6 100644
--- a/common/data_source/config.py
+++ b/common/data_source/config.py
@@ -66,6 +66,7 @@ class DocumentSource(str, Enum):
SEAFILE = "seafile"
MYSQL = "mysql"
POSTGRESQL = "postgresql"
+ DINGTALK_AI_TABLE = "dingtalk_ai_table"
class FileOrigin(str, Enum):
diff --git a/common/data_source/dingtalk_ai_table_connector.py b/common/data_source/dingtalk_ai_table_connector.py
new file mode 100644
index 0000000000..66588d4d30
--- /dev/null
+++ b/common/data_source/dingtalk_ai_table_connector.py
@@ -0,0 +1,433 @@
+"""DingTalk AI Table connector for RAGFlow. By the way, "notable" is a reference to the DingTalk AI Table.
+
+This connector ingests records from DingTalk AI Table as documents.
+It first retrieves all sheets from a specified table, then fetches all records
+from each sheet.
+
+API Documentation:
+- GetAllSheets: https://open.dingtalk.com/document/development/api-notable-getallsheets
+- ListRecords: https://open.dingtalk.com/document/development/api-notable-listrecords
+"""
+
+import json
+import logging
+from datetime import datetime, timezone
+from typing import Any
+
+from alibabacloud_dingtalk.notable_1_0.client import Client as NotableClient
+from alibabacloud_dingtalk.notable_1_0 import models as notable_models
+from alibabacloud_tea_openapi import models as open_api_models
+from alibabacloud_tea_util import models as util_models
+from alibabacloud_tea_util.client import Client as UtilClient
+
+from common.data_source.config import INDEX_BATCH_SIZE, DocumentSource
+from common.data_source.exceptions import ConnectorMissingCredentialError, ConnectorValidationError
+from common.data_source.interfaces import LoadConnector, PollConnector, SecondsSinceUnixEpoch
+from common.data_source.models import Document, GenerateDocumentsOutput
+
+logger = logging.getLogger(__name__)
+
+# Document ID prefix for DingTalk Notable
+_DINGTALK_AI_TABLE_DOC_ID_PREFIX = "dingtalk_ai_table:"
+
+
+class DingTalkAITableClientNotSetUpError(PermissionError):
+ """Exception raised when DingTalk Notable client is not initialized."""
+
+ def __init__(self) -> None:
+ super().__init__("DingTalk Notable client is not set up. Did you forget to call load_credentials()?")
+
+
+class DingTalkAITableConnector(LoadConnector, PollConnector):
+ """
+ DingTalk AI Table (Notable) connector for accessing table records.
+
+ This connector:
+ 1. Retrieves all sheets from a specified Notable table using GetAllSheets API
+ 2. For each sheet, fetches all records using ListRecords API with pagination
+ 3. Converts each record into a Document for RAGFlow ingestion
+
+ Required credentials:
+ - access_token: DingTalk access token (x-acs-dingtalk-access-token)
+ - operator_id: User's unionId for API calls
+
+ Configuration:
+ - table_id: The Notable table ID (e.g., 'qnYxxx')
+ """
+
+ def __init__(
+ self,
+ table_id: str,
+ operator_id: str,
+ batch_size: int = INDEX_BATCH_SIZE,
+ ) -> None:
+ """
+ Initialize the DingTalk Notable connector.
+
+ Args:
+ table_id: The Notable table ID
+ operator_id: User's unionId for API calls
+ batch_size: Number of records per batch for document generation
+ """
+ self.table_id = table_id
+ self.operator_id = operator_id
+ self.batch_size = batch_size
+ self._client: NotableClient | None = None
+ self._access_token: str | None = None
+
+ def _create_client(self) -> NotableClient:
+ """Create DingTalk Notable API client."""
+ config = open_api_models.Config()
+ config.protocol = "https"
+ config.region_id = "central"
+ return NotableClient(config)
+
+ def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None:
+ """
+ Load DingTalk credentials.
+
+ Args:
+ credentials: Dictionary containing 'access_token'
+
+ Returns:
+ None
+ """
+ access_token = credentials.get("access_token")
+ if not access_token:
+ raise ConnectorMissingCredentialError("DingTalk access_token is required")
+
+ self._access_token = access_token
+ self._client = self._create_client()
+ return None
+
+ @property
+ def client(self) -> NotableClient:
+ """Get the DingTalk AITable client."""
+ if self._client is None:
+ raise DingTalkAITableClientNotSetUpError()
+ return self._client
+
+ @property
+ def access_token(self) -> str:
+ """Get the access token."""
+ if self._access_token is None:
+ raise ConnectorMissingCredentialError("DingTalk access_token not loaded")
+ return self._access_token
+
+ def validate_connector_settings(self) -> None:
+ """Validate DingTalk connector settings by trying to get all sheets."""
+ if self._client is None or self._access_token is None:
+ raise ConnectorMissingCredentialError("DingTalk Notable")
+
+ try:
+ # Try to get sheets to validate credentials
+ headers = notable_models.GetAllSheetsHeaders()
+ headers.x_acs_dingtalk_access_token = self._access_token
+
+ request = notable_models.GetAllSheetsRequest(
+ operator_id=self.operator_id,
+ )
+
+ self.client.get_all_sheets_with_options(
+ self.table_id,
+ request,
+ headers,
+ util_models.RuntimeOptions(),
+ )
+ except Exception as e:
+ logger.exception("[DingTalk Notable]: Failed to validate credentials")
+ raise ConnectorValidationError(f"DingTalk Notable credential validation failed: {e}")
+
+ def _get_all_sheets(self) -> list[dict[str, Any]]:
+ """
+ Retrieve all sheets from the Notable table.
+
+ Returns:
+ List of sheet information dictionaries
+ """
+ headers = notable_models.GetAllSheetsHeaders()
+ headers.x_acs_dingtalk_access_token = self._access_token
+
+ request = notable_models.GetAllSheetsRequest(
+ operator_id=self.operator_id,
+ )
+
+ try:
+ response = self.client.get_all_sheets_with_options(
+ self.table_id,
+ request,
+ headers,
+ util_models.RuntimeOptions(),
+ )
+
+ sheets = []
+ if response.body and response.body.value:
+ for sheet in response.body.value:
+ sheets.append(
+ {
+ "id": sheet.id,
+ "name": sheet.name,
+ }
+ )
+
+ logger.info(f"[DingTalk Notable]: Found {len(sheets)} sheets in table {self.table_id}")
+ return sheets
+
+ except Exception as e:
+ logger.exception(f"[DingTalk Notable]: Failed to get sheets: {e}")
+ raise
+
+ def _list_records(
+ self,
+ sheet_id: str,
+ next_token: str | None = None,
+ max_results: int = 100,
+ ) -> tuple[list[dict[str, Any]], str | None]:
+ """
+ List records from a specific sheet with pagination.
+
+ Args:
+ sheet_id: The sheet ID
+ next_token: Token for pagination
+ max_results: Maximum number of results per page
+
+ Returns:
+ Tuple of (records list, next_token or None if no more)
+ """
+ headers = notable_models.ListRecordsHeaders()
+ headers.x_acs_dingtalk_access_token = self._access_token
+
+ request = notable_models.ListRecordsRequest(
+ operator_id=self.operator_id,
+ max_results=max_results,
+ next_token=next_token or "",
+ )
+
+ try:
+ response = self.client.list_records_with_options(
+ self.table_id,
+ sheet_id,
+ request,
+ headers,
+ util_models.RuntimeOptions(),
+ )
+
+ records = []
+ new_next_token = None
+
+ if response.body:
+ if response.body.records:
+ for record in response.body.records:
+ records.append(
+ {
+ "id": record.id,
+ "fields": record.fields,
+ }
+ )
+ if response.body.next_token:
+ new_next_token = response.body.next_token
+
+ return records, new_next_token
+
+ except Exception as e:
+ if not UtilClient.empty(getattr(e, "code", None)) and not UtilClient.empty(getattr(e, "message", None)):
+ logger.error(f"[DingTalk AITable]: API error - code: {e.code}, message: {e.message}")
+ raise
+
+ def _get_all_records(self, sheet_id: str) -> list[dict[str, Any]]:
+ """
+ Retrieve all records from a sheet with pagination.
+
+ Args:
+ sheet_id: The sheet ID
+
+ Returns:
+ List of all records
+ """
+ all_records = []
+ next_token = None
+
+ while True:
+ records, next_token = self._list_records(
+ sheet_id=sheet_id,
+ next_token=next_token,
+ )
+ all_records.extend(records)
+
+ if not next_token:
+ break
+
+ logger.info(f"[DingTalk Notable]: Retrieved {len(all_records)} records from sheet {sheet_id}")
+ return all_records
+
+ def _convert_record_to_document(
+ self,
+ record: dict[str, Any],
+ sheet_id: str,
+ sheet_name: str,
+ ) -> Document:
+ """
+ Convert a Notable record to a Document.
+
+ Args:
+ record: The record dictionary
+ sheet_id: The sheet ID
+ sheet_name: The sheet name
+
+ Returns:
+ Document object
+ """
+ record_id = record.get("id", "unknown")
+ fields = record.get("fields", {})
+
+ # Convert fields to JSON string for blob content
+ content = json.dumps(fields, ensure_ascii=False, indent=2)
+ blob = content.encode("utf-8")
+
+ # Create semantic identifier from record fields
+ # Try to find a meaningful title/name field
+ semantic_identifier = f"{sheet_name} - Record {record_id}"
+
+ # Try to find a title-like field
+ for key, value in fields.items():
+ if isinstance(value, str) and len(value) > 0 and len(value) < 100:
+ semantic_identifier = f"{sheet_name} - {value[:50]}"
+ break
+
+ # Metadata
+ metadata: dict[str, str | list[str]] = {
+ "table_id": self.table_id,
+ "sheet_id": sheet_id,
+ "sheet_name": sheet_name,
+ "record_id": record_id,
+ }
+
+ # Create document
+ doc = Document(
+ id=f"{_DINGTALK_AI_TABLE_DOC_ID_PREFIX}{self.table_id}:{sheet_id}:{record_id}",
+ source=DocumentSource.DINGTALK_AI_TABLE,
+ semantic_identifier=semantic_identifier,
+ extension=".json",
+ blob=blob,
+ size_bytes=len(blob),
+ doc_updated_at=datetime.now(timezone.utc),
+ metadata=metadata,
+ )
+
+ return doc
+
+ def _yield_documents_from_table(
+ self,
+ start: SecondsSinceUnixEpoch | None = None,
+ end: SecondsSinceUnixEpoch | None = None,
+ ) -> GenerateDocumentsOutput:
+ """
+ Yield documents from all sheets in the table.
+
+ Args:
+ start: Optional start timestamp for filtering
+ end: Optional end timestamp for filtering
+
+ Yields:
+ Lists of Document objects
+ """
+ # Get all sheets
+ sheets = self._get_all_sheets()
+
+ batch: list[Document] = []
+
+ for sheet in sheets:
+ sheet_id = sheet["id"]
+ sheet_name = sheet["name"]
+
+ # Get all records from this sheet
+ records = self._get_all_records(sheet_id)
+
+ for record in records:
+ doc = self._convert_record_to_document(
+ record=record,
+ sheet_id=sheet_id,
+ sheet_name=sheet_name,
+ )
+
+ # Apply time filtering if specified
+ if start is not None or end is not None:
+ doc_time = doc.doc_updated_at.timestamp() if doc.doc_updated_at else None
+ if doc_time is not None:
+ if start is not None and doc_time < start:
+ continue
+ if end is not None and doc_time > end:
+ continue
+
+ batch.append(doc)
+
+ if len(batch) >= self.batch_size:
+ yield batch
+ batch = []
+
+ if batch:
+ yield batch
+
+ def load_from_state(self) -> GenerateDocumentsOutput:
+ """
+ Load all documents from the DingTalk Notable table.
+
+ Yields:
+ Lists of Document objects
+ """
+ return self._yield_documents_from_table()
+
+ def poll_source(
+ self,
+ start: SecondsSinceUnixEpoch,
+ end: SecondsSinceUnixEpoch,
+ ) -> GenerateDocumentsOutput:
+ """
+ Poll for documents within a time range.
+
+ Args:
+ start: Start timestamp
+ end: End timestamp
+
+ Yields:
+ Lists of Document objects
+ """
+ return self._yield_documents_from_table(start=start, end=end)
+
+
+if __name__ == "__main__":
+ import os
+
+ logging.basicConfig(level=logging.DEBUG)
+
+ # Example usage
+ table_id = os.environ.get("DINGTALK_AI_TABLE_BASE_ID", "")
+ operator_id = os.environ.get("DINGTALK_OPERATOR_ID", "")
+ access_token = os.environ.get("DINGTALK_ACCESS_TOKEN", "")
+
+ if not all([table_id, operator_id, access_token]):
+ print("Please set DINGTALK_AI_TABLE_BASE_ID, DINGTALK_OPERATOR_ID, and DINGTALK_ACCESS_TOKEN environment variables")
+ exit(1)
+
+ connector = DingTalkAITableConnector(
+ table_id=table_id,
+ operator_id=operator_id,
+ )
+ connector.load_credentials({"access_token": access_token})
+
+ try:
+ connector.validate_connector_settings()
+ print("Connector settings validated successfully")
+ except Exception as e:
+ print(f"Validation failed: {e}")
+ exit(1)
+
+ document_batches = connector.load_from_state()
+ try:
+ first_batch = next(document_batches)
+ print(f"Loaded {len(first_batch)} documents in first batch.")
+ for doc in first_batch[:5]: # Print first 5 docs
+ print(f"- {doc.semantic_identifier} ({doc.size_bytes} bytes)")
+ print(f" Metadata: {doc.metadata}")
+ except StopIteration:
+ print("No documents available in DingTalk Notable table.")
diff --git a/pyproject.toml b/pyproject.toml
index 53dc38cf8c..0665a1c536 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -156,6 +156,7 @@ dependencies = [
"pygithub>=2.8.1",
"asana>=5.2.2",
"python-gitlab>=7.0.0",
+ "alibabacloud-dingtalk>=2.0.0",
"quart-schema==0.23.0",
]
diff --git a/rag/svr/sync_data_source.py b/rag/svr/sync_data_source.py
index ac317d418e..044c7484df 100644
--- a/rag/svr/sync_data_source.py
+++ b/rag/svr/sync_data_source.py
@@ -55,6 +55,7 @@ from common.data_source import (
ZendeskConnector,
SeaFileConnector,
RDBMSConnector,
+ DingTalkAITableConnector,
)
from common.constants import FileSource, TaskStatus
from common.data_source.config import INDEX_BATCH_SIZE
@@ -1221,6 +1222,49 @@ class SeaFile(SyncBase):
return document_generator
+class DingTalkAITable(SyncBase):
+ SOURCE_NAME: str = FileSource.DINGTALK_AI_TABLE
+
+ async def _generate(self, task: dict):
+ """
+ Sync records from DingTalk AI Table (Notable).
+ """
+ self.connector = DingTalkAITableConnector(
+ table_id=self.conf.get("table_id"),
+ operator_id=self.conf.get("operator_id"),
+ batch_size=self.conf.get("batch_size", INDEX_BATCH_SIZE),
+ )
+
+ credentials = self.conf.get("credentials", {})
+ if "access_token" not in credentials:
+ raise ValueError("Missing access_token in credentials")
+
+ self.connector.load_credentials(
+ {"access_token": credentials["access_token"]}
+ )
+
+ poll_start = task.get("poll_range_start")
+
+ if task.get("reindex") == "1" or poll_start is None:
+ document_generator = self.connector.load_from_state()
+ begin_info = "totally"
+ else:
+ document_generator = self.connector.poll_source(
+ poll_start.timestamp(),
+ datetime.now(timezone.utc).timestamp(),
+ )
+ begin_info = f"from {poll_start}"
+
+ logging.info(
+ "Connect to DingTalk AI Table: table_id(%s), operator_id(%s) %s",
+ self.conf.get("table_id"),
+ self.conf.get("operator_id"),
+ begin_info,
+ )
+
+ return document_generator
+
+
class MySQL(SyncBase):
SOURCE_NAME: str = FileSource.MYSQL
@@ -1321,6 +1365,7 @@ func_factory = {
FileSource.SEAFILE: SeaFile,
FileSource.MYSQL: MySQL,
FileSource.POSTGRESQL: PostgreSQL,
+ FileSource.DINGTALK_AI_TABLE: DingTalkAITable,
}
diff --git a/uv.lock b/uv.lock
index 6c54506565..0b1423a014 100644
--- a/uv.lock
+++ b/uv.lock
@@ -319,12 +319,39 @@ wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/c6/7d375cc1b1cab0f46950f556b70a2b17235747429a0889b73f3d46ff6023/alibabacloud_devs20230714-2.4.1-py3-none-any.whl", hash = "sha256:dbd260718e6db50021d804218b40bc99ee9c7e40b1def382aef8e542f5921113", size = 59307, upload-time = "2025-08-08T07:40:28.504Z" },
]
+[[package]]
+name = "alibabacloud-dingtalk"
+version = "2.2.38"
+source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
+dependencies = [
+ { name = "alibabacloud-endpoint-util" },
+ { name = "alibabacloud-gateway-dingtalk" },
+ { name = "alibabacloud-gateway-spi" },
+ { name = "alibabacloud-openapi-util" },
+ { name = "alibabacloud-tea-openapi" },
+ { name = "alibabacloud-tea-util" },
+]
+sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/95/ad71f7cb1a2814d17f3a731b37c27eb71df0895daa912e2436d2f0dd06d4/alibabacloud_dingtalk-2.2.38.tar.gz", hash = "sha256:39cba6ff3accf0a5c7fe7de651a65a5a784c4ef63e442750fd822c19864ed6f1", size = 1954698, upload-time = "2026-01-08T11:38:04.913Z" }
+wheels = [
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/77/f6679f78becf73bfbf3468367ffa6fe694120e0cd677eacbc454dbb379a1/alibabacloud_dingtalk-2.2.38-py3-none-any.whl", hash = "sha256:c3dfc918c45f49fe61469230c0808fd6f316341594a2895564511b5542f50019", size = 2074155, upload-time = "2026-01-08T11:38:03.395Z" },
+]
+
[[package]]
name = "alibabacloud-endpoint-util"
version = "0.0.4"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/7d/8cc92a95c920e344835b005af6ea45a0db98763ad6ad19299d26892e6c8d/alibabacloud_endpoint_util-0.0.4.tar.gz", hash = "sha256:a593eb8ddd8168d5dc2216cd33111b144f9189fcd6e9ca20e48f358a739bbf90", size = 2813, upload-time = "2025-06-12T07:20:52.572Z" }
+[[package]]
+name = "alibabacloud-gateway-dingtalk"
+version = "1.0.2"
+source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
+dependencies = [
+ { name = "alibabacloud-gateway-spi" },
+ { name = "alibabacloud-tea-util" },
+]
+sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/40/751d8bdf133d7fcf053f10c98e8e506810e7bee06458a02eaaa14d30ac26/alibabacloud_gateway_dingtalk-1.0.2.tar.gz", hash = "sha256:acea8b0b1d11e0394913f0b0899ddd19c0bfceab716060449b57fcc250ceb300", size = 2938, upload-time = "2023-04-25T09:48:42.249Z" }
+
[[package]]
name = "alibabacloud-gateway-spi"
version = "0.0.3"
@@ -6218,6 +6245,7 @@ dependencies = [
{ name = "agentrun-sdk" },
{ name = "aiosmtplib" },
{ name = "akshare" },
+ { name = "alibabacloud-dingtalk" },
{ name = "anthropic" },
{ name = "arxiv" },
{ name = "asana" },
@@ -6357,6 +6385,7 @@ requires-dist = [
{ name = "agentrun-sdk", specifier = ">=0.0.16,<1.0.0" },
{ name = "aiosmtplib", specifier = ">=5.0.0" },
{ name = "akshare", specifier = ">=1.15.78,<2.0.0" },
+ { name = "alibabacloud-dingtalk", specifier = ">=2.0.0" },
{ name = "anthropic", specifier = "==0.34.1" },
{ name = "arxiv", specifier = "==2.1.3" },
{ name = "asana", specifier = ">=5.2.2" },
diff --git a/web/src/assets/svg/data-source/dingtalk-ai-table.svg b/web/src/assets/svg/data-source/dingtalk-ai-table.svg
new file mode 100644
index 0000000000..589602c480
--- /dev/null
+++ b/web/src/assets/svg/data-source/dingtalk-ai-table.svg
@@ -0,0 +1,27 @@
+
\ No newline at end of file
diff --git a/web/src/locales/bg.ts b/web/src/locales/bg.ts
index 03b391cf97..ef4fc7822e 100644
--- a/web/src/locales/bg.ts
+++ b/web/src/locales/bg.ts
@@ -998,6 +998,8 @@ The above is the content you need to summarize.`,
'Свържете GitHub за синхронизиране на pull requests и issues за извличане.',
airtableDescription:
'Свържете се с Airtable и синхронизирайте файлове от определена таблица в определено работно пространство.',
+ dingtalkAITableDescription:
+ 'Свържете се с Dingtalk AI Table и синхронизирайте записи от определена таблица.',
gitlabDescription:
'Свържете GitLab за синхронизиране на хранилища, issues, merge requests и свързана документация.',
asanaDescription:
diff --git a/web/src/locales/de.ts b/web/src/locales/de.ts
index 508115b186..d549af3fb0 100644
--- a/web/src/locales/de.ts
+++ b/web/src/locales/de.ts
@@ -1016,6 +1016,8 @@ Beispiel: Virtual Hosted Style`,
'Verbinden Sie GitHub, um Pull Requests und Issues zur Recherche zu synchronisieren.',
airtableDescription:
'Verbinden Sie sich mit Airtable und synchronisieren Sie Dateien aus einer bestimmten Tabelle in einem vorgesehenen Arbeitsbereich.',
+ dingtalkAITableDescription:
+ 'Verbinden Sie sich mit Dingtalk AI Table und synchronisieren Sie Datensätze aus einer bestimmten Tabelle.',
asanaDescription:
'Verbinden Sie sich mit Asana und synchronisieren Sie Dateien aus einem bestimmten Arbeitsbereich.',
imapDescription:
diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts
index 4df8e286b5..4257d5a0e1 100644
--- a/web/src/locales/en.ts
+++ b/web/src/locales/en.ts
@@ -1034,6 +1034,8 @@ Example: Virtual Hosted Style`,
'Connect GitHub to sync pull requests and issues for retrieval.',
airtableDescription:
'Connect to Airtable and synchronize files from a specified table within a designated workspace.',
+ dingtalkAITableDescription:
+ 'Connect to Dingtalk AI Table and synchronize records from a specified table.',
gitlabDescription:
'Connect GitLab to sync repositories, issues, merge requests, and related documentation.',
asanaDescription:
diff --git a/web/src/locales/es.ts b/web/src/locales/es.ts
index 816353495f..1ea94962dc 100644
--- a/web/src/locales/es.ts
+++ b/web/src/locales/es.ts
@@ -472,6 +472,8 @@ export default {
apiVersionMessage: '¡Por favor ingresa la versión de la API!',
modelsToBeAddedTooltip:
'Si tu proveedor de modelos no aparece en la lista pero afirma ser compatible con OpenAI, selecciona la tarjeta OpenAI-API-compatible para añadir el/los modelo(s) correspondiente(s).',
+ dingtalkAITableDescription:
+ 'Conéctese a Dingtalk AI Table y sincronice registros de una tabla especificada.',
},
message: {
registered: '¡Registrado!',
diff --git a/web/src/locales/fr.ts b/web/src/locales/fr.ts
index ccb735c410..f19f1b6d05 100644
--- a/web/src/locales/fr.ts
+++ b/web/src/locales/fr.ts
@@ -680,6 +680,8 @@ export default {
modelsToBeAddedTooltip:
'Si votre fournisseur de modèle n\'est pas listé mais prétend être "compatible OpenAI", sélectionnez la carte compatible OpenAI-API pour ajouter le(s) modèle(s) pertinent(s).',
mcp: 'MCP',
+ dingtalkAITableDescription:
+ 'Connectez-vous à Dingtalk AI Table et synchronisez les enregistrements d\'une table spécifiée.',
},
message: {
registered: 'Enregistré !',
diff --git a/web/src/locales/id.ts b/web/src/locales/id.ts
index 7969d85666..09aa6a29d1 100644
--- a/web/src/locales/id.ts
+++ b/web/src/locales/id.ts
@@ -669,6 +669,8 @@ export default {
apiVersionMessage: 'Silakan masukkan versi API',
modelsToBeAddedTooltip:
'Jika penyedia model Anda tidak tercantum tetapi mengklaim kompatibel dengan OpenAI, pilih kartu OpenAI-API-compatible untuk menambahkan model yang relevan.',
+ dingtalkAITableDescription:
+ 'Hubungkan ke Dingtalk AI Table dan sinkronkan catatan dari tabel yang ditentukan.',
},
message: {
registered: 'Terdaftar!',
diff --git a/web/src/locales/it.ts b/web/src/locales/it.ts
index 04222f4607..05627ea1dc 100644
--- a/web/src/locales/it.ts
+++ b/web/src/locales/it.ts
@@ -840,6 +840,8 @@ Quanto sopra è il contenuto che devi riassumere.`,
configuration: 'Configurazione',
view: 'Visualizza',
mcp: 'MCP',
+ dingtalkAITableDescription:
+ 'Connettiti a Dingtalk AI Table e sincronizza i record da una tabella specificata.',
},
message: {
registered: 'Registrato!',
diff --git a/web/src/locales/ja.ts b/web/src/locales/ja.ts
index 3eb93aae5e..5b32e0fbf2 100644
--- a/web/src/locales/ja.ts
+++ b/web/src/locales/ja.ts
@@ -691,6 +691,8 @@ export default {
sureQuit: '参加したチームから退出してもよろしいですか?',
modelsToBeAddedTooltip:
'モデルプロバイダーがリストにないが「OpenAI互換」を謳っている場合は、OpenAI-API-compatible カードを選択して関連モデルを追加してください。',
+ dingtalkAITableDescription:
+ 'Dingtalk AI Table に接続し、指定されたテーブルからレコードを同期します。',
},
message: {
registered: '登録完了!',
diff --git a/web/src/locales/pt-br.ts b/web/src/locales/pt-br.ts
index 1ce96814ca..719f21a97f 100644
--- a/web/src/locales/pt-br.ts
+++ b/web/src/locales/pt-br.ts
@@ -639,6 +639,8 @@ export default {
sureQuit: 'Tem certeza de que deseja sair da equipe que você ingressou?',
modelsToBeAddedTooltip:
'Se o seu provedor de modelo não estiver listado, mas afirmar ser compatível com a OpenAI, selecione o card OpenAI-API-compatible para adicionar o(s) modelo(s) relevante(s). ',
+ dingtalkAITableDescription:
+ 'Conecte-se ao Dingtalk AI Table e sincronize registros de uma tabela especificada.',
},
message: {
registered: 'Registrado!',
diff --git a/web/src/locales/ru.ts b/web/src/locales/ru.ts
index 60b8d8ab1c..923314ef5a 100644
--- a/web/src/locales/ru.ts
+++ b/web/src/locales/ru.ts
@@ -778,6 +778,8 @@ export default {
'Подключите GitHub для синхронизации содержимого Pull Request и Issue для поиска.',
airtableDescription:
'Подключите Airtable и синхронизируйте файлы из указанной таблицы в заданном рабочем пространстве.',
+ dingtalkAITableDescription:
+ 'Подключите Dingtalk AI Table и синхронизируйте записи из указанной таблицы.',
gitlabDescription:
'Подключите GitLab для синхронизации репозиториев, задач, merge requests и связанной документации.',
asanaDescription:
diff --git a/web/src/locales/vi.ts b/web/src/locales/vi.ts
index ffe7312131..c3f933c30a 100644
--- a/web/src/locales/vi.ts
+++ b/web/src/locales/vi.ts
@@ -723,6 +723,8 @@ export default {
FishAudioRefIDMessage: `Vui lòng nhập ID của model tham chiếu (để trống để sử dụng model mặc định)`,
modelsToBeAddedTooltip:
'Nếu nhà cung cấp mô hình của bạn không có trong danh sách nhưng tuyên bố tương thích với "OpenAI", hãy chọn thẻ OpenAI-API-compatible để thêm mô hình liên quan.',
+ dingtalkAITableDescription:
+ 'Kết nối với Dingtalk AI Table và đồng bộ hóa bản ghi từ một bảng được chỉ định.',
},
message: {
registered: 'Đã đăng ký!',
diff --git a/web/src/locales/zh-traditional.ts b/web/src/locales/zh-traditional.ts
index 229c6bea5f..611dddf007 100644
--- a/web/src/locales/zh-traditional.ts
+++ b/web/src/locales/zh-traditional.ts
@@ -563,6 +563,8 @@ export default {
avatar: '头像',
avatarTip: '這會在你的個人主頁展示',
profileDescription: '在此更新您的照片和個人詳細信息。',
+ dingtalkAITableDescription:
+ '連接釘釘AI表格,同步指定表格中的記錄。',
gitlabDescription:
'連接 GitLab,同步儲存庫、Issue、合併請求(MR)及相關文件內容。',
bedrockCredentialsHint:
diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts
index 8a278177f5..3a624d42eb 100644
--- a/web/src/locales/zh.ts
+++ b/web/src/locales/zh.ts
@@ -919,6 +919,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
githubDescription:
'连接 GitHub,可同步 Pull Request 与 Issue 内容用于检索。',
airtableDescription: '连接 Airtable,同步指定工作区下指定表格中的文件。',
+ dingtalkAITableDescription:
+ '连接钉钉AI表格,同步指定表格中的记录。',
gitlabDescription:
'连接 GitLab,同步仓库、Issue、合并请求(MR)及相关文档内容。',
asanaDescription: '连接 Asana,同步工作区中的文件。',
diff --git a/web/src/pages/user-setting/data-source/constant/index.tsx b/web/src/pages/user-setting/data-source/constant/index.tsx
index 96f6987323..b06d424b4c 100644
--- a/web/src/pages/user-setting/data-source/constant/index.tsx
+++ b/web/src/pages/user-setting/data-source/constant/index.tsx
@@ -30,6 +30,7 @@ export enum DataSourceKey {
OCI_STORAGE = 'oci_storage',
GOOGLE_CLOUD_STORAGE = 'google_cloud_storage',
AIRTABLE = 'airtable',
+ DINGTALK_AI_TABLE = 'dingtalk_ai_table',
GITLAB = 'gitlab',
ASANA = 'asana',
IMAP = 'imap',
@@ -123,6 +124,11 @@ export const generateDataSourceInfo = (t: TFunction) => {
description: t(`setting.${DataSourceKey.AIRTABLE}Description`),
icon: ,
},
+ [DataSourceKey.DINGTALK_AI_TABLE]: {
+ name: 'Dingtalk AI Table',
+ description: t(`setting.dingtalkAITableDescription`),
+ icon: ,
+ },
[DataSourceKey.GITLAB]: {
name: 'GitLab',
description: t(`setting.${DataSourceKey.GITLAB}Description`),
@@ -658,6 +664,26 @@ export const DataSourceFormFields = {
required: true,
},
],
+ [DataSourceKey.DINGTALK_AI_TABLE]: [
+ {
+ label: 'Access Token',
+ name: 'config.credentials.access_token',
+ type: FormFieldType.Password,
+ required: true,
+ },
+ {
+ label: 'Base ID',
+ name: 'config.table_id',
+ type: FormFieldType.Text,
+ required: true,
+ },
+ {
+ label: 'Operator ID',
+ name: 'config.operator_id',
+ type: FormFieldType.Text,
+ required: true,
+ },
+ ],
[DataSourceKey.GITLAB]: [
{
label: 'Project Owner',
@@ -1135,6 +1161,17 @@ export const DataSourceFormDefaultValues = {
},
},
},
+ [DataSourceKey.DINGTALK_AI_TABLE]: {
+ name: '',
+ source: DataSourceKey.DINGTALK_AI_TABLE,
+ config: {
+ table_id: '',
+ operator_id: '',
+ credentials: {
+ access_token: '',
+ },
+ },
+ },
[DataSourceKey.GITLAB]: {
name: '',
source: DataSourceKey.GITLAB,