camel-ai 0.2.59__py3-none-any.whl → 0.2.82__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.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (506) hide show
  1. camel/__init__.py +3 -3
  2. camel/agents/__init__.py +2 -2
  3. camel/agents/_types.py +9 -4
  4. camel/agents/_utils.py +40 -2
  5. camel/agents/base.py +2 -2
  6. camel/agents/chat_agent.py +5012 -902
  7. camel/agents/critic_agent.py +2 -2
  8. camel/agents/deductive_reasoner_agent.py +56 -56
  9. camel/agents/embodied_agent.py +2 -2
  10. camel/agents/knowledge_graph_agent.py +20 -20
  11. camel/agents/mcp_agent.py +39 -36
  12. camel/agents/multi_hop_generator_agent.py +3 -3
  13. camel/agents/programmed_agent_instruction.py +2 -2
  14. camel/agents/repo_agent.py +4 -3
  15. camel/agents/role_assignment_agent.py +2 -2
  16. camel/agents/search_agent.py +2 -2
  17. camel/agents/task_agent.py +2 -2
  18. camel/agents/tool_agents/__init__.py +2 -2
  19. camel/agents/tool_agents/base.py +2 -2
  20. camel/agents/tool_agents/hugging_face_tool_agent.py +3 -3
  21. camel/benchmarks/__init__.py +2 -2
  22. camel/benchmarks/apibank.py +5 -5
  23. camel/benchmarks/apibench.py +2 -2
  24. camel/benchmarks/base.py +2 -2
  25. camel/benchmarks/browsecomp.py +44 -33
  26. camel/benchmarks/gaia.py +17 -13
  27. camel/benchmarks/mock_website/README.md +94 -0
  28. camel/benchmarks/mock_website/mock_web.py +299 -0
  29. camel/benchmarks/mock_website/requirements.txt +3 -0
  30. camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
  31. camel/benchmarks/mock_website/task.json +104 -0
  32. camel/benchmarks/nexus.py +3 -3
  33. camel/benchmarks/ragbench.py +2 -2
  34. camel/bots/__init__.py +2 -2
  35. camel/bots/discord/__init__.py +2 -2
  36. camel/bots/discord/discord_app.py +2 -2
  37. camel/bots/discord/discord_installation.py +2 -2
  38. camel/bots/discord/discord_store.py +3 -3
  39. camel/bots/slack/__init__.py +2 -2
  40. camel/bots/slack/models.py +4 -4
  41. camel/bots/slack/slack_app.py +2 -2
  42. camel/bots/telegram_bot.py +2 -2
  43. camel/configs/__init__.py +26 -2
  44. camel/configs/aihubmix_config.py +90 -0
  45. camel/configs/aiml_config.py +2 -2
  46. camel/configs/amd_config.py +70 -0
  47. camel/configs/anthropic_config.py +8 -7
  48. camel/configs/base_config.py +2 -2
  49. camel/configs/bedrock_config.py +5 -3
  50. camel/configs/cerebras_config.py +98 -0
  51. camel/configs/cohere_config.py +3 -3
  52. camel/configs/cometapi_config.py +106 -0
  53. camel/configs/crynux_config.py +94 -0
  54. camel/configs/deepseek_config.py +9 -8
  55. camel/configs/gemini_config.py +6 -4
  56. camel/configs/groq_config.py +6 -4
  57. camel/configs/internlm_config.py +6 -4
  58. camel/configs/litellm_config.py +2 -2
  59. camel/configs/lmstudio_config.py +6 -4
  60. camel/configs/minimax_config.py +95 -0
  61. camel/configs/mistral_config.py +3 -3
  62. camel/configs/modelscope_config.py +5 -3
  63. camel/configs/moonshot_config.py +2 -2
  64. camel/configs/nebius_config.py +105 -0
  65. camel/configs/netmind_config.py +2 -2
  66. camel/configs/novita_config.py +2 -2
  67. camel/configs/nvidia_config.py +2 -2
  68. camel/configs/ollama_config.py +2 -2
  69. camel/configs/openai_config.py +8 -3
  70. camel/configs/openrouter_config.py +6 -4
  71. camel/configs/ppio_config.py +2 -2
  72. camel/configs/qianfan_config.py +85 -0
  73. camel/configs/qwen_config.py +2 -2
  74. camel/configs/reka_config.py +3 -3
  75. camel/configs/samba_config.py +8 -6
  76. camel/configs/sglang_config.py +2 -2
  77. camel/configs/siliconflow_config.py +2 -2
  78. camel/configs/togetherai_config.py +2 -2
  79. camel/configs/vllm_config.py +4 -2
  80. camel/configs/watsonx_config.py +2 -2
  81. camel/configs/yi_config.py +6 -4
  82. camel/configs/zhipuai_config.py +6 -4
  83. camel/{data_collector → data_collectors}/__init__.py +2 -2
  84. camel/{data_collector → data_collectors}/alpaca_collector.py +19 -10
  85. camel/{data_collector → data_collectors}/base.py +2 -2
  86. camel/{data_collector → data_collectors}/sharegpt_collector.py +3 -3
  87. camel/datagen/__init__.py +2 -2
  88. camel/datagen/cot_datagen.py +32 -37
  89. camel/datagen/evol_instruct/__init__.py +2 -2
  90. camel/datagen/evol_instruct/evol_instruct.py +2 -2
  91. camel/datagen/evol_instruct/scorer.py +24 -25
  92. camel/datagen/evol_instruct/templates.py +48 -48
  93. camel/datagen/self_improving_cot.py +5 -5
  94. camel/datagen/self_instruct/__init__.py +2 -2
  95. camel/datagen/self_instruct/filter/__init__.py +2 -2
  96. camel/datagen/self_instruct/filter/filter_function.py +2 -2
  97. camel/datagen/self_instruct/filter/filter_registry.py +2 -2
  98. camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
  99. camel/datagen/self_instruct/self_instruct.py +2 -2
  100. camel/datagen/self_instruct/templates.py +47 -47
  101. camel/datagen/source2synth/__init__.py +2 -2
  102. camel/datagen/source2synth/data_processor.py +2 -2
  103. camel/datagen/source2synth/models.py +2 -2
  104. camel/datagen/source2synth/user_data_processor_config.py +2 -2
  105. camel/datahubs/__init__.py +2 -2
  106. camel/datahubs/base.py +2 -2
  107. camel/datahubs/huggingface.py +2 -2
  108. camel/datahubs/models.py +2 -2
  109. camel/datasets/__init__.py +2 -2
  110. camel/datasets/base_generator.py +41 -12
  111. camel/datasets/few_shot_generator.py +18 -18
  112. camel/datasets/models.py +3 -3
  113. camel/datasets/self_instruct_generator.py +2 -2
  114. camel/datasets/static_dataset.py +152 -2
  115. camel/embeddings/__init__.py +2 -2
  116. camel/embeddings/azure_embedding.py +2 -2
  117. camel/embeddings/base.py +2 -2
  118. camel/embeddings/gemini_embedding.py +2 -2
  119. camel/embeddings/jina_embedding.py +10 -3
  120. camel/embeddings/mistral_embedding.py +2 -2
  121. camel/embeddings/openai_compatible_embedding.py +2 -2
  122. camel/embeddings/openai_embedding.py +2 -2
  123. camel/embeddings/sentence_transformers_embeddings.py +4 -4
  124. camel/embeddings/together_embedding.py +2 -2
  125. camel/embeddings/vlm_embedding.py +11 -4
  126. camel/environments/__init__.py +14 -2
  127. camel/environments/models.py +2 -2
  128. camel/environments/multi_step.py +2 -2
  129. camel/environments/rlcards_env.py +860 -0
  130. camel/environments/single_step.py +30 -5
  131. camel/environments/tic_tac_toe.py +3 -3
  132. camel/extractors/__init__.py +2 -2
  133. camel/extractors/base.py +2 -2
  134. camel/extractors/python_strategies.py +2 -2
  135. camel/generators.py +2 -2
  136. camel/human.py +2 -2
  137. camel/interpreters/__init__.py +4 -2
  138. camel/interpreters/base.py +16 -3
  139. camel/interpreters/docker/Dockerfile +53 -7
  140. camel/interpreters/docker_interpreter.py +70 -11
  141. camel/interpreters/e2b_interpreter.py +59 -11
  142. camel/interpreters/internal_python_interpreter.py +81 -4
  143. camel/interpreters/interpreter_error.py +2 -2
  144. camel/interpreters/ipython_interpreter.py +23 -5
  145. camel/interpreters/microsandbox_interpreter.py +395 -0
  146. camel/interpreters/subprocess_interpreter.py +36 -4
  147. camel/loaders/__init__.py +17 -5
  148. camel/loaders/apify_reader.py +2 -2
  149. camel/loaders/base_io.py +2 -2
  150. camel/loaders/base_loader.py +85 -0
  151. camel/loaders/chunkr_reader.py +128 -93
  152. camel/loaders/crawl4ai_reader.py +2 -2
  153. camel/loaders/firecrawl_reader.py +6 -6
  154. camel/loaders/jina_url_reader.py +2 -2
  155. camel/loaders/markitdown.py +2 -2
  156. camel/loaders/mineru_extractor.py +2 -2
  157. camel/loaders/mistral_reader.py +148 -0
  158. camel/loaders/scrapegraph_reader.py +2 -2
  159. camel/loaders/unstructured_io.py +2 -2
  160. camel/logger.py +5 -5
  161. camel/memories/__init__.py +2 -2
  162. camel/memories/agent_memories.py +86 -3
  163. camel/memories/base.py +36 -2
  164. camel/memories/blocks/__init__.py +2 -2
  165. camel/memories/blocks/chat_history_block.py +126 -9
  166. camel/memories/blocks/vectordb_block.py +10 -3
  167. camel/memories/context_creators/__init__.py +2 -2
  168. camel/memories/context_creators/score_based.py +31 -239
  169. camel/memories/records.py +98 -13
  170. camel/messages/__init__.py +2 -2
  171. camel/messages/base.py +193 -46
  172. camel/messages/conversion/__init__.py +2 -2
  173. camel/messages/conversion/alpaca.py +2 -2
  174. camel/messages/conversion/conversation_models.py +2 -2
  175. camel/messages/conversion/sharegpt/__init__.py +2 -2
  176. camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
  177. camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
  178. camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
  179. camel/messages/func_message.py +54 -17
  180. camel/models/__init__.py +18 -2
  181. camel/models/_utils.py +3 -3
  182. camel/models/aihubmix_model.py +83 -0
  183. camel/models/aiml_model.py +11 -18
  184. camel/models/amd_model.py +101 -0
  185. camel/models/anthropic_model.py +127 -20
  186. camel/models/aws_bedrock_model.py +12 -35
  187. camel/models/azure_openai_model.py +263 -63
  188. camel/models/base_audio_model.py +5 -3
  189. camel/models/base_model.py +195 -26
  190. camel/models/cerebras_model.py +83 -0
  191. camel/models/cohere_model.py +81 -21
  192. camel/models/cometapi_model.py +83 -0
  193. camel/models/crynux_model.py +87 -0
  194. camel/models/deepseek_model.py +61 -59
  195. camel/models/fish_audio_model.py +8 -2
  196. camel/models/gemini_model.py +439 -30
  197. camel/models/groq_model.py +11 -19
  198. camel/models/internlm_model.py +11 -18
  199. camel/models/litellm_model.py +94 -34
  200. camel/models/lmstudio_model.py +17 -20
  201. camel/models/minimax_model.py +83 -0
  202. camel/models/mistral_model.py +84 -19
  203. camel/models/model_factory.py +49 -6
  204. camel/models/model_manager.py +33 -11
  205. camel/models/modelscope_model.py +13 -193
  206. camel/models/moonshot_model.py +195 -21
  207. camel/models/nebius_model.py +83 -0
  208. camel/models/nemotron_model.py +19 -9
  209. camel/models/netmind_model.py +11 -18
  210. camel/models/novita_model.py +11 -18
  211. camel/models/nvidia_model.py +11 -18
  212. camel/models/ollama_model.py +14 -21
  213. camel/models/openai_audio_models.py +2 -2
  214. camel/models/openai_compatible_model.py +234 -27
  215. camel/models/openai_model.py +255 -39
  216. camel/models/openrouter_model.py +11 -19
  217. camel/models/ppio_model.py +11 -18
  218. camel/models/qianfan_model.py +89 -0
  219. camel/models/qwen_model.py +13 -193
  220. camel/models/reka_model.py +90 -21
  221. camel/models/reward/__init__.py +2 -2
  222. camel/models/reward/base_reward_model.py +2 -2
  223. camel/models/reward/evaluator.py +2 -2
  224. camel/models/reward/nemotron_model.py +2 -2
  225. camel/models/reward/skywork_model.py +2 -2
  226. camel/models/samba_model.py +117 -49
  227. camel/models/sglang_model.py +162 -42
  228. camel/models/siliconflow_model.py +12 -35
  229. camel/models/stub_model.py +10 -7
  230. camel/models/togetherai_model.py +11 -18
  231. camel/models/vllm_model.py +10 -18
  232. camel/models/volcano_model.py +16 -20
  233. camel/models/watsonx_model.py +69 -19
  234. camel/models/yi_model.py +11 -18
  235. camel/models/zhipuai_model.py +70 -18
  236. camel/parsers/__init__.py +18 -0
  237. camel/parsers/mcp_tool_call_parser.py +176 -0
  238. camel/personas/__init__.py +2 -2
  239. camel/personas/persona.py +2 -2
  240. camel/personas/persona_hub.py +2 -2
  241. camel/prompts/__init__.py +2 -2
  242. camel/prompts/ai_society.py +2 -2
  243. camel/prompts/base.py +2 -2
  244. camel/prompts/code.py +2 -2
  245. camel/prompts/evaluation.py +2 -2
  246. camel/prompts/generate_text_embedding_data.py +2 -2
  247. camel/prompts/image_craft.py +2 -2
  248. camel/prompts/misalignment.py +2 -2
  249. camel/prompts/multi_condition_image_craft.py +2 -2
  250. camel/prompts/object_recognition.py +2 -2
  251. camel/prompts/persona_hub.py +3 -3
  252. camel/prompts/prompt_templates.py +2 -2
  253. camel/prompts/role_description_prompt_template.py +2 -2
  254. camel/prompts/solution_extraction.py +8 -8
  255. camel/prompts/task_prompt_template.py +2 -2
  256. camel/prompts/translation.py +2 -2
  257. camel/prompts/video_description_prompt.py +3 -3
  258. camel/responses/__init__.py +2 -2
  259. camel/responses/agent_responses.py +2 -2
  260. camel/retrievers/__init__.py +2 -2
  261. camel/retrievers/auto_retriever.py +23 -3
  262. camel/retrievers/base.py +2 -2
  263. camel/retrievers/bm25_retriever.py +3 -4
  264. camel/retrievers/cohere_rerank_retriever.py +2 -2
  265. camel/retrievers/hybrid_retrival.py +4 -4
  266. camel/retrievers/vector_retriever.py +2 -2
  267. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  268. camel/{runtime → runtimes}/__init__.py +2 -2
  269. camel/runtimes/api.py +153 -0
  270. camel/{runtime → runtimes}/base.py +2 -2
  271. camel/{runtime → runtimes}/configs.py +13 -13
  272. camel/{runtime → runtimes}/daytona_runtime.py +18 -19
  273. camel/{runtime → runtimes}/docker_runtime.py +13 -13
  274. camel/{runtime → runtimes}/llm_guard_runtime.py +28 -28
  275. camel/{runtime → runtimes}/remote_http_runtime.py +12 -12
  276. camel/{runtime → runtimes}/ubuntu_docker_runtime.py +3 -3
  277. camel/{runtime → runtimes}/utils/__init__.py +2 -2
  278. camel/{runtime → runtimes}/utils/function_risk_toolkit.py +2 -2
  279. camel/{runtime → runtimes}/utils/ignore_risk_toolkit.py +2 -2
  280. camel/schemas/__init__.py +2 -2
  281. camel/schemas/base.py +2 -2
  282. camel/schemas/openai_converter.py +3 -3
  283. camel/schemas/outlines_converter.py +2 -2
  284. camel/services/agent_openapi_server.py +380 -0
  285. camel/societies/__init__.py +4 -2
  286. camel/societies/babyagi_playing.py +2 -2
  287. camel/societies/role_playing.py +201 -80
  288. camel/societies/workforce/__init__.py +10 -3
  289. camel/societies/workforce/base.py +9 -5
  290. camel/societies/workforce/events.py +143 -0
  291. camel/societies/workforce/prompts.py +258 -33
  292. camel/societies/workforce/role_playing_worker.py +95 -30
  293. camel/societies/workforce/single_agent_worker.py +659 -30
  294. camel/societies/workforce/structured_output_handler.py +512 -0
  295. camel/societies/workforce/task_channel.py +182 -38
  296. camel/societies/workforce/utils.py +784 -18
  297. camel/societies/workforce/worker.py +96 -28
  298. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  299. camel/societies/workforce/workforce.py +5730 -366
  300. camel/societies/workforce/workforce_callback.py +103 -0
  301. camel/societies/workforce/workforce_logger.py +647 -0
  302. camel/societies/workforce/workforce_metrics.py +33 -0
  303. camel/storages/__init__.py +10 -2
  304. camel/storages/graph_storages/__init__.py +2 -2
  305. camel/storages/graph_storages/base.py +2 -2
  306. camel/storages/graph_storages/graph_element.py +2 -2
  307. camel/storages/graph_storages/nebula_graph.py +4 -4
  308. camel/storages/graph_storages/neo4j_graph.py +7 -7
  309. camel/storages/key_value_storages/__init__.py +2 -2
  310. camel/storages/key_value_storages/base.py +2 -2
  311. camel/storages/key_value_storages/in_memory.py +2 -2
  312. camel/storages/key_value_storages/json.py +17 -4
  313. camel/storages/key_value_storages/mem0_cloud.py +50 -49
  314. camel/storages/key_value_storages/redis.py +2 -2
  315. camel/storages/object_storages/__init__.py +2 -2
  316. camel/storages/object_storages/amazon_s3.py +2 -2
  317. camel/storages/object_storages/azure_blob.py +2 -2
  318. camel/storages/object_storages/base.py +2 -2
  319. camel/storages/object_storages/google_cloud.py +3 -3
  320. camel/storages/vectordb_storages/__init__.py +12 -2
  321. camel/storages/vectordb_storages/base.py +2 -2
  322. camel/storages/vectordb_storages/chroma.py +731 -0
  323. camel/storages/vectordb_storages/faiss.py +712 -0
  324. camel/storages/vectordb_storages/milvus.py +2 -2
  325. camel/storages/vectordb_storages/oceanbase.py +16 -17
  326. camel/storages/vectordb_storages/pgvector.py +349 -0
  327. camel/storages/vectordb_storages/qdrant.py +6 -6
  328. camel/storages/vectordb_storages/surreal.py +372 -0
  329. camel/storages/vectordb_storages/tidb.py +11 -8
  330. camel/storages/vectordb_storages/weaviate.py +714 -0
  331. camel/tasks/__init__.py +2 -2
  332. camel/tasks/task.py +366 -27
  333. camel/tasks/task_prompt.py +3 -3
  334. camel/terminators/__init__.py +2 -2
  335. camel/terminators/base.py +2 -2
  336. camel/terminators/response_terminator.py +2 -2
  337. camel/terminators/token_limit_terminator.py +2 -2
  338. camel/toolkits/__init__.py +58 -10
  339. camel/toolkits/aci_toolkit.py +66 -21
  340. camel/toolkits/arxiv_toolkit.py +8 -8
  341. camel/toolkits/ask_news_toolkit.py +2 -2
  342. camel/toolkits/async_browser_toolkit.py +174 -575
  343. camel/toolkits/audio_analysis_toolkit.py +3 -3
  344. camel/toolkits/base.py +65 -7
  345. camel/toolkits/bohrium_toolkit.py +318 -0
  346. camel/toolkits/browser_toolkit.py +306 -566
  347. camel/toolkits/browser_toolkit_commons.py +568 -0
  348. camel/toolkits/code_execution.py +67 -11
  349. camel/toolkits/context_summarizer_toolkit.py +684 -0
  350. camel/toolkits/craw4ai_toolkit.py +93 -0
  351. camel/toolkits/dappier_toolkit.py +12 -8
  352. camel/toolkits/data_commons_toolkit.py +2 -2
  353. camel/toolkits/dingtalk.py +1135 -0
  354. camel/toolkits/earth_science_toolkit.py +5367 -0
  355. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  356. camel/toolkits/excel_toolkit.py +910 -70
  357. camel/toolkits/file_toolkit.py +1402 -0
  358. camel/toolkits/function_tool.py +128 -20
  359. camel/toolkits/github_toolkit.py +148 -43
  360. camel/toolkits/gmail_toolkit.py +1839 -0
  361. camel/toolkits/google_calendar_toolkit.py +40 -6
  362. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  363. camel/toolkits/google_maps_toolkit.py +2 -2
  364. camel/toolkits/google_scholar_toolkit.py +2 -2
  365. camel/toolkits/human_toolkit.py +36 -12
  366. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  367. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  368. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  369. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  370. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  371. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
  372. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  373. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  374. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1929 -0
  375. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  376. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
  377. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  378. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  379. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  380. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  381. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
  382. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
  383. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  384. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
  385. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  386. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  387. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  388. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  389. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  390. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  391. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  392. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  393. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  394. camel/toolkits/image_analysis_toolkit.py +3 -3
  395. camel/toolkits/image_generation_toolkit.py +390 -0
  396. camel/toolkits/jina_reranker_toolkit.py +195 -79
  397. camel/toolkits/klavis_toolkit.py +7 -3
  398. camel/toolkits/linkedin_toolkit.py +2 -2
  399. camel/toolkits/markitdown_toolkit.py +104 -0
  400. camel/toolkits/math_toolkit.py +66 -12
  401. camel/toolkits/mcp_toolkit.py +841 -600
  402. camel/toolkits/memory_toolkit.py +7 -3
  403. camel/toolkits/meshy_toolkit.py +2 -2
  404. camel/toolkits/message_agent_toolkit.py +608 -0
  405. camel/toolkits/message_integration.py +724 -0
  406. camel/toolkits/mineru_toolkit.py +2 -2
  407. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  408. camel/toolkits/networkx_toolkit.py +2 -2
  409. camel/toolkits/note_taking_toolkit.py +277 -0
  410. camel/toolkits/notion_mcp_toolkit.py +224 -0
  411. camel/toolkits/notion_toolkit.py +2 -2
  412. camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
  413. camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
  414. camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
  415. camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
  416. camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
  417. camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
  418. camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
  419. camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
  420. camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
  421. camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
  422. camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
  423. camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
  424. camel/toolkits/open_api_specs/security_config.py +2 -2
  425. camel/toolkits/open_api_specs/speak/__init__.py +2 -2
  426. camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
  427. camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
  428. camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
  429. camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
  430. camel/toolkits/open_api_toolkit.py +2 -2
  431. camel/toolkits/openbb_toolkit.py +7 -3
  432. camel/toolkits/origene_mcp_toolkit.py +56 -0
  433. camel/toolkits/page_script.js +86 -74
  434. camel/toolkits/playwright_mcp_toolkit.py +27 -32
  435. camel/toolkits/pptx_toolkit.py +790 -0
  436. camel/toolkits/pubmed_toolkit.py +2 -2
  437. camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
  438. camel/toolkits/pyautogui_toolkit.py +2 -2
  439. camel/toolkits/reddit_toolkit.py +2 -2
  440. camel/toolkits/resend_toolkit.py +168 -0
  441. camel/toolkits/retrieval_toolkit.py +2 -2
  442. camel/toolkits/screenshot_toolkit.py +213 -0
  443. camel/toolkits/search_toolkit.py +539 -146
  444. camel/toolkits/searxng_toolkit.py +2 -2
  445. camel/toolkits/semantic_scholar_toolkit.py +2 -2
  446. camel/toolkits/slack_toolkit.py +108 -58
  447. camel/toolkits/sql_toolkit.py +712 -0
  448. camel/toolkits/stripe_toolkit.py +2 -2
  449. camel/toolkits/sympy_toolkit.py +3 -3
  450. camel/toolkits/task_planning_toolkit.py +134 -0
  451. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  452. camel/toolkits/terminal_toolkit/terminal_toolkit.py +1070 -0
  453. camel/toolkits/terminal_toolkit/utils.py +532 -0
  454. camel/toolkits/thinking_toolkit.py +3 -3
  455. camel/toolkits/twitter_toolkit.py +8 -3
  456. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  457. camel/toolkits/video_analysis_toolkit.py +112 -29
  458. camel/toolkits/video_download_toolkit.py +22 -16
  459. camel/toolkits/weather_toolkit.py +2 -2
  460. camel/toolkits/web_deploy_toolkit.py +1219 -0
  461. camel/toolkits/wechat_official_toolkit.py +483 -0
  462. camel/toolkits/whatsapp_toolkit.py +2 -2
  463. camel/toolkits/wolfram_alpha_toolkit.py +53 -25
  464. camel/toolkits/zapier_toolkit.py +7 -3
  465. camel/types/__init__.py +4 -4
  466. camel/types/agents/__init__.py +2 -2
  467. camel/types/agents/tool_calling_record.py +6 -3
  468. camel/types/enums.py +454 -35
  469. camel/types/mcp_registries.py +2 -2
  470. camel/types/openai_types.py +4 -4
  471. camel/types/unified_model_type.py +43 -6
  472. camel/utils/__init__.py +20 -2
  473. camel/utils/async_func.py +2 -2
  474. camel/utils/chunker/__init__.py +2 -2
  475. camel/utils/chunker/base.py +2 -2
  476. camel/utils/chunker/code_chunker.py +2 -2
  477. camel/utils/chunker/uio_chunker.py +2 -2
  478. camel/utils/commons.py +65 -7
  479. camel/utils/constants.py +5 -2
  480. camel/utils/context_utils.py +1134 -0
  481. camel/utils/deduplication.py +2 -2
  482. camel/utils/filename.py +2 -2
  483. camel/utils/langfuse.py +258 -0
  484. camel/utils/mcp.py +140 -6
  485. camel/utils/mcp_client.py +1056 -0
  486. camel/utils/message_summarizer.py +148 -0
  487. camel/utils/response_format.py +2 -2
  488. camel/utils/token_counting.py +45 -22
  489. camel/utils/tool_result.py +44 -0
  490. camel/verifiers/__init__.py +2 -2
  491. camel/verifiers/base.py +2 -2
  492. camel/verifiers/math_verifier.py +2 -2
  493. camel/verifiers/models.py +2 -2
  494. camel/verifiers/physics_verifier.py +2 -2
  495. camel/verifiers/python_verifier.py +2 -2
  496. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/METADATA +349 -108
  497. camel_ai-0.2.82.dist-info/RECORD +507 -0
  498. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
  499. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
  500. camel/loaders/pandas_reader.py +0 -368
  501. camel/runtime/api.py +0 -97
  502. camel/toolkits/dalle_toolkit.py +0 -171
  503. camel/toolkits/file_write_toolkit.py +0 -395
  504. camel/toolkits/openai_agent_toolkit.py +0 -135
  505. camel/toolkits/terminal_toolkit.py +0 -1037
  506. camel_ai-0.2.59.dist-info/RECORD +0 -410
