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