camel-ai 0.2.73a4__py3-none-any.whl → 0.2.80a2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_utils.py +38 -0
  3. camel/agents/chat_agent.py +2217 -519
  4. camel/agents/mcp_agent.py +30 -27
  5. camel/configs/__init__.py +15 -0
  6. camel/configs/aihubmix_config.py +88 -0
  7. camel/configs/amd_config.py +70 -0
  8. camel/configs/cometapi_config.py +104 -0
  9. camel/configs/minimax_config.py +93 -0
  10. camel/configs/nebius_config.py +103 -0
  11. camel/data_collectors/alpaca_collector.py +15 -6
  12. camel/datasets/base_generator.py +39 -10
  13. camel/environments/single_step.py +28 -3
  14. camel/environments/tic_tac_toe.py +1 -1
  15. camel/interpreters/__init__.py +2 -0
  16. camel/interpreters/docker/Dockerfile +3 -12
  17. camel/interpreters/e2b_interpreter.py +34 -1
  18. camel/interpreters/microsandbox_interpreter.py +395 -0
  19. camel/loaders/__init__.py +11 -2
  20. camel/loaders/chunkr_reader.py +9 -0
  21. camel/memories/agent_memories.py +48 -4
  22. camel/memories/base.py +26 -0
  23. camel/memories/blocks/chat_history_block.py +122 -4
  24. camel/memories/context_creators/score_based.py +25 -384
  25. camel/memories/records.py +88 -8
  26. camel/messages/base.py +153 -34
  27. camel/models/__init__.py +10 -0
  28. camel/models/aihubmix_model.py +83 -0
  29. camel/models/aiml_model.py +1 -16
  30. camel/models/amd_model.py +101 -0
  31. camel/models/anthropic_model.py +6 -19
  32. camel/models/aws_bedrock_model.py +2 -33
  33. camel/models/azure_openai_model.py +114 -89
  34. camel/models/base_audio_model.py +3 -1
  35. camel/models/base_model.py +32 -14
  36. camel/models/cohere_model.py +1 -16
  37. camel/models/cometapi_model.py +83 -0
  38. camel/models/crynux_model.py +1 -16
  39. camel/models/deepseek_model.py +1 -16
  40. camel/models/fish_audio_model.py +6 -0
  41. camel/models/gemini_model.py +36 -18
  42. camel/models/groq_model.py +1 -17
  43. camel/models/internlm_model.py +1 -16
  44. camel/models/litellm_model.py +1 -16
  45. camel/models/lmstudio_model.py +1 -17
  46. camel/models/minimax_model.py +83 -0
  47. camel/models/mistral_model.py +1 -16
  48. camel/models/model_factory.py +27 -1
  49. camel/models/modelscope_model.py +1 -16
  50. camel/models/moonshot_model.py +105 -24
  51. camel/models/nebius_model.py +83 -0
  52. camel/models/nemotron_model.py +0 -5
  53. camel/models/netmind_model.py +1 -16
  54. camel/models/novita_model.py +1 -16
  55. camel/models/nvidia_model.py +1 -16
  56. camel/models/ollama_model.py +4 -19
  57. camel/models/openai_compatible_model.py +62 -41
  58. camel/models/openai_model.py +62 -57
  59. camel/models/openrouter_model.py +1 -17
  60. camel/models/ppio_model.py +1 -16
  61. camel/models/qianfan_model.py +1 -16
  62. camel/models/qwen_model.py +1 -16
  63. camel/models/reka_model.py +1 -16
  64. camel/models/samba_model.py +34 -47
  65. camel/models/sglang_model.py +64 -31
  66. camel/models/siliconflow_model.py +1 -16
  67. camel/models/stub_model.py +0 -4
  68. camel/models/togetherai_model.py +1 -16
  69. camel/models/vllm_model.py +1 -16
  70. camel/models/volcano_model.py +0 -17
  71. camel/models/watsonx_model.py +1 -16
  72. camel/models/yi_model.py +1 -16
  73. camel/models/zhipuai_model.py +60 -16
  74. camel/parsers/__init__.py +18 -0
  75. camel/parsers/mcp_tool_call_parser.py +176 -0
  76. camel/retrievers/auto_retriever.py +1 -0
  77. camel/runtimes/daytona_runtime.py +11 -12
  78. camel/societies/__init__.py +2 -0
  79. camel/societies/workforce/__init__.py +2 -0
  80. camel/societies/workforce/events.py +122 -0
  81. camel/societies/workforce/prompts.py +146 -66
  82. camel/societies/workforce/role_playing_worker.py +15 -11
  83. camel/societies/workforce/single_agent_worker.py +302 -65
  84. camel/societies/workforce/structured_output_handler.py +30 -18
  85. camel/societies/workforce/task_channel.py +163 -27
  86. camel/societies/workforce/utils.py +107 -13
  87. camel/societies/workforce/workflow_memory_manager.py +772 -0
  88. camel/societies/workforce/workforce.py +1949 -579
  89. camel/societies/workforce/workforce_callback.py +74 -0
  90. camel/societies/workforce/workforce_logger.py +168 -145
  91. camel/societies/workforce/workforce_metrics.py +33 -0
  92. camel/storages/key_value_storages/json.py +15 -2
  93. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  94. camel/storages/object_storages/google_cloud.py +1 -1
  95. camel/storages/vectordb_storages/oceanbase.py +13 -13
  96. camel/storages/vectordb_storages/qdrant.py +3 -3
  97. camel/storages/vectordb_storages/tidb.py +8 -6
  98. camel/tasks/task.py +4 -3
  99. camel/toolkits/__init__.py +20 -7
  100. camel/toolkits/aci_toolkit.py +45 -0
  101. camel/toolkits/base.py +6 -4
  102. camel/toolkits/code_execution.py +28 -1
  103. camel/toolkits/context_summarizer_toolkit.py +684 -0
  104. camel/toolkits/dappier_toolkit.py +5 -1
  105. camel/toolkits/dingtalk.py +1135 -0
  106. camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
  107. camel/toolkits/excel_toolkit.py +1 -1
  108. camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +430 -36
  109. camel/toolkits/function_tool.py +13 -3
  110. camel/toolkits/github_toolkit.py +104 -17
  111. camel/toolkits/gmail_toolkit.py +1839 -0
  112. camel/toolkits/google_calendar_toolkit.py +38 -4
  113. camel/toolkits/google_drive_mcp_toolkit.py +12 -31
  114. camel/toolkits/hybrid_browser_toolkit/config_loader.py +15 -0
  115. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +77 -8
  116. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +884 -88
  117. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  118. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
  119. camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
  120. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +959 -89
  121. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +9 -2
  122. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +281 -213
  123. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  124. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  125. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  126. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +23 -3
  127. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +72 -7
  128. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +582 -132
  129. camel/toolkits/hybrid_browser_toolkit_py/actions.py +158 -0
  130. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +55 -8
  131. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +43 -0
  132. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +321 -8
  133. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +10 -4
  134. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +45 -4
  135. camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +151 -53
  136. camel/toolkits/klavis_toolkit.py +5 -1
  137. camel/toolkits/markitdown_toolkit.py +27 -1
  138. camel/toolkits/math_toolkit.py +64 -10
  139. camel/toolkits/mcp_toolkit.py +366 -71
  140. camel/toolkits/memory_toolkit.py +5 -1
  141. camel/toolkits/message_integration.py +18 -13
  142. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  143. camel/toolkits/note_taking_toolkit.py +19 -10
  144. camel/toolkits/notion_mcp_toolkit.py +16 -26
  145. camel/toolkits/openbb_toolkit.py +5 -1
  146. camel/toolkits/origene_mcp_toolkit.py +8 -49
  147. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  148. camel/toolkits/resend_toolkit.py +168 -0
  149. camel/toolkits/search_toolkit.py +264 -91
  150. camel/toolkits/slack_toolkit.py +64 -10
  151. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  152. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  153. camel/toolkits/terminal_toolkit/utils.py +532 -0
  154. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  155. camel/toolkits/video_analysis_toolkit.py +17 -11
  156. camel/toolkits/wechat_official_toolkit.py +483 -0
  157. camel/toolkits/zapier_toolkit.py +5 -1
  158. camel/types/__init__.py +2 -2
  159. camel/types/enums.py +274 -7
  160. camel/types/openai_types.py +2 -2
  161. camel/types/unified_model_type.py +15 -0
  162. camel/utils/commons.py +36 -5
  163. camel/utils/constants.py +3 -0
  164. camel/utils/context_utils.py +1003 -0
  165. camel/utils/mcp.py +138 -4
  166. camel/utils/token_counting.py +43 -20
  167. {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +223 -83
  168. {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +170 -141
  169. camel/loaders/pandas_reader.py +0 -368
  170. camel/toolkits/openai_agent_toolkit.py +0 -135
  171. camel/toolkits/terminal_toolkit.py +0 -1550
  172. {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  173. {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1135 @@
1
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ import base64
16
+ import hashlib
17
+ import hmac
18
+ import os
19
+ import re
20
+ import time
21
+ import urllib.parse
22
+ from typing import Any, Dict, List, Literal, Optional
23
+
24
+ import requests
25
+
26
+ from camel.logger import get_logger
27
+ from camel.toolkits import FunctionTool
28
+ from camel.toolkits.base import BaseToolkit
29
+ from camel.utils import MCPServer, api_keys_required, retry_on_error
30
+
31
+ logger = get_logger(__name__)
32
+
33
+ # Global variables for caching access token
34
+ _dingtalk_access_token = None
35
+ _dingtalk_access_token_expires_at = 0
36
+
37
+
38
+ @retry_on_error()
39
+ def _get_dingtalk_access_token() -> str:
40
+ r"""Gets access token for Dingtalk API.
41
+
42
+ Returns:
43
+ str: Access token for API requests.
44
+ """
45
+ global _dingtalk_access_token, _dingtalk_access_token_expires_at
46
+
47
+ if (
48
+ _dingtalk_access_token
49
+ and _dingtalk_access_token_expires_at > time.time()
50
+ ):
51
+ return _dingtalk_access_token
52
+
53
+ app_key = os.environ.get("DINGTALK_APP_KEY", "")
54
+ app_secret = os.environ.get("DINGTALK_APP_SECRET", "")
55
+
56
+ url = "https://oapi.dingtalk.com/gettoken"
57
+ params = {"appkey": app_key, "appsecret": app_secret}
58
+
59
+ response = requests.get(url, params=params, timeout=30)
60
+ response.raise_for_status()
61
+ data = response.json()
62
+
63
+ if data.get("errcode") == 0 and "access_token" in data:
64
+ _dingtalk_access_token = data["access_token"]
65
+ _dingtalk_access_token_expires_at = (
66
+ time.time() + data.get("expires_in", 7200) - 60
67
+ )
68
+ logger.info("Dingtalk access token refreshed.")
69
+ return _dingtalk_access_token
70
+ else:
71
+ errcode = data.get("errcode")
72
+ errmsg = data.get("errmsg", "Unknown error")
73
+ raise ValueError(f"Failed to get access token {errcode}: {errmsg}")
74
+
75
+
76
+ def _make_dingtalk_request(
77
+ method: Literal["GET", "POST"], endpoint: str, **kwargs
78
+ ) -> Dict[str, Any]:
79
+ r"""Makes authenticated request to Dingtalk API.
80
+
81
+ Args:
82
+ method (Literal["GET", "POST"]): HTTP method to use.
83
+ endpoint (str): API endpoint path.
84
+ **kwargs: Additional arguments passed to requests.
85
+
86
+ Returns:
87
+ Dict[str, Any]: API response data.
88
+
89
+ Raises:
90
+ Exception: If API request fails or returns error.
91
+ """
92
+ global _dingtalk_access_token, _dingtalk_access_token_expires_at
93
+ access_token = _get_dingtalk_access_token()
94
+
95
+ # Handle URL parameter concatenation
96
+ separator = "&" if "?" in endpoint else "?"
97
+ url = f"https://oapi.dingtalk.com{endpoint}{separator}access_token={access_token}"
98
+
99
+ if method.upper() == "GET":
100
+ response = requests.get(url, **kwargs)
101
+ else:
102
+ response = requests.post(url, **kwargs)
103
+
104
+ response.raise_for_status()
105
+ data = response.json()
106
+
107
+ if data.get("errcode") and data.get("errcode") != 0:
108
+ errcode = data.get("errcode")
109
+ errmsg = data.get("errmsg", "Unknown error")
110
+ raise ValueError(f"Dingtalk API error {errcode}: {errmsg}")
111
+
112
+ return data
113
+
114
+
115
+ def _generate_signature(secret: str, timestamp: str) -> str:
116
+ r"""Generates signature for Dingtalk webhook.
117
+
118
+ Args:
119
+ secret (str): Webhook secret.
120
+ timestamp (str): Current timestamp.
121
+
122
+ Returns:
123
+ str: Generated signature.
124
+ """
125
+ string_to_sign = f"{timestamp}\n{secret}"
126
+ hmac_code = hmac.new(
127
+ secret.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha256
128
+ ).digest()
129
+ return base64.b64encode(hmac_code).decode('utf-8')
130
+
131
+
132
+ @MCPServer()
133
+ class DingtalkToolkit(BaseToolkit):
134
+ r"""A toolkit for Dingtalk operations.
135
+
136
+ This toolkit provides methods to interact with the Dingtalk API,
137
+ allowing users to send messages, manage users, departments, and handle
138
+ webhook operations.
139
+ """
140
+
141
+ def __init__(self, timeout: Optional[float] = None):
142
+ r"""Initializes the DingtalkToolkit.
143
+
144
+ Args:
145
+ timeout (Optional[float]): Timeout for API requests in seconds.
146
+ """
147
+ super().__init__(timeout=timeout)
148
+ self.base_url = "https://oapi.dingtalk.com"
149
+
150
+ # Validate credentials
151
+ app_key = os.environ.get("DINGTALK_APP_KEY", "")
152
+ app_secret = os.environ.get("DINGTALK_APP_SECRET", "")
153
+
154
+ if not all([app_key, app_secret]):
155
+ raise ValueError(
156
+ "Dingtalk credentials missing. Set DINGTALK_APP_KEY and"
157
+ " DINGTALK_APP_SECRET."
158
+ )
159
+
160
+ # Initialize access token for enterprise reliability
161
+ self._initialize_token_safely()
162
+
163
+ def _initialize_token_safely(self):
164
+ r"""Safely initializes access token during toolkit setup.
165
+
166
+ This method attempts to get an access token during initialization
167
+ but doesn't raise exceptions if it fails, allowing the toolkit
168
+ to be instantiated even if credentials are temporarily invalid.
169
+ """
170
+ try:
171
+ _get_dingtalk_access_token()
172
+ logger.info("Dingtalk toolkit initialized successfully.")
173
+ except Exception as e:
174
+ logger.warning(f"Failed to initialize access token: {e}")
175
+ logger.warning(
176
+ "Toolkit created but may fail on actual API calls. "
177
+ "Check your credentials."
178
+ )
179
+
180
+ @api_keys_required(
181
+ [
182
+ (None, "DINGTALK_APP_KEY"),
183
+ (None, "DINGTALK_APP_SECRET"),
184
+ ]
185
+ )
186
+ def dingtalk_send_text_message(
187
+ self,
188
+ userid: str,
189
+ content: str,
190
+ ) -> str:
191
+ r"""Sends a text message to a Dingtalk user.
192
+
193
+ Args:
194
+ userid (str): The user's userid.
195
+ content (str): Message content.
196
+
197
+ Returns:
198
+ str: Success or error message.
199
+
200
+ References:
201
+ https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
202
+ """
203
+ payload = {
204
+ "msg": {"msgtype": "text", "text": {"content": content}},
205
+ "userid": userid,
206
+ }
207
+
208
+ try:
209
+ _make_dingtalk_request(
210
+ "POST",
211
+ "/topapi/message/corpconversation/asyncsend_v2",
212
+ headers={"Content-Type": "application/json"},
213
+ json=payload,
214
+ )
215
+ return f"Message sent successfully to user {userid}."
216
+ except Exception as e:
217
+ return f"Failed to send message: {e!s}"
218
+
219
+ @api_keys_required(
220
+ [
221
+ (None, "DINGTALK_APP_KEY"),
222
+ (None, "DINGTALK_APP_SECRET"),
223
+ ]
224
+ )
225
+ def dingtalk_send_markdown_message(
226
+ self,
227
+ userid: str,
228
+ title: str,
229
+ markdown_content: str,
230
+ ) -> str:
231
+ r"""Sends a markdown message to a Dingtalk user.
232
+
233
+ Args:
234
+ userid (str): The user's userid.
235
+ title (str): Message title.
236
+ markdown_content (str): Markdown formatted content.
237
+
238
+ Returns:
239
+ str: Success or error message.
240
+
241
+ References:
242
+ https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
243
+ """
244
+ payload = {
245
+ "msg": {
246
+ "msgtype": "markdown",
247
+ "markdown": {"title": title, "text": markdown_content},
248
+ },
249
+ "userid": userid,
250
+ }
251
+
252
+ try:
253
+ _make_dingtalk_request(
254
+ "POST",
255
+ "/topapi/message/corpconversation/asyncsend_v2",
256
+ headers={"Content-Type": "application/json"},
257
+ json=payload,
258
+ )
259
+ return f"Markdown message sent successfully to user {userid}."
260
+ except Exception as e:
261
+ return f"Failed to send markdown message: {e!s}"
262
+
263
+ @api_keys_required(
264
+ [
265
+ (None, "DINGTALK_APP_KEY"),
266
+ (None, "DINGTALK_APP_SECRET"),
267
+ ]
268
+ )
269
+ def dingtalk_get_user_info(
270
+ self,
271
+ userid: str,
272
+ ) -> Dict[str, Any]:
273
+ r"""Retrieves Dingtalk user information.
274
+
275
+ Args:
276
+ userid (str): The user's userid.
277
+
278
+ Returns:
279
+ Dict[str, Any]: User information or error information.
280
+
281
+ References:
282
+ https://open.dingtalk.com/document/orgapp-server/query-user-details
283
+ """
284
+ payload = {"userid": userid}
285
+
286
+ try:
287
+ data = _make_dingtalk_request(
288
+ "POST",
289
+ "/topapi/v2/user/get",
290
+ headers={"Content-Type": "application/json"},
291
+ json=payload,
292
+ )
293
+ return data
294
+ except Exception as e:
295
+ return {"error": f"Failed to get user info: {e!s}"}
296
+
297
+ @api_keys_required(
298
+ [
299
+ (None, "DINGTALK_APP_KEY"),
300
+ (None, "DINGTALK_APP_SECRET"),
301
+ ]
302
+ )
303
+ def dingtalk_get_department_list(
304
+ self,
305
+ dept_id: Optional[int] = None,
306
+ ) -> Dict[str, Any]:
307
+ r"""Retrieves list of departments.
308
+
309
+ Args:
310
+ dept_id (Optional[int]): Department ID. If None, gets root
311
+ departments.
312
+
313
+ Returns:
314
+ Dict[str, Any]: Department list or error information.
315
+
316
+ References:
317
+ https://open.dingtalk.com/document/orgapp-server/obtain-the-department-list-v2
318
+ """
319
+ payload = {}
320
+ if dept_id is not None:
321
+ payload["dept_id"] = dept_id
322
+
323
+ try:
324
+ data = _make_dingtalk_request(
325
+ "POST",
326
+ "/topapi/v2/department/listsub",
327
+ headers={"Content-Type": "application/json"},
328
+ json=payload,
329
+ )
330
+ return data
331
+ except Exception as e:
332
+ return {"error": f"Failed to get department list: {e!s}"}
333
+
334
+ @api_keys_required(
335
+ [
336
+ (None, "DINGTALK_APP_KEY"),
337
+ (None, "DINGTALK_APP_SECRET"),
338
+ ]
339
+ )
340
+ def dingtalk_get_department_users(
341
+ self,
342
+ dept_id: int,
343
+ offset: int = 0,
344
+ size: int = 100,
345
+ ) -> Dict[str, Any]:
346
+ r"""Retrieves users in a department.
347
+
348
+ Args:
349
+ dept_id (int): Department ID.
350
+ offset (int): Offset for pagination (default: 0).
351
+ size (int): Number of users to retrieve (default: 100, max: 100).
352
+
353
+ Returns:
354
+ Dict[str, Any]: Users list or error information.
355
+
356
+ References:
357
+ https://open.dingtalk.com/document/orgapp-server/queries-the-complete-information-of-a-department-user
358
+ """
359
+ payload = {"dept_id": dept_id, "offset": offset, "size": size}
360
+
361
+ try:
362
+ data = _make_dingtalk_request(
363
+ "POST",
364
+ "/topapi/v2/user/list",
365
+ headers={"Content-Type": "application/json"},
366
+ json=payload,
367
+ )
368
+ return data
369
+ except Exception as e:
370
+ return {"error": f"Failed to get department users: {e!s}"}
371
+
372
+ @api_keys_required(
373
+ [
374
+ (None, "DINGTALK_APP_KEY"),
375
+ (None, "DINGTALK_APP_SECRET"),
376
+ ]
377
+ )
378
+ def dingtalk_search_users_by_name(
379
+ self,
380
+ name: str,
381
+ ) -> Dict[str, Any]:
382
+ r"""Searches for users by name.
383
+
384
+ Args:
385
+ name (str): User name to search for.
386
+
387
+ Returns:
388
+ Dict[str, Any]: Search results or error information.
389
+
390
+ References:
391
+ https://open.dingtalk.com/document/orgapp-server/query-users
392
+ """
393
+ payload = {"name": name}
394
+
395
+ try:
396
+ data = _make_dingtalk_request(
397
+ "POST",
398
+ "/topapi/user/search",
399
+ headers={"Content-Type": "application/json"},
400
+ json=payload,
401
+ )
402
+ return data
403
+ except Exception as e:
404
+ return {"error": f"Failed to search users: {e!s}"}
405
+
406
+ # Include more webhook methods if useful. Double check link implementation
407
+ # is done correctly here.
408
+ def dingtalk_send_webhook_message(
409
+ self,
410
+ content: str,
411
+ msgtype: Literal["text", "markdown", "link", "actionCard"] = "text",
412
+ title: Optional[str] = None,
413
+ webhook_url: Optional[str] = None,
414
+ webhook_secret: Optional[str] = None,
415
+ ) -> str:
416
+ r"""Sends a message via Dingtalk webhook.
417
+
418
+ Args:
419
+ content (str): Message content.
420
+ msgtype (Literal): Message type (text, markdown, link, actionCard).
421
+ title (Optional[str]): Message title (required for markdown).
422
+ webhook_url (Optional[str]): Webhook URL. If None, uses env var.
423
+ webhook_secret (Optional[str]): Webhook secret. If None, uses env
424
+ var.
425
+
426
+ Returns:
427
+ str: Success or error message.
428
+
429
+ References:
430
+ https://open.dingtalk.com/document/robots/custom-robot-access
431
+ """
432
+ # Get webhook configuration
433
+ url = webhook_url or os.environ.get("DINGTALK_WEBHOOK_URL", "")
434
+ secret = webhook_secret or os.environ.get(
435
+ "DINGTALK_WEBHOOK_SECRET", ""
436
+ )
437
+
438
+ if not url:
439
+ return "Error: Webhook URL not provided or set in environment"
440
+
441
+ # Prepare message payload
442
+ payload: Dict[str, Any] = {"msgtype": msgtype}
443
+
444
+ if msgtype == "text":
445
+ payload["text"] = {"content": content}
446
+ elif msgtype == "markdown":
447
+ if not title:
448
+ return "Error: Title is required for markdown messages"
449
+ payload["markdown"] = {"title": title, "text": content}
450
+ elif msgtype == "link":
451
+ # For link messages, content should be structured differently
452
+ # This is a simplified implementation
453
+ payload["link"] = {
454
+ "text": content,
455
+ "title": title or "Link Message",
456
+ "messageUrl": "https://www.dingtalk.com/",
457
+ }
458
+ elif msgtype == "actionCard":
459
+ payload["actionCard"] = {
460
+ "title": title or "Action Card",
461
+ "text": content,
462
+ "singleTitle": "Read More",
463
+ "singleURL": "https://www.dingtalk.com/",
464
+ }
465
+
466
+ # Add signature if secret is provided
467
+ if secret:
468
+ timestamp = str(round(time.time() * 1000))
469
+ sign = _generate_signature(secret, timestamp)
470
+ url += f"&timestamp={timestamp}&sign={urllib.parse.quote(sign)}"
471
+
472
+ try:
473
+ response = requests.post(
474
+ url,
475
+ json=payload,
476
+ headers={"Content-Type": "application/json"},
477
+ timeout=self.timeout,
478
+ )
479
+ response.raise_for_status()
480
+
481
+ result = response.json()
482
+ if result.get("errcode") == 0:
483
+ return "Webhook message sent successfully"
484
+ else:
485
+ return (
486
+ f"Webhook error: {result.get('errmsg', 'Unknown error')}"
487
+ )
488
+
489
+ except Exception as e:
490
+ return f"Failed to send webhook message: {e!s}"
491
+
492
+ @api_keys_required(
493
+ [
494
+ (None, "DINGTALK_APP_KEY"),
495
+ (None, "DINGTALK_APP_SECRET"),
496
+ ]
497
+ )
498
+ def dingtalk_create_group(
499
+ self,
500
+ name: str,
501
+ owner: str,
502
+ useridlist: List[str],
503
+ ) -> Dict[str, Any]:
504
+ r"""Creates a Dingtalk group.
505
+
506
+ Args:
507
+ name (str): Group name.
508
+ owner (str): Group owner's userid.
509
+ useridlist (List[str]): List of user IDs to add to the group.
510
+
511
+ Returns:
512
+ Dict[str, Any]: Group creation result with chatid or error.
513
+
514
+ References:
515
+ https://open.dingtalk.com/document/orgapp-server/create-group-session
516
+ """
517
+ payload = {
518
+ "name": name,
519
+ "owner": owner,
520
+ "useridlist": useridlist,
521
+ }
522
+
523
+ try:
524
+ data = _make_dingtalk_request(
525
+ "POST",
526
+ "/topapi/im/chat/create",
527
+ headers={"Content-Type": "application/json"},
528
+ json=payload,
529
+ )
530
+ return data
531
+ except Exception as e:
532
+ return {"error": f"Failed to create group: {e!s}"}
533
+
534
+ @api_keys_required(
535
+ [
536
+ (None, "DINGTALK_APP_KEY"),
537
+ (None, "DINGTALK_APP_SECRET"),
538
+ ]
539
+ )
540
+ def dingtalk_send_group_message(
541
+ self,
542
+ chatid: str,
543
+ content: str,
544
+ msgtype: Literal["text", "markdown"] = "text",
545
+ ) -> str:
546
+ r"""Sends a message to a Dingtalk group.
547
+
548
+ Args:
549
+ chatid (str): Group chat ID.
550
+ content (str): Message content.
551
+ msgtype (Literal["text", "markdown"]): Message type.
552
+
553
+ Returns:
554
+ str: Success or error message.
555
+
556
+ References:
557
+ https://open.dingtalk.com/document/orgapp-server/send-group-messages
558
+ """
559
+ msg_data: Dict[str, Any] = {"msgtype": msgtype}
560
+
561
+ if msgtype == "text":
562
+ msg_data["text"] = {"content": content}
563
+ elif msgtype == "markdown":
564
+ msg_data["markdown"] = {"text": content}
565
+
566
+ payload = {
567
+ "msg": msg_data,
568
+ "chatid": chatid,
569
+ }
570
+
571
+ try:
572
+ _make_dingtalk_request(
573
+ "POST",
574
+ "/topapi/message/corpconversation/asyncsend_v2",
575
+ headers={"Content-Type": "application/json"},
576
+ json=payload,
577
+ )
578
+ return f"Message sent successfully to group {chatid}."
579
+ except Exception as e:
580
+ return f"Failed to send group message: {e!s}"
581
+
582
+ @api_keys_required(
583
+ [
584
+ (None, "DINGTALK_APP_KEY"),
585
+ (None, "DINGTALK_APP_SECRET"),
586
+ ]
587
+ )
588
+ def dingtalk_send_link_message(
589
+ self,
590
+ userid: str,
591
+ title: str,
592
+ text: str,
593
+ message_url: str,
594
+ pic_url: Optional[str] = None,
595
+ ) -> str:
596
+ r"""Sends a link message to a Dingtalk user.
597
+
598
+ Args:
599
+ userid (str): The user's userid.
600
+ title (str): Link title.
601
+ text (str): Link description text.
602
+ message_url (str): URL to link to.
603
+ pic_url (Optional[str]): Picture URL for the link.
604
+
605
+ Returns:
606
+ str: Success or error message.
607
+
608
+ References:
609
+ https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
610
+ """
611
+ link_data: Dict[str, Any] = {
612
+ "title": title,
613
+ "text": text,
614
+ "messageUrl": message_url,
615
+ }
616
+
617
+ if pic_url:
618
+ link_data["picUrl"] = pic_url
619
+
620
+ payload = {
621
+ "msg": {"msgtype": "link", "link": link_data},
622
+ "userid": userid,
623
+ }
624
+
625
+ try:
626
+ _make_dingtalk_request(
627
+ "POST",
628
+ "/topapi/message/corpconversation/asyncsend_v2",
629
+ headers={"Content-Type": "application/json"},
630
+ json=payload,
631
+ )
632
+ return f"Link message sent successfully to user {userid}."
633
+ except Exception as e:
634
+ return f"Failed to send link message: {e!s}"
635
+
636
+ @api_keys_required(
637
+ [
638
+ (None, "DINGTALK_APP_KEY"),
639
+ (None, "DINGTALK_APP_SECRET"),
640
+ ]
641
+ )
642
+ def dingtalk_send_action_card_message(
643
+ self,
644
+ userid: str,
645
+ title: str,
646
+ text: str,
647
+ single_title: str,
648
+ single_url: str,
649
+ ) -> str:
650
+ r"""Sends an action card message to a Dingtalk user.
651
+
652
+ Args:
653
+ userid (str): The user's userid.
654
+ title (str): Card title.
655
+ text (str): Card content text.
656
+ single_title (str): Action button title.
657
+ single_url (str): Action button URL.
658
+
659
+ Returns:
660
+ str: Success or error message.
661
+
662
+ References:
663
+ https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
664
+ """
665
+ payload = {
666
+ "msg": {
667
+ "msgtype": "actionCard",
668
+ "actionCard": {
669
+ "title": title,
670
+ "text": text,
671
+ "singleTitle": single_title,
672
+ "singleURL": single_url,
673
+ },
674
+ },
675
+ "userid": userid,
676
+ }
677
+
678
+ try:
679
+ _make_dingtalk_request(
680
+ "POST",
681
+ "/topapi/message/corpconversation/asyncsend_v2",
682
+ headers={"Content-Type": "application/json"},
683
+ json=payload,
684
+ )
685
+ return f"Action card message sent successfully to user {userid}."
686
+ except Exception as e:
687
+ return f"Failed to send action card message: {e!s}"
688
+
689
+ @api_keys_required(
690
+ [
691
+ (None, "DINGTALK_APP_KEY"),
692
+ (None, "DINGTALK_APP_SECRET"),
693
+ ]
694
+ )
695
+ def dingtalk_get_user_by_mobile(self, mobile: str) -> Dict[str, Any]:
696
+ r"""Gets user information by mobile number.
697
+
698
+ Args:
699
+ mobile (str): User's mobile number. Should be a valid Chinese
700
+ mobile number format (11 digits starting with 1).
701
+
702
+ Returns:
703
+ Dict[str, Any]: User information or error information.
704
+ """
705
+ # Validate mobile number format (Chinese mobile number: 11 digits
706
+ # starting with 1)
707
+ mobile_pattern = r'^1[3-9]\d{9}$'
708
+ if not re.match(mobile_pattern, mobile):
709
+ return {
710
+ "error": "Invalid mobile number format. Expected 11 digits "
711
+ "starting with 1 (e.g., 13800000000)."
712
+ }
713
+
714
+ payload = {"mobile": mobile}
715
+
716
+ try:
717
+ data = _make_dingtalk_request(
718
+ "POST",
719
+ "/topapi/v2/user/getbymobile",
720
+ headers={"Content-Type": "application/json"},
721
+ json=payload,
722
+ )
723
+ return data
724
+ except Exception as e:
725
+ return {"error": f"Failed to get user by mobile: {e!s}"}
726
+
727
+ @api_keys_required(
728
+ [
729
+ (None, "DINGTALK_APP_KEY"),
730
+ (None, "DINGTALK_APP_SECRET"),
731
+ ]
732
+ )
733
+ def dingtalk_get_user_by_unionid(self, unionid: str) -> Dict[str, Any]:
734
+ r"""Gets user information by unionid.
735
+
736
+ Args:
737
+ unionid (str): User's unique identifier across all DingTalk
738
+ organizations. This is a global identifier that remains
739
+ consistent even if the user belongs to multiple DingTalk
740
+ organizations, unlike userid which is organization-specific.
741
+
742
+ Returns:
743
+ Dict[str, Any]: User information or error information.
744
+
745
+ References:
746
+ https://open.dingtalk.com/document/orgapp-server/query-a-user-by-the-union-id
747
+ """
748
+ try:
749
+ data = _make_dingtalk_request(
750
+ "POST",
751
+ "/topapi/user/getbyunionid",
752
+ headers={"Content-Type": "application/json"},
753
+ json={"unionid": unionid},
754
+ )
755
+ return data
756
+ except Exception as e:
757
+ return {"error": f"Failed to get user by unionid: {e!s}"}
758
+
759
+ @api_keys_required(
760
+ [
761
+ (None, "DINGTALK_APP_KEY"),
762
+ (None, "DINGTALK_APP_SECRET"),
763
+ ]
764
+ )
765
+ def dingtalk_get_department_detail(self, dept_id: int) -> Dict[str, Any]:
766
+ r"""Gets detailed information about a department.
767
+
768
+ Args:
769
+ dept_id (int): Department ID.
770
+
771
+ Returns:
772
+ Dict[str, Any]: Department details or error information.
773
+
774
+ References:
775
+ https://open.dingtalk.com/document/orgapp-server/query-department-details0-v2
776
+ """
777
+ payload = {"dept_id": dept_id}
778
+
779
+ try:
780
+ data = _make_dingtalk_request(
781
+ "POST",
782
+ "/topapi/v2/department/get",
783
+ headers={"Content-Type": "application/json"},
784
+ json=payload,
785
+ )
786
+ return data
787
+ except Exception as e:
788
+ return {"error": f"Failed to get department detail: {e!s}"}
789
+
790
+ @api_keys_required(
791
+ [
792
+ (None, "DINGTALK_APP_KEY"),
793
+ (None, "DINGTALK_APP_SECRET"),
794
+ ]
795
+ )
796
+ def dingtalk_send_oa_message(
797
+ self,
798
+ userid: str,
799
+ message_url: str,
800
+ head_bgcolor: str,
801
+ head_text: str,
802
+ body_title: str,
803
+ body_content: str,
804
+ ) -> str:
805
+ r"""Sends an OA (Office Automation) message to a Dingtalk user.
806
+
807
+ Args:
808
+ userid (str): The user's userid.
809
+ message_url (str): URL for the message action.
810
+ head_bgcolor (str): Header background color (hex format).
811
+ head_text (str): Header text.
812
+ body_title (str): Body title.
813
+ body_content (str): Body content.
814
+
815
+ Returns:
816
+ str: Success or error message.
817
+
818
+ References:
819
+ https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
820
+ """
821
+ oa_data: Dict[str, Any] = {
822
+ "message_url": message_url,
823
+ "head": {
824
+ "bgcolor": head_bgcolor,
825
+ "text": head_text,
826
+ },
827
+ "body": {
828
+ "title": body_title,
829
+ "form": [{"key": "Content:", "value": body_content}],
830
+ },
831
+ }
832
+
833
+ payload = {
834
+ "msg": {"msgtype": "oa", "oa": oa_data},
835
+ "userid": userid,
836
+ }
837
+
838
+ try:
839
+ _make_dingtalk_request(
840
+ "POST",
841
+ "/topapi/message/corpconversation/asyncsend_v2",
842
+ headers={"Content-Type": "application/json"},
843
+ json=payload,
844
+ )
845
+ return f"OA message sent successfully to user {userid}."
846
+ except Exception as e:
847
+ return f"Failed to send OA message: {e!s}"
848
+
849
+ @api_keys_required(
850
+ [
851
+ (None, "DINGTALK_APP_KEY"),
852
+ (None, "DINGTALK_APP_SECRET"),
853
+ ]
854
+ )
855
+ def dingtalk_get_group_info(self, chatid: str) -> Dict[str, Any]:
856
+ r"""Gets information about a group chat.
857
+
858
+ Args:
859
+ chatid (str): Group chat ID.
860
+
861
+ Returns:
862
+ Dict[str, Any]: Group information or error information.
863
+
864
+ References:
865
+ https://open.dingtalk.com/document/orgapp-server/query-group-session-information
866
+ """
867
+ payload = {"chatid": chatid}
868
+
869
+ try:
870
+ data = _make_dingtalk_request(
871
+ "POST",
872
+ "/topapi/im/chat/get",
873
+ headers={"Content-Type": "application/json"},
874
+ json=payload,
875
+ )
876
+ return data
877
+ except Exception as e:
878
+ return {"error": f"Failed to get group info: {e!s}"}
879
+
880
+ @api_keys_required(
881
+ [
882
+ (None, "DINGTALK_APP_KEY"),
883
+ (None, "DINGTALK_APP_SECRET"),
884
+ ]
885
+ )
886
+ def dingtalk_update_group(
887
+ self,
888
+ chatid: str,
889
+ name: Optional[str] = None,
890
+ owner: Optional[str] = None,
891
+ add_useridlist: Optional[List[str]] = None,
892
+ del_useridlist: Optional[List[str]] = None,
893
+ ) -> Dict[str, Any]:
894
+ r"""Updates a Dingtalk group configuration.
895
+
896
+ Args:
897
+ chatid (str): Group chat ID.
898
+ name (Optional[str]): New group name.
899
+ owner (Optional[str]): New group owner userid.
900
+ add_useridlist (Optional[List[str]]): List of user IDs to add.
901
+ Note: Internally converted to comma-separated string as
902
+ required by the DingTalk API.
903
+ del_useridlist (Optional[List[str]]): List of user IDs to remove.
904
+ Note: Internally converted to comma-separated string as
905
+ required by the DingTalk API.
906
+
907
+ Returns:
908
+ Dict[str, Any]: Update result or error information.
909
+
910
+ References:
911
+ https://open.dingtalk.com/document/orgapp-server/modify-group-session
912
+ """
913
+ payload: Dict[str, Any] = {"chatid": chatid}
914
+
915
+ if name:
916
+ payload["name"] = name
917
+ if owner:
918
+ payload["owner"] = owner
919
+ # Note: DingTalk update group API requires comma-separated string
920
+ # format
921
+ # This is different from send_work_notification which uses array format
922
+ if add_useridlist:
923
+ payload["add_useridlist"] = ",".join(add_useridlist)
924
+ if del_useridlist:
925
+ payload["del_useridlist"] = ",".join(del_useridlist)
926
+
927
+ try:
928
+ data = _make_dingtalk_request(
929
+ "POST",
930
+ "/topapi/im/chat/update",
931
+ headers={"Content-Type": "application/json"},
932
+ json=payload,
933
+ )
934
+ return data
935
+ except Exception as e:
936
+ return {"error": f"Failed to update group: {e!s}"}
937
+
938
+ @api_keys_required(
939
+ [
940
+ (None, "DINGTALK_APP_KEY"),
941
+ (None, "DINGTALK_APP_SECRET"),
942
+ ]
943
+ )
944
+ def dingtalk_send_work_notification(
945
+ self,
946
+ userid_list: List[str],
947
+ msg_content: str,
948
+ msg_type: Literal["text", "markdown"] = "text",
949
+ ) -> str:
950
+ r"""Sends work notification to multiple users.
951
+
952
+ Args:
953
+ userid_list (List[str]): List of user IDs to send to.
954
+ Note: This API accepts array format, unlike some other APIs
955
+ that require comma-separated strings.
956
+ msg_content (str): Message content.
957
+ msg_type (Literal["text", "markdown"]): Message type.
958
+
959
+ Returns:
960
+ str: Success or error message.
961
+
962
+ References:
963
+ https://open.dingtalk.com/document/orgapp-server/asynchronous-sending-of-enterprise-session-messages
964
+ """
965
+ if not userid_list:
966
+ return "Error: userid_list cannot be empty"
967
+
968
+ if len(userid_list) > 100:
969
+ return "Error: Cannot send to more than 100 users at once"
970
+
971
+ msg_data: Dict[str, Any] = {"msgtype": msg_type}
972
+ if msg_type == "text":
973
+ msg_data["text"] = {"content": msg_content}
974
+ elif msg_type == "markdown":
975
+ msg_data["markdown"] = {"text": msg_content}
976
+
977
+ payload = {
978
+ "msg": msg_data,
979
+ "userid_list": userid_list, # Array format for this API
980
+ }
981
+
982
+ try:
983
+ _make_dingtalk_request(
984
+ "POST",
985
+ "/topapi/message/corpconversation/asyncsend_v2",
986
+ headers={"Content-Type": "application/json"},
987
+ json=payload,
988
+ )
989
+ return (
990
+ f"Work notification sent successfully to "
991
+ f"{len(userid_list)} users."
992
+ )
993
+ except Exception as e:
994
+ return f"Failed to send work notification: {e!s}"
995
+
996
+ @api_keys_required(
997
+ [
998
+ (None, "DINGTALK_APP_KEY"),
999
+ (None, "DINGTALK_APP_SECRET"),
1000
+ ]
1001
+ )
1002
+ def dingtalk_get_userid_by_phone(self, phone_number: str) -> str:
1003
+ r"""Gets user ID by phone number for LLM agents.
1004
+
1005
+ Args:
1006
+ phone_number (str): User's phone number.
1007
+
1008
+ Returns:
1009
+ str: User ID or error message.
1010
+
1011
+ References:
1012
+ https://open.dingtalk.com/document/orgapp-server/query-user-details
1013
+ """
1014
+ try:
1015
+ user_info = self.dingtalk_get_user_by_mobile(phone_number)
1016
+ if 'result' in user_info and 'userid' in user_info['result']:
1017
+ return user_info['result']['userid']
1018
+ else:
1019
+ return f"User not found for phone number: {phone_number}"
1020
+ except Exception as e:
1021
+ return f"Failed to get user ID by phone: {e!s}"
1022
+
1023
+ @api_keys_required(
1024
+ [
1025
+ (None, "DINGTALK_APP_KEY"),
1026
+ (None, "DINGTALK_APP_SECRET"),
1027
+ ]
1028
+ )
1029
+ def dingtalk_get_userid_by_name(self, user_name: str) -> str:
1030
+ r"""Gets user ID by user name for LLM agents.
1031
+
1032
+ Args:
1033
+ user_name (str): User's display name.
1034
+
1035
+ Returns:
1036
+ str: User ID or error message.
1037
+
1038
+ References:
1039
+ https://open.dingtalk.com/document/orgapp-server/query-users
1040
+ """
1041
+ try:
1042
+ search_result = self.dingtalk_search_users_by_name(user_name)
1043
+ if search_result.get('result'):
1044
+ # Return the first match
1045
+ return search_result['result'][0].get(
1046
+ 'userid', f"No userid found for user: {user_name}"
1047
+ )
1048
+ else:
1049
+ return f"User not found with name: {user_name}"
1050
+ except Exception as e:
1051
+ return f"Failed to get user ID by name: {e!s}"
1052
+
1053
+ @api_keys_required(
1054
+ [
1055
+ (None, "DINGTALK_APP_KEY"),
1056
+ (None, "DINGTALK_APP_SECRET"),
1057
+ ]
1058
+ )
1059
+ def dingtalk_get_department_id_by_name(self, department_name: str) -> str:
1060
+ r"""Gets department ID by department name for LLM agents.
1061
+
1062
+ Args:
1063
+ department_name (str): Department name to search for.
1064
+
1065
+ Returns:
1066
+ str: Department ID or error message.
1067
+ """
1068
+ try:
1069
+ dept_list = self.dingtalk_get_department_list()
1070
+ if 'result' in dept_list:
1071
+ for dept in dept_list['result']:
1072
+ if dept.get('name') == department_name:
1073
+ return str(dept.get('id', ''))
1074
+ return f"Department not found: {department_name}"
1075
+ else:
1076
+ return "Failed to get department list"
1077
+ except Exception as e:
1078
+ return f"Failed to get department ID by name: {e!s}"
1079
+
1080
+ def dingtalk_get_chatid_by_group_name(self, group_name: str) -> str:
1081
+ r"""Gets chat ID by group name for LLM agents.
1082
+
1083
+ Note: This function provides guidance as Dingtalk API doesn't directly
1084
+ support searching groups by name. Users should use the group creation
1085
+ response or group management features to obtain chat IDs.
1086
+
1087
+ Args:
1088
+ group_name (str): Group name to search for.
1089
+
1090
+ Returns:
1091
+ str: Guidance message for obtaining chat ID.
1092
+ """
1093
+ return (
1094
+ f"To get chat ID for group '{group_name}': "
1095
+ "1. Use dingtalk_create_group() and save the returned chatid, or "
1096
+ "2. Contact group admin to provide the chat ID, or "
1097
+ "3. Use Dingtalk admin console to find the group ID. "
1098
+ "Chat IDs are typically returned when creating groups."
1099
+ )
1100
+
1101
+ def get_tools(self) -> List[FunctionTool]:
1102
+ r"""Returns toolkit functions as tools."""
1103
+ return [
1104
+ # Original message functions
1105
+ FunctionTool(self.dingtalk_send_text_message),
1106
+ FunctionTool(self.dingtalk_send_markdown_message),
1107
+ FunctionTool(self.dingtalk_send_webhook_message),
1108
+ # New message types
1109
+ FunctionTool(self.dingtalk_send_link_message),
1110
+ FunctionTool(self.dingtalk_send_action_card_message),
1111
+ FunctionTool(self.dingtalk_send_oa_message),
1112
+ FunctionTool(self.dingtalk_send_work_notification),
1113
+ # User management functions
1114
+ FunctionTool(self.dingtalk_get_user_info),
1115
+ FunctionTool(self.dingtalk_get_user_by_mobile),
1116
+ FunctionTool(self.dingtalk_get_user_by_unionid),
1117
+ FunctionTool(self.dingtalk_search_users_by_name),
1118
+ # Department management functions
1119
+ FunctionTool(self.dingtalk_get_department_list),
1120
+ FunctionTool(self.dingtalk_get_department_users),
1121
+ FunctionTool(self.dingtalk_get_department_detail),
1122
+ # Group management functions
1123
+ FunctionTool(self.dingtalk_create_group),
1124
+ FunctionTool(self.dingtalk_send_group_message),
1125
+ FunctionTool(self.dingtalk_get_group_info),
1126
+ FunctionTool(self.dingtalk_update_group),
1127
+ # Helper functions for LLM agents to get IDs
1128
+ FunctionTool(self.dingtalk_get_userid_by_phone),
1129
+ FunctionTool(self.dingtalk_get_userid_by_name),
1130
+ FunctionTool(self.dingtalk_get_department_id_by_name),
1131
+ FunctionTool(self.dingtalk_get_chatid_by_group_name),
1132
+ ]
1133
+
1134
+
1135
+ # many other functionalities, cards etc build on top after initial works