@@ -1,4 +1,4 @@
1
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
1
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
2
2
  # Licensed under the Apache License, Version 2.0 (the "License");
3
3
  # you may not use this file except in compliance with the License.
4
4
  # You may obtain a copy of the License at
@@ -10,15 +10,23 @@
10
10
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
13
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  import os
15
+ import warnings
15
16
  from typing import Any, Dict, List, Literal, Optional, TypeAlias, Union, cast
16
17
 
17
18
  import requests
18
19
 
20
+ from camel.logger import get_logger
19
21
  from camel.toolkits.base import BaseToolkit
20
22
  from camel.toolkits.function_tool import FunctionTool
21
- from camel.utils import MCPServer, api_keys_required, dependencies_required
23
+ from camel.utils import (
24
+ MCPServer,
25
+ api_keys_required,
26
+ dependencies_required,
27
+ )
28
+
29
+ logger = get_logger(__name__)
22
30
 
23
31
 
24
32
  @MCPServer()
@@ -29,6 +37,24 @@ class SearchToolkit(BaseToolkit):
29
37
  search engines like Google, DuckDuckGo, Wikipedia and Wolfram Alpha, Brave.
30
38
  """
31
39
 
40
+ def __init__(
41
+ self,
42
+ timeout: Optional[float] = None,
43
+ exclude_domains: Optional[List[str]] = None,
44
+ ):
45
+ r"""Initializes the SearchToolkit.
46
+
47
+ Args:
48
+ timeout (float): Timeout for API requests in seconds.
49
+ (default: :obj:`None`)
50
+ exclude_domains (Optional[List[str]]): List of domains to
51
+ exclude from search results. Currently only supported
52
+ by the `search_google` function.
53
+ (default: :obj:`None`)
54
+ """
55
+ super().__init__(timeout=timeout)
56
+ self.exclude_domains = exclude_domains
57
+
32
58
  @dependencies_required("wikipedia")
33
59
  def search_wiki(self, entity: str) -> str:
34
60
  r"""Search the entity in WikiPedia and return the summary of the
@@ -86,8 +112,8 @@ class SearchToolkit(BaseToolkit):
86
112
  depth (Literal["standard", "deep"]): The depth of the search.
87
113
  "standard" for a straightforward search, "deep" for a more
88
114
  comprehensive search.
89
- output_type (Literal["searchResults", "sourcedAnswer",
90
- "structured"]): The type of output:
115
+ output_type (Literal["searchResults", "sourcedAnswer", "structured"]):
116
+ The type of output:
91
117
  - "searchResults" for raw search results,
92
118
  - "sourcedAnswer" for an answer with supporting sources,
93
119
  - "structured" for output based on a provided schema.
@@ -139,9 +165,12 @@ class SearchToolkit(BaseToolkit):
139
165
  except Exception as e:
140
166
  return {"error": f"An unexpected error occurred: {e!s}"}
141
167
 
142
- @dependencies_required("duckduckgo_search")
168
+ @dependencies_required("ddgs")
143
169
  def search_duckduckgo(
144
- self, query: str, source: str = "text", max_results: int = 5
170
+ self,
171
+ query: str,
172
+ source: str = "text",
173
+ number_of_result_pages: int = 10,
145
174
  ) -> List[Dict[str, Any]]:
146
175
  r"""Use DuckDuckGo search engine to search information for
147
176
  the given query.
@@ -154,76 +183,79 @@ class SearchToolkit(BaseToolkit):
154
183
  query (str): The query to be searched.
155
184
  source (str): The type of information to query (e.g., "text",
156
185
  "images", "videos"). Defaults to "text".
157
- max_results (int): Max number of results, defaults to `5`.
186
+ number_of_result_pages (int): The number of result pages to
187
+ retrieve. Adjust this based on your task - use fewer results
188
+ for focused searches and more for comprehensive searches.
189
+ (default: :obj:`10`)
158
190
 
159
191
  Returns:
160
192
  List[Dict[str, Any]]: A list of dictionaries where each dictionary
161
193
  represents a search result.
162
194
  """
163
- from duckduckgo_search import DDGS
164
- from requests.exceptions import RequestException
195
+ from ddgs import DDGS
165
196
 
166
197
  ddgs = DDGS()
167
198
  responses: List[Dict[str, Any]] = []
168
199
 
169
200
  if source == "text":
170
201
  try:
171
- results = ddgs.text(keywords=query, max_results=max_results)
172
- except RequestException as e:
202
+ results = ddgs.text(query, max_results=number_of_result_pages)
203
+ # Iterate over results found
204
+ for i, result in enumerate(results, start=1):
205
+ # Creating a response object with a similar structure
206
+ response = {
207
+ "result_id": i,
208
+ "title": result["title"],
209
+ "description": result["body"],
210
+ "url": result["href"],
211
+ }
212
+ responses.append(response)
213
+ except Exception as e:
173
214
  # Handle specific exceptions or general request exceptions
174
215
  responses.append({"error": f"duckduckgo search failed.{e}"})
175
216
 
176
- # Iterate over results found
177
- for i, result in enumerate(results, start=1):
178
- # Creating a response object with a similar structure
179
- response = {
180
- "result_id": i,
181
- "title": result["title"],
182
- "description": result["body"],
183
- "url": result["href"],
184
- }
185
- responses.append(response)
186
-
187
217
  elif source == "images":
188
218
  try:
189
- results = ddgs.images(keywords=query, max_results=max_results)
190
- except RequestException as e:
219
+ results = ddgs.images(
220
+ query, max_results=number_of_result_pages
221
+ )
222
+ # Iterate over results found
223
+ for i, result in enumerate(results, start=1):
224
+ # Creating a response object with a similar structure
225
+ response = {
226
+ "result_id": i,
227
+ "title": result["title"],
228
+ "image": result["image"],
229
+ "url": result["url"],
230
+ "source": result["source"],
231
+ }
232
+ responses.append(response)
233
+ except Exception as e:
191
234
  # Handle specific exceptions or general request exceptions
192
235
  responses.append({"error": f"duckduckgo search failed.{e}"})
193
236
 
194
- # Iterate over results found
195
- for i, result in enumerate(results, start=1):
196
- # Creating a response object with a similar structure
197
- response = {
198
- "result_id": i,
199
- "title": result["title"],
200
- "image": result["image"],
201
- "url": result["url"],
202
- "source": result["source"],
203
- }
204
- responses.append(response)
205
-
206
237
  elif source == "videos":
207
238
  try:
208
- results = ddgs.videos(keywords=query, max_results=max_results)
209
- except RequestException as e:
239
+ results = ddgs.videos(
240
+ query, max_results=number_of_result_pages
241
+ )
242
+ # Iterate over results found
243
+ for i, result in enumerate(results, start=1):
244
+ # Creating a response object with a similar structure
245
+ response = {
246
+ "result_id": i,
247
+ "title": result["title"],
248
+ "description": result["description"],
249
+ "embed_url": result["embed_url"],
250
+ "publisher": result["publisher"],
251
+ "duration": result["duration"],
252
+ "published": result["published"],
253
+ }
254
+ responses.append(response)
255
+ except Exception as e:
210
256
  # Handle specific exceptions or general request exceptions
211
257
  responses.append({"error": f"duckduckgo search failed.{e}"})
212
258
 
213
- # Iterate over results found
214
- for i, result in enumerate(results, start=1):
215
- # Creating a response object with a similar structure
216
- response = {
217
- "result_id": i,
218
- "title": result["title"],
219
- "description": result["description"],
220
- "embed_url": result["embed_url"],
221
- "publisher": result["publisher"],
222
- "duration": result["duration"],
223
- "published": result["published"],
224
- }
225
- responses.append(response)
226
-
227
259
  # If no answer found, return an empty list
228
260
  return responses
229
261
 
@@ -238,7 +270,6 @@ class SearchToolkit(BaseToolkit):
238
270
  country: str = "US",
239
271
  search_lang: str = "en",
240
272
  ui_lang: str = "en-US",
241
- count: int = 20,
242
273
  offset: int = 0,
243
274
  safesearch: str = "moderate",
244
275
  freshness: Optional[str] = None,
@@ -249,6 +280,7 @@ class SearchToolkit(BaseToolkit):
249
280
  units: Optional[str] = None,
250
281
  extra_snippets: Optional[bool] = None,
251
282
  summary: Optional[bool] = None,
283
+ number_of_result_pages: int = 10,
252
284
  ) -> Dict[str, Any]:
253
285
  r"""This function queries the Brave search engine API and returns a
254
286
  dictionary, representing a search result.
@@ -262,17 +294,18 @@ class SearchToolkit(BaseToolkit):
262
294
  The country string is limited to 2 character country codes of
263
295
  supported countries. For a list of supported values, see
264
296
  Country Codes. (default: :obj:`US `)
265
- search_lang (str): The search language preference. The 2 or more
266
- character language code for which search results are provided.
267
- For a list of possible values, see Language Codes.
297
+ search_lang (str): The search language preference.
298
+ Use ONLY these exact values, NOT standard ISO codes:
299
+ 'ar', 'eu', 'bn', 'bg', 'ca', 'zh-hans', 'zh-hant', 'hr',
300
+ 'cs', 'da', 'nl', 'en', 'en-gb', 'et', 'fi', 'fr', 'gl', 'de',
301
+ 'gu', 'he', 'hi', 'hu', 'is', 'it', 'jp', 'kn', 'ko', 'lv',
302
+ 'lt', 'ms', 'ml', 'mr', 'nb', 'pl', 'pt-br', 'pt-pt', 'pa',
303
+ 'ro', 'ru', 'sr', 'sk', 'sl', 'es', 'sv', 'ta', 'te', 'th',
304
+ 'tr', 'uk', 'vi'.
268
305
  ui_lang (str): User interface language preferred in response.
269
- Usually of the format '<language_code>-<country_code>'. For
270
- more, see RFC 9110. For a list of supported values, see UI
271
- Language Codes.
272
- count (int): The number of search results returned in response.
273
- The maximum is 20. The actual number delivered may be less than
274
- requested. Combine this parameter with offset to paginate
275
- search results.
306
+ Format: '<language_code>-<country_code>'. Common examples:
307
+ 'en-US', 'en-GB', 'jp-JP', 'zh-hans-CN', 'zh-hant-TW',
308
+ 'de-DE', 'fr-FR', 'es-ES', 'pt-BR', 'ru-RU', 'ko-KR'.
276
309
  offset (int): The zero based offset that indicates number of search
277
310
  results per page (count) to skip before returning the result.
278
311
  The maximum is 9. The actual number delivered may be less than
@@ -334,6 +367,10 @@ class SearchToolkit(BaseToolkit):
334
367
  summary (Optional[bool]): This parameter enables summary key
335
368
  generation in web search results. This is required for
336
369
  summarizer to be enabled.
370
+ number_of_result_pages (int): The number of result pages to
371
+ retrieve. Adjust this based on your task - use fewer results
372
+ for focused searches and more for comprehensive searches.
373
+ (default: :obj:`10`)
337
374
 
338
375
  Returns:
339
376
  Dict[str, Any]: A dictionary representing a search result.
@@ -360,7 +397,7 @@ class SearchToolkit(BaseToolkit):
360
397
  "country": country,
361
398
  "search_lang": search_lang,
362
399
  "ui_lang": ui_lang,
363
- "count": count,
400
+ "count": number_of_result_pages,
364
401
  "offset": offset,
365
402
  "safesearch": safesearch,
366
403
  "freshness": freshness,
@@ -372,10 +409,36 @@ class SearchToolkit(BaseToolkit):
372
409
  "extra_snippets": extra_snippets,
373
410
  "summary": summary,
374
411
  }
412
+ params = {k: v for k, v in params.items() if v is not None}
375
413
 
376
414
  response = requests.get(url, headers=headers, params=params)
377
- data = response.json()["web"]
378
- return data
415
+ try:
416
+ response.raise_for_status()
417
+ except requests.HTTPError as e:
418
+ raise RuntimeError(
419
+ f"Brave API HTTP error: {e}, body={response.text!r}"
420
+ )
421
+
422
+ json_data = response.json()
423
+ # Check if response has search results
424
+ content_keys = [
425
+ 'web',
426
+ 'news',
427
+ 'videos',
428
+ 'images',
429
+ 'locations',
430
+ 'discussions',
431
+ 'faq',
432
+ 'infobox',
433
+ ]
434
+ has_results = any(key in json_data for key in content_keys)
435
+
436
+ if not has_results:
437
+ # Return empty results structure if no content found
438
+ json_data['web'] = {'results': []}
439
+ json_data['message'] = 'No search results found for the query'
440
+
441
+ return json_data
379
442
 
380
443
  @api_keys_required(
381
444
  [
@@ -384,25 +447,53 @@ class SearchToolkit(BaseToolkit):
384
447
  ]
385
448
  )
386
449
  def search_google(
387
- self, query: str, num_result_pages: int = 5
450
+ self,
451
+ query: str,
452
+ search_type: str = "web",
453
+ number_of_result_pages: int = 10,
454
+ start_page: int = 1,
388
455
  ) -> List[Dict[str, Any]]:
389
456
  r"""Use Google search engine to search information for the given query.
390
457
 
391
458
  Args:
392
459
  query (str): The query to be searched.
393
- num_result_pages (int): The number of result pages to retrieve.
460
+ search_type (str): The type of search to perform. Must be either
461
+ "web" for web pages or "image" for image search. Any other
462
+ value will raise a ValueError. (default: "web")
463
+ number_of_result_pages (int): The number of result pages to
464
+ retrieve. Must be a positive integer between 1 and 10.
465
+ Google Custom Search API limits results to 10 per request.
466
+ If a value greater than 10 is provided, it will be capped
467
+ at 10 with a warning. Adjust this based on your task - use
468
+ fewer results for focused searches and more for comprehensive
469
+ searches. (default: :obj:`10`)
470
+ start_page (int): The result page to start from. Must be a
471
+ positive integer (>= 1). Use this for pagination - e.g.,
472
+ start_page=1 for results 1-10, start_page=11 for results
473
+ 11-20, etc. This allows agents to check initial results
474
+ and continue searching if needed. (default: :obj:`1`)
394
475
 
395
476
  Returns:
396
477
  List[Dict[str, Any]]: A list of dictionaries where each dictionary
397
- represents a website.
398
- Each dictionary contains the following keys:
478
+ represents a search result.
479
+
480
+ For web search, each dictionary contains:
399
481
  - 'result_id': A number in order.
400
482
  - 'title': The title of the website.
401
483
  - 'description': A brief description of the website.
402
484
  - 'long_description': More detail of the website.
403
485
  - 'url': The URL of the website.
404
486
 
405
- Example:
487
+ For image search, each dictionary contains:
488
+ - 'result_id': A number in order.
489
+ - 'title': The title of the image.
490
+ - 'image_url': The URL of the image.
491
+ - 'display_link': The website hosting the image.
492
+ - 'context_url': The URL of the page containing the image.
493
+ - 'width': Image width in pixels (if available).
494
+ - 'height': Image height in pixels (if available).
495
+
496
+ Example web result:
406
497
  {
407
498
  'result_id': 1,
408
499
  'title': 'OpenAI',
@@ -414,29 +505,80 @@ class SearchToolkit(BaseToolkit):
414
505
  benefit humanity as a whole',
415
506
  'url': 'https://www.openai.com'
416
507
  }
417
- title, description, url of a website.
508
+
509
+ Example image result:
510
+ {
511
+ 'result_id': 1,
512
+ 'title': 'Beautiful Sunset',
513
+ 'image_url': 'https://example.com/image.jpg',
514
+ 'display_link': 'example.com',
515
+ 'context_url': 'https://example.com/page.html',
516
+ 'width': 800,
517
+ 'height': 600
518
+ }
418
519
  """
520
+ from urllib.parse import quote
521
+
419
522
  import requests
420
523
 
524
+ # Validate input parameters
525
+ if not isinstance(start_page, int) or start_page < 1:
526
+ raise ValueError("start_page must be a positive integer")
527
+
528
+ if (
529
+ not isinstance(number_of_result_pages, int)
530
+ or number_of_result_pages < 1
531
+ ):
532
+ raise ValueError(
533
+ "number_of_result_pages must be a positive integer"
534
+ )
535
+
536
+ # Google Custom Search API has a limit of 10 results per request
537
+ if number_of_result_pages > 10:
538
+ logger.warning(
539
+ f"Google API limits results to 10 per request. "
540
+ f"Requested {number_of_result_pages}, using 10 instead."
541
+ )
542
+ number_of_result_pages = 10
543
+
544
+ if search_type not in ["web", "image"]:
545
+ raise ValueError("search_type must be either 'web' or 'image'")
546
+
421
547
  # https://developers.google.com/custom-search/v1/overview
422
548
  GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
423
549
  # https://cse.google.com/cse/all
424
550
  SEARCH_ENGINE_ID = os.getenv("SEARCH_ENGINE_ID")
425
551
 
426
- # Using the first page
427
- start_page_idx = 1
552
+ # Using the specified start page
553
+ start_page_idx = start_page
428
554
  # Different language may get different result
429
555
  search_language = "en"
430
- # How many pages to return
431
- num_result_pages = num_result_pages
556
+
557
+ modified_query = query
558
+ if self.exclude_domains:
559
+ # Use Google's -site: operator to exclude domains
560
+ exclusion_terms = " ".join(
561
+ [f"-site:{domain}" for domain in self.exclude_domains]
562
+ )
563
+ modified_query = f"{query} {exclusion_terms}"
564
+ logger.debug(f"Excluded domains, modified query: {modified_query}")
565
+
566
+ encoded_query = quote(modified_query)
567
+
432
568
  # Constructing the URL
433
569
  # Doc: https://developers.google.com/custom-search/v1/using_rest
434
- url = (
570
+ base_url = (
435
571
  f"https://www.googleapis.com/customsearch/v1?"
436
- f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={query}&start="
437
- f"{start_page_idx}&lr={search_language}&num={num_result_pages}"
572
+ f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={encoded_query}&start="
573
+ f"{start_page_idx}&lr={search_language}&num={number_of_result_pages}"
438
574
  )
439
575
 
576
+ # Add searchType parameter for image search
577
+ if search_type == "image":
578
+ url = base_url + "&searchType=image"
579
+ else:
580
+ url = base_url
581
+
440
582
  responses = []
441
583
  # Fetch the results given the URL
442
584
  try:
@@ -448,55 +590,109 @@ class SearchToolkit(BaseToolkit):
448
590
  if "items" in data:
449
591
  search_items = data.get("items")
450
592
 
451
- # Iterate over 10 results found
593
+ # Iterate over results found
452
594
  for i, search_item in enumerate(search_items, start=1):
453
- # Check metatags are present
454
- if "pagemap" not in search_item:
455
- continue
456
- if "metatags" not in search_item["pagemap"]:
457
- continue
458
- if (
459
- "og:description"
460
- in search_item["pagemap"]["metatags"][0]
461
- ):
462
- long_description = search_item["pagemap"]["metatags"][
463
- 0
464
- ]["og:description"]
595
+ if search_type == "image":
596
+ # Process image search results
597
+ title = search_item.get("title")
598
+ image_url = search_item.get("link")
599
+ display_link = search_item.get("displayLink")
600
+
601
+ # Get context URL (page containing the image)
602
+ image_info = search_item.get("image", {})
603
+ context_url = image_info.get("contextLink", "")
604
+
605
+ # Get image dimensions if available
606
+ width = image_info.get("width")
607
+ height = image_info.get("height")
608
+
609
+ response = {
610
+ "result_id": i,
611
+ "title": title,
612
+ "image_url": image_url,
613
+ "display_link": display_link,
614
+ "context_url": context_url,
615
+ }
616
+
617
+ if width:
618
+ response["width"] = int(width)
619
+ if height:
620
+ response["height"] = int(height)
621
+
622
+ responses.append(response)
465
623
  else:
466
- long_description = "N/A"
467
- # Get the page title
468
- title = search_item.get("title")
469
- # Page snippet
470
- snippet = search_item.get("snippet")
471
-
472
- # Extract the page url
473
- link = search_item.get("link")
474
- response = {
475
- "result_id": i,
476
- "title": title,
477
- "description": snippet,
478
- "long_description": long_description,
479
- "url": link,
480
- }
481
- responses.append(response)
624
+ if "pagemap" not in search_item:
625
+ continue
626
+ if "metatags" not in search_item["pagemap"]:
627
+ continue
628
+ if (
629
+ "og:description"
630
+ in search_item["pagemap"]["metatags"][0]
631
+ ):
632
+ long_description = search_item["pagemap"][
633
+ "metatags"
634
+ ][0]["og:description"]
635
+ else:
636
+ long_description = "N/A"
637
+ title = search_item.get("title")
638
+ snippet = search_item.get("snippet")
639
+
640
+ link = search_item.get("link")
641
+ response = {
642
+ "result_id": i,
643
+ "title": title,
644
+ "description": snippet,
645
+ "long_description": long_description,
646
+ "url": link,
647
+ }
648
+ responses.append(response)
482
649
  else:
483
- responses.append({"error": "google search failed."})
650
+ if "error" in data:
651
+ error_info = data.get("error", {})
652
+ logger.error(
653
+ f"Google search failed - API response: {error_info}"
654
+ )
655
+ responses.append(
656
+ {
657
+ "error": f"Google search failed - "
658
+ f"API response: {error_info}"
659
+ }
660
+ )
661
+ elif "searchInformation" in data:
662
+ search_info = data.get("searchInformation", {})
663
+ total_results = search_info.get("totalResults", "0")
664
+ if total_results == "0":
665
+ logger.info(f"No results found for query: {query}")
666
+ # Return empty list to indicate no results (not an error)
667
+ responses = []
668
+ else:
669
+ logger.warning(
670
+ f"Google search returned no items but claims {total_results} results"
671
+ )
672
+ responses = []
673
+ else:
674
+ logger.error(
675
+ f"Unexpected Google API response format: {data}"
676
+ )
677
+ responses.append(
678
+ {"error": "Unexpected response format from Google API"}
679
+ )
484
680
 
485
- except requests.RequestException:
486
- # Handle specific exceptions or general request exceptions
487
- responses.append({"error": "google search failed."})
488
- # If no answer found, return an empty list
681
+ except Exception as e:
682
+ responses.append({"error": f"google search failed: {e!s}"})
489
683
  return responses
490
684
 
491
- def tavily_search(
492
- self, query: str, num_results: int = 5, **kwargs
685
+ def search_tavily(
686
+ self, query: str, number_of_result_pages: int = 10, **kwargs
493
687
  ) -> List[Dict[str, Any]]:
494
688
  r"""Use Tavily Search API to search information for the given query.
495
689
 
496
690
  Args:
497
691
  query (str): The query to be searched.
498
- num_results (int): The number of search results to retrieve
499
- (default is `5`).
692
+ number_of_result_pages (int): The number of result pages to
693
+ retrieve. Adjust this based on your task - use fewer results
694
+ for focused searches and more for comprehensive searches.
695
+ (default: :obj:`10`)
500
696
  **kwargs: Additional optional parameters supported by Tavily's API:
501
697
  - search_depth (str): "basic" or "advanced" search depth.
502
698
  - topic (str): The search category, e.g., "general" or "news."
@@ -532,7 +728,9 @@ class SearchToolkit(BaseToolkit):
532
728
  client = TavilyClient(Tavily_API_KEY)
533
729
 
534
730
  try:
535
- results = client.search(query, max_results=num_results, **kwargs)
731
+ results = client.search(
732
+ query, max_results=number_of_result_pages, **kwargs
733
+ )
536
734
  return results
537
735
  except Exception as e:
538
736
  return [{"error": f"An unexpected error occurred: {e!s}"}]
@@ -543,8 +741,8 @@ class SearchToolkit(BaseToolkit):
543
741
  query: str,
544
742
  freshness: str = "noLimit",
545
743
  summary: bool = False,
546
- count: int = 10,
547
744
  page: int = 1,
745
+ number_of_result_pages: int = 10,
548
746
  ) -> Dict[str, Any]:
549
747
  r"""Query the Bocha AI search API and return search results.
550
748
 
@@ -559,8 +757,11 @@ class SearchToolkit(BaseToolkit):
559
757
  - 'oneYear': past year.
560
758
  summary (bool): Whether to include text summaries in results.
561
759
  Default is False.
562
- count (int): Number of results to return (1-50). Default is 10.
563
760
  page (int): Page number of results. Default is 1.
761
+ number_of_result_pages (int): The number of result pages to
762
+ retrieve. Adjust this based on your task - use fewer results
763
+ for focused searches and more for comprehensive searches.
764
+ (default: :obj:`10`)
564
765
 
565
766
  Returns:
566
767
  Dict[str, Any]: A dictionary containing search results, including
@@ -582,7 +783,7 @@ class SearchToolkit(BaseToolkit):
582
783
  "query": query,
583
784
  "freshness": freshness,
584
785
  "summary": summary,
585
- "count": count,
786
+ "count": number_of_result_pages,
586
787
  "page": page,
587
788
  },
588
789
  ensure_ascii=False,
@@ -600,15 +801,19 @@ class SearchToolkit(BaseToolkit):
600
801
  except requests.exceptions.RequestException as e:
601
802
  return {"error": f"Bocha AI search failed: {e!s}"}
602
803
 
603
- def search_baidu(self, query: str, max_results: int = 5) -> Dict[str, Any]:
804
+ def search_baidu(
805
+ self, query: str, number_of_result_pages: int = 10
806
+ ) -> Dict[str, Any]:
604
807
  r"""Search Baidu using web scraping to retrieve relevant search
605
808
  results. This method queries Baidu's search engine and extracts search
606
809
  results including titles, descriptions, and URLs.
607
810
 
608
811
  Args:
609
812
  query (str): Search query string to submit to Baidu.
610
- max_results (int): Maximum number of results to return.
611
- (default: :obj:`5`)
813
+ number_of_result_pages (int): The number of result pages to
814
+ retrieve. Adjust this based on your task - use fewer results
815
+ for focused searches and more for comprehensive searches.
816
+ (default: :obj:`10`)
612
817
 
613
818
  Returns:
614
819
  Dict[str, Any]: A dictionary containing search results or error
@@ -626,7 +831,7 @@ class SearchToolkit(BaseToolkit):
626
831
  ),
627
832
  "Referer": "https://www.baidu.com",
628
833
  }
629
- params = {"wd": query, "rn": str(max_results)}
834
+ params = {"wd": query, "rn": str(number_of_result_pages)}
630
835
 
631
836
  response = requests.get(url, headers=headers, params=params)
632
837
  response.encoding = "utf-8"
@@ -655,7 +860,7 @@ class SearchToolkit(BaseToolkit):
655
860
  "url": link,
656
861
  }
657
862
  )
658
- if len(results) >= max_results:
863
+ if len(results) >= number_of_result_pages:
659
864
  break
660
865
 
661
866
  if not results:
@@ -669,7 +874,9 @@ class SearchToolkit(BaseToolkit):
669
874
  except Exception as e:
670
875
  return {"error": f"Baidu scraping error: {e!s}"}
671
876
 
672
- def search_bing(self, query: str, max_results: int = 5) -> Dict[str, Any]:
877
+ def search_bing(
878
+ self, query: str, number_of_result_pages: int = 10
879
+ ) -> Dict[str, Any]:
673
880
  r"""Use Bing search engine to search information for the given query.
674
881
 
675
882
  This function queries the Chinese version of Bing search engine (cn.
@@ -681,8 +888,10 @@ class SearchToolkit(BaseToolkit):
681
888
  Args:
682
889
  query (str): The search query string to submit to Bing. Works best
683
890
  with Chinese queries or when Chinese results are preferred.
684
- max_results (int): Maximum number of results to return.
685
- (default: :obj:`5`)
891
+ number_of_result_pages (int): The number of result pages to
892
+ retrieve. Adjust this based on your task - use fewer results
893
+ for focused searches and more for comprehensive searches.
894
+ (default: :obj:`10`)
686
895
 
687
896
  Returns:
688
897
  Dict ([str, Any]): A dictionary containing either:
@@ -732,7 +941,7 @@ class SearchToolkit(BaseToolkit):
732
941
  result_items = b_results_tag.find_all("li")
733
942
 
734
943
  results: List[Dict[str, Any]] = []
735
- for i in range(min(len(result_items), max_results)):
944
+ for i in range(min(len(result_items), number_of_result_pages)):
736
945
  row = result_items[i]
737
946
  if not isinstance(row, Tag):
738
947
  continue
@@ -797,11 +1006,11 @@ class SearchToolkit(BaseToolkit):
797
1006
  "financial report",
798
1007
  ]
799
1008
  ] = None,
800
- num_results: int = 10,
801
1009
  include_text: Optional[List[str]] = None,
802
1010
  exclude_text: Optional[List[str]] = None,
803
1011
  use_autoprompt: bool = True,
804
1012
  text: bool = False,
1013
+ number_of_result_pages: int = 10,
805
1014
  ) -> Dict[str, Any]:
806
1015
  r"""Use Exa search API to perform intelligent web search with optional
807
1016
  content extraction.
@@ -813,8 +1022,6 @@ class SearchToolkit(BaseToolkit):
813
1022
  and neural search. (default: :obj:`"auto"`)
814
1023
  category (Optional[Literal]): Category to focus the search on, such
815
1024
  as "research paper" or "news". (default: :obj:`None`)
816
- num_results (int): Number of results to return (max 100).
817
- (default: :obj:`10`)
818
1025
  include_text (Optional[List[str]]): Strings that must be present in
819
1026
  webpage text. Limited to 1 string of up to 5 words.
820
1027
  (default: :obj:`None`)
@@ -825,6 +1032,10 @@ class SearchToolkit(BaseToolkit):
825
1032
  enhance the query. (default: :obj:`True`)
826
1033
  text (bool): Whether to include webpage contents in results.
827
1034
  (default: :obj:`False`)
1035
+ number_of_result_pages (int): The number of result pages to
1036
+ retrieve. Must be between 1 and 100. Adjust this based on
1037
+ your task - use fewer results for focused searches and more
1038
+ for comprehensive searches. (default: :obj:`10`)
828
1039
 
829
1040
  Returns:
830
1041
  Dict[str, Any]: A dict containing search results and metadata:
@@ -843,7 +1054,10 @@ class SearchToolkit(BaseToolkit):
843
1054
  try:
844
1055
  exa = Exa(EXA_API_KEY)
845
1056
 
846
- if num_results is not None and not 0 < num_results <= 100:
1057
+ if (
1058
+ number_of_result_pages is not None
1059
+ and not 0 < number_of_result_pages <= 100
1060
+ ):
847
1061
  raise ValueError("num_results must be between 1 and 100")
848
1062
 
849
1063
  if include_text is not None:
@@ -870,7 +1084,7 @@ class SearchToolkit(BaseToolkit):
870
1084
  query=query,
871
1085
  type=search_type,
872
1086
  category=category,
873
- num_results=num_results,
1087
+ num_results=number_of_result_pages,
874
1088
  include_text=include_text,
875
1089
  exclude_text=exclude_text,
876
1090
  use_autoprompt=use_autoprompt,
@@ -884,7 +1098,7 @@ class SearchToolkit(BaseToolkit):
884
1098
  query=query,
885
1099
  type=search_type,
886
1100
  category=category,
887
- num_results=num_results,
1101
+ num_results=number_of_result_pages,
888
1102
  include_text=include_text,
889
1103
  exclude_text=exclude_text,
890
1104
  use_autoprompt=use_autoprompt,
@@ -914,10 +1128,10 @@ class SearchToolkit(BaseToolkit):
914
1128
  "news_center",
915
1129
  ]
916
1130
  ] = None,
917
- page: int = 1,
918
1131
  return_main_text: bool = False,
919
1132
  return_markdown_text: bool = True,
920
1133
  enable_rerank: bool = True,
1134
+ number_of_result_pages: int = 10,
921
1135
  ) -> Dict[str, Any]:
922
1136
  r"""Query the Alibaba Tongxiao search API and return search results.
923
1137
 
@@ -931,17 +1145,14 @@ class SearchToolkit(BaseToolkit):
931
1145
 
932
1146
  Args:
933
1147
  query (str): The search query string (length >= 1 and <= 100).
934
- time_range (Literal["OneDay", "OneWeek", "OneMonth", "OneYear",
935
- "NoLimit"]): Time frame filter for search results.
1148
+ time_range (Literal["OneDay", "OneWeek", "OneMonth", "OneYear", "NoLimit"]):
1149
+ Time frame filter for search results.
936
1150
  (default: :obj:`"NoLimit"`)
937
- industry (Optional[Literal["finance", "law", "medical",
938
- "internet", "tax", "news_province", "news_center"]]):
1151
+ industry (Optional[Literal["finance", "law", "medical", "internet", "tax", "news_province", "news_center"]]):
939
1152
  Industry-specific search filter. When specified, only returns
940
1153
  results from sites in the specified industries. Multiple
941
1154
  industries can be comma-separated.
942
1155
  (default: :obj:`None`)
943
- page (int): Page number for results pagination.
944
- (default: :obj:`1`)
945
1156
  return_main_text (bool): Whether to include the main text of the
946
1157
  webpage in results. (default: :obj:`True`)
947
1158
  return_markdown_text (bool): Whether to include markdown formatted
@@ -949,6 +1160,10 @@ class SearchToolkit(BaseToolkit):
949
1160
  enable_rerank (bool): Whether to enable result reranking. If
950
1161
  response time is critical, setting this to False can reduce
951
1162
  response time by approximately 140ms. (default: :obj:`True`)
1163
+ number_of_result_pages (int): The number of result pages to
1164
+ retrieve. Adjust this based on your task - use fewer results
1165
+ for focused searches and more for comprehensive searches.
1166
+ (default: :obj:`10`)
952
1167
 
953
1168
  Returns:
954
1169
  Dict[str, Any]: A dictionary containing either search results with
@@ -974,7 +1189,7 @@ class SearchToolkit(BaseToolkit):
974
1189
  params: Dict[str, Union[str, int]] = {
975
1190
  "query": query,
976
1191
  "timeRange": time_range,
977
- "page": page,
1192
+ "page": number_of_result_pages,
978
1193
  "returnMainText": str(return_main_text).lower(),
979
1194
  "returnMarkdownText": str(return_markdown_text).lower(),
980
1195
  "enableRerank": str(enable_rerank).lower(),
@@ -1062,6 +1277,172 @@ class SearchToolkit(BaseToolkit):
1062
1277
  f"search: {e!s}"
1063
1278
  }
1064
1279
 
1280
+ @api_keys_required([(None, 'METASO_API_KEY')])
1281
+ def search_metaso(
1282
+ self,
1283
+ query: str,
1284
+ page: int = 1,
1285
+ include_summary: bool = False,
1286
+ include_raw_content: bool = False,
1287
+ concise_snippet: bool = False,
1288
+ scope: Literal[
1289
+ "webpage", "document", "scholar", "image", "video", "podcast"
1290
+ ] = "webpage",
1291
+ ) -> Dict[str, Any]:
1292
+ r"""Perform a web search using the metaso.cn API.
1293
+
1294
+ Args:
1295
+ query (str): The search query string.
1296
+ page (int): Page number. (default: :obj:`1`)
1297
+ include_summary (bool): Whether to include summary in the result.
1298
+ (default: :obj:`False`)
1299
+ include_raw_content (bool): Whether to include raw content in the
1300
+ result. (default: :obj:`False`)
1301
+ concise_snippet (bool): Whether to return concise snippet.
1302
+ (default: :obj:`False`)
1303
+ scope (Literal["webpage", "document", "scholar", "image", "video",
1304
+ "podcast"]): Search scope. (default: :obj:`"webpage"`)
1305
+
1306
+ Returns:
1307
+ Dict[str, Any]: Search results or error information.
1308
+ """
1309
+ import http.client
1310
+ import json
1311
+
1312
+ # It is recommended to put the token in environment variable for
1313
+ # security
1314
+
1315
+ METASO_API_KEY = os.getenv("METASO_API_KEY")
1316
+
1317
+ conn = http.client.HTTPSConnection("metaso.cn")
1318
+ payload = json.dumps(
1319
+ {
1320
+ "q": query,
1321
+ "scope": scope,
1322
+ "includeSummary": include_summary,
1323
+ "page": str(page),
1324
+ "includeRawContent": include_raw_content,
1325
+ "conciseSnippet": concise_snippet,
1326
+ }
1327
+ )
1328
+ headers = {
1329
+ 'Authorization': f'Bearer {METASO_API_KEY}',
1330
+ 'Accept': 'application/json',
1331
+ 'Content-Type': 'application/json',
1332
+ }
1333
+ try:
1334
+ conn.request("POST", "/api/v1/search", payload, headers)
1335
+ res = conn.getresponse()
1336
+ data = res.read()
1337
+ result = data.decode("utf-8")
1338
+ try:
1339
+ return json.loads(result)
1340
+ except Exception:
1341
+ return {
1342
+ "error": f"Metaso returned content could not be parsed: {result}"
1343
+ }
1344
+ except Exception as e:
1345
+ return {"error": f"Metaso search failed: {e}"}
1346
+
1347
+ @dependencies_required("google-search-results")
1348
+ @api_keys_required([(None, 'SERPAPI_KEY')])
1349
+ def search_serpapi(
1350
+ self,
1351
+ query: str,
1352
+ engine: str = "google",
1353
+ location: str = "Austin,Texas",
1354
+ google_domain: str = "google.com",
1355
+ gl: str = "us",
1356
+ search_lang: str = "en",
1357
+ device: str = "desktop",
1358
+ number_of_result_pages: int = 1,
1359
+ safe: str = "off",
1360
+ filter: int = 0,
1361
+ custom_params: Optional[Dict[str, Any]] = None,
1362
+ ) -> Dict[str, Any]:
1363
+ r"""Use SerpApi search engine to search information for the given query.
1364
+
1365
+ SerpApi provides real-time search engine results from multiple search engines
1366
+ including Google, Bing, Yahoo, DuckDuckGo, Baidu, Yandex, and more.
1367
+
1368
+ Args:
1369
+ query (str): The search query string.
1370
+ engine (str): Search engine to use. Supported engines include:
1371
+ 'google', 'bing', 'yahoo', 'duckduckgo', 'baidu', 'yandex',
1372
+ 'youtube', 'ebay', 'amazon', etc. (default: :obj:`"google"`)
1373
+ location (str): Location for localized search results. Can be a city,
1374
+ state, country, or coordinates. (default: :obj:`"Austin,Texas"`)
1375
+ google_domain (str): Google domain to use (e.g., 'google.com', 'google.co.uk').
1376
+ Only applicable for Google engine. (default: :obj:`"google.com"`)
1377
+ gl (str): Country code for localized results (e.g., 'us', 'uk', 'ca').
1378
+ Only applicable for Google engine. (default: :obj:`"us"`)
1379
+ search_lang (str): Language code for results (e.g., 'en', 'es', 'fr').
1380
+ Only applicable for Google engine. (default: :obj:`"en"`)
1381
+ device (str): Device type: 'desktop', 'tablet', or 'mobile'.
1382
+ (default: :obj:`"desktop"`)
1383
+ number_of_result_pages (int): Number of organic results to return.
1384
+ Adjust based on task needs. (default: :obj:`1`)
1385
+ safe (str): Safe search level: 'off', 'medium', 'high', 'active'.
1386
+ (default: :obj:`"off"`)
1387
+ filter (int): Filter results: 0 (no filter), 1 (filter similar results).
1388
+ (default: :obj:`0`)
1389
+ custom_params (Optional[Dict[str, Any]]): Additional custom parameters
1390
+ to pass to SerpApi. (default: :obj:`None`)
1391
+
1392
+ Returns:
1393
+ Dict[str, Any]: A dictionary containing search results:
1394
+ - 'results': List of organic search results, each containing:
1395
+ - 'title': The title of the search result
1396
+ - 'link': The URL of the search result
1397
+ - 'snippet': The description snippet
1398
+ - 'keywords': Highlighted keywords in the snippet
1399
+ - 'source': The source of the result
1400
+ - 'error: Error if any
1401
+ """
1402
+ from serpapi import SerpApiClient
1403
+
1404
+ SerpApiClient.SERP_API_KEY = os.getenv("SERPAPI_KEY")
1405
+ params = {
1406
+ "engine": engine,
1407
+ "q": query,
1408
+ "location": location,
1409
+ "google_domain": google_domain,
1410
+ "gl": gl,
1411
+ "hl": search_lang,
1412
+ "device": device,
1413
+ "num": number_of_result_pages,
1414
+ "safe": safe,
1415
+ "filter": filter,
1416
+ }
1417
+
1418
+ if custom_params is not None:
1419
+ params.update(custom_params)
1420
+ try:
1421
+ search = SerpApiClient(params)
1422
+ results = search.get_dict()
1423
+
1424
+ if (
1425
+ "organic_results" not in results
1426
+ or not results["organic_results"]
1427
+ ):
1428
+ return {"error": "No organic results found"}
1429
+
1430
+ formatted_results = []
1431
+ for result in results['organic_results']:
1432
+ formatted_result = {
1433
+ "title": result.get("title", ""),
1434
+ "link": result.get("link", ""),
1435
+ "snippet": result.get("snippet", ""),
1436
+ "keywords": result.get("snippet_highlighted_words", []),
1437
+ "source": result.get("source", ""),
1438
+ }
1439
+ formatted_results.append(formatted_result)
1440
+
1441
+ return {"results": formatted_results}
1442
+
1443
+ except Exception as e:
1444
+ return {"error": f"SerpApi search failed: {e!s}"}
1445
+
1065
1446
  def get_tools(self) -> List[FunctionTool]:
1066
1447
  r"""Returns a list of FunctionTool objects representing the
1067
1448
  functions in the toolkit.
@@ -1075,11 +1456,23 @@ class SearchToolkit(BaseToolkit):
1075
1456
  FunctionTool(self.search_linkup),
1076
1457
  FunctionTool(self.search_google),
1077
1458
  FunctionTool(self.search_duckduckgo),
1078
- FunctionTool(self.tavily_search),
1459
+ FunctionTool(self.search_tavily),
1079
1460
  FunctionTool(self.search_brave),
1080
1461
  FunctionTool(self.search_bocha),
1081
1462
  FunctionTool(self.search_baidu),
1082
1463
  FunctionTool(self.search_bing),
1083
1464
  FunctionTool(self.search_exa),
1084
1465
  FunctionTool(self.search_alibaba_tongxiao),
1466
+ FunctionTool(self.search_metaso),
1467
+ FunctionTool(self.search_serpapi),
1085
1468
  ]
1469
+
1470
+ # Deprecated method alias for backward compatibility
1471
+ def tavily_search(self, *args, **kwargs):
1472
+ r"""Deprecated: Use search_tavily instead for consistency with other search methods."""
1473
+ warnings.warn(
1474
+ "tavily_search is deprecated. Use search_tavily instead for consistency.",
1475
+ DeprecationWarning,
1476
+ stacklevel=2,
1477
+ )
1478
+ return self.search_tavily(*args, **kwargs)