camel-ai 0.2.65__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 (505) 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 +4835 -947
  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 +23 -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/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 +2 -2
  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 +5 -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 +2 -2
  75. camel/configs/samba_config.py +6 -4
  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_collectors/__init__.py +2 -2
  84. camel/data_collectors/alpaca_collector.py +18 -9
  85. camel/data_collectors/base.py +2 -2
  86. camel/data_collectors/sharegpt_collector.py +2 -2
  87. camel/datagen/__init__.py +2 -2
  88. camel/datagen/cot_datagen.py +3 -3
  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 +12 -12
  92. camel/datagen/evol_instruct/templates.py +16 -16
  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 +2 -2
  113. camel/datasets/self_instruct_generator.py +2 -2
  114. camel/datasets/static_dataset.py +2 -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 +2 -2
  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 +2 -2
  124. camel/embeddings/together_embedding.py +2 -2
  125. camel/embeddings/vlm_embedding.py +2 -2
  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 +2 -2
  139. camel/interpreters/docker/Dockerfile +14 -24
  140. camel/interpreters/docker_interpreter.py +5 -4
  141. camel/interpreters/e2b_interpreter.py +36 -3
  142. camel/interpreters/internal_python_interpreter.py +53 -4
  143. camel/interpreters/interpreter_error.py +2 -2
  144. camel/interpreters/ipython_interpreter.py +2 -2
  145. camel/interpreters/microsandbox_interpreter.py +395 -0
  146. camel/interpreters/subprocess_interpreter.py +2 -2
  147. camel/loaders/__init__.py +13 -4
  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 +11 -2
  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 +2 -2
  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 +125 -7
  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 +90 -10
  170. camel/messages/__init__.py +2 -2
  171. camel/messages/base.py +178 -43
  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 +16 -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 +212 -89
  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 +16 -21
  192. camel/models/cometapi_model.py +83 -0
  193. camel/models/crynux_model.py +11 -18
  194. camel/models/deepseek_model.py +18 -58
  195. camel/models/fish_audio_model.py +8 -2
  196. camel/models/gemini_model.py +389 -26
  197. camel/models/groq_model.py +11 -19
  198. camel/models/internlm_model.py +11 -18
  199. camel/models/litellm_model.py +56 -34
  200. camel/models/lmstudio_model.py +17 -20
  201. camel/models/minimax_model.py +83 -0
  202. camel/models/mistral_model.py +18 -19
  203. camel/models/model_factory.py +37 -3
  204. camel/models/model_manager.py +26 -8
  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 +188 -45
  215. camel/models/openai_model.py +216 -71
  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 +21 -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 +48 -47
  227. camel/models/sglang_model.py +88 -40
  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 +7 -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 +3 -2
  262. camel/retrievers/base.py +2 -2
  263. camel/retrievers/bm25_retriever.py +2 -2
  264. camel/retrievers/cohere_rerank_retriever.py +2 -2
  265. camel/retrievers/hybrid_retrival.py +2 -2
  266. camel/retrievers/vector_retriever.py +2 -2
  267. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  268. camel/runtimes/__init__.py +2 -2
  269. camel/runtimes/api.py +79 -23
  270. camel/runtimes/base.py +2 -2
  271. camel/runtimes/configs.py +13 -13
  272. camel/runtimes/daytona_runtime.py +17 -18
  273. camel/runtimes/docker_runtime.py +12 -12
  274. camel/runtimes/llm_guard_runtime.py +26 -26
  275. camel/runtimes/remote_http_runtime.py +11 -11
  276. camel/runtimes/ubuntu_docker_runtime.py +2 -2
  277. camel/runtimes/utils/__init__.py +2 -2
  278. camel/runtimes/utils/function_risk_toolkit.py +2 -2
  279. camel/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 +2 -2
  290. camel/societies/workforce/events.py +143 -0
  291. camel/societies/workforce/prompts.py +258 -33
  292. camel/societies/workforce/role_playing_worker.py +88 -31
  293. camel/societies/workforce/single_agent_worker.py +638 -40
  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 +780 -65
  297. camel/societies/workforce/worker.py +92 -26
  298. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  299. camel/societies/workforce/workforce.py +5276 -355
  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 +6 -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 +8 -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 +2 -2
  324. camel/storages/vectordb_storages/milvus.py +2 -2
  325. camel/storages/vectordb_storages/oceanbase.py +15 -15
  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 +2 -2
  331. camel/tasks/__init__.py +2 -2
  332. camel/tasks/task.py +348 -26
  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 +54 -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 +4 -4
  343. camel/toolkits/audio_analysis_toolkit.py +3 -3
  344. camel/toolkits/base.py +65 -7
  345. camel/toolkits/bohrium_toolkit.py +2 -2
  346. camel/toolkits/browser_toolkit.py +34 -21
  347. camel/toolkits/browser_toolkit_commons.py +4 -4
  348. camel/toolkits/code_execution.py +31 -4
  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 +905 -71
  357. camel/toolkits/file_toolkit.py +1402 -0
  358. camel/toolkits/function_tool.py +126 -18
  359. camel/toolkits/github_toolkit.py +109 -22
  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 -6
  395. camel/toolkits/image_generation_toolkit.py +390 -0
  396. camel/toolkits/jina_reranker_toolkit.py +5 -6
  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 +412 -36
  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 +53 -53
  434. camel/toolkits/playwright_mcp_toolkit.py +13 -31
  435. camel/toolkits/pptx_toolkit.py +36 -23
  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 +5 -5
  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 +2 -2
  456. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  457. camel/toolkits/video_analysis_toolkit.py +109 -29
  458. camel/toolkits/video_download_toolkit.py +19 -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 +2 -2
  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 +378 -39
  469. camel/types/mcp_registries.py +2 -2
  470. camel/types/openai_types.py +4 -4
  471. camel/types/unified_model_type.py +38 -6
  472. camel/utils/__init__.py +2 -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 +38 -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 +2 -2
  484. camel/utils/mcp.py +140 -6
  485. camel/utils/mcp_client.py +48 -38
  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.65.dist-info → camel_ai-0.2.82.dist-info}/METADATA +327 -94
  497. camel_ai-0.2.82.dist-info/RECORD +507 -0
  498. {camel_ai-0.2.65.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
  499. {camel_ai-0.2.65.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
  500. camel/loaders/pandas_reader.py +0 -368
  501. camel/toolkits/dalle_toolkit.py +0 -175
  502. camel/toolkits/file_write_toolkit.py +0 -444
  503. camel/toolkits/openai_agent_toolkit.py +0 -135
  504. camel/toolkits/terminal_toolkit.py +0 -1037
  505. camel_ai-0.2.65.dist-info/RECORD +0 -426
@@ -0,0 +1,860 @@
1
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ import re
16
+ from abc import abstractmethod
17
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
18
+
19
+ if TYPE_CHECKING:
20
+ from rlcard.agents import RandomAgent
21
+
22
+ from camel.environments.models import Action, Observation
23
+ from camel.environments.multi_step import MultiStepEnv
24
+ from camel.extractors import BaseExtractor, BaseExtractorStrategy
25
+ from camel.logger import get_logger
26
+
27
+ logger = get_logger(__name__)
28
+
29
+
30
+ class ActionExtractor(BaseExtractorStrategy):
31
+ r"""A strategy for extracting RLCard actions from text."""
32
+
33
+ def __init__(self, action_pattern: str = r"<Action>\s*(.+)") -> None:
34
+ r"""Initialize the action extractor with a regex pattern.
35
+
36
+ Args:
37
+ action_pattern (str): The regex pattern to extract actions.
38
+ (default: :obj:`"<Action>\\s*(.+)"`).
39
+ """
40
+ self.action_pattern = action_pattern
41
+
42
+ async def extract(self, text: str) -> Optional[str]:
43
+ r"""Extract a valid RLCard action from text.
44
+
45
+ Looks for a pattern '<Action> action_str' where action_str is the
46
+ string representation of the action.
47
+
48
+ Args:
49
+ text (str): The text to extract the action from.
50
+
51
+ Returns:
52
+ Optional[str]: The extracted action as a string, or None
53
+ if no valid action is found.
54
+ """
55
+ match = re.search(self.action_pattern, text)
56
+ if match:
57
+ action_str = match.group(1).strip()
58
+ return action_str
59
+ return None
60
+
61
+
62
+ class RLCardsEnv(MultiStepEnv):
63
+ r"""A base environment for RLCard games.
64
+
65
+ This environment implements a wrapper around RLCard environments for
66
+ reinforcement learning with LLMs. It handles the conversion between
67
+ RLCard states and actions and the CAMEL environment interface.
68
+ """
69
+
70
+ def __init__(
71
+ self,
72
+ game_name: str,
73
+ extractor: Optional[BaseExtractor] = None,
74
+ max_steps: Optional[int] = None,
75
+ num_players: int = 2,
76
+ **kwargs,
77
+ ) -> None:
78
+ r"""Initialize the RLCard environment.
79
+
80
+ Args:
81
+ game_name (str): The name of the RLCard game to play.
82
+ extractor (Optional[BaseExtractor]): Extractor to process LLM
83
+ responses. If None, a default extractor with ActionExtractor
84
+ will be used. (default: :obj:`None`)
85
+ max_steps (Optional[int]): Maximum steps per episode.
86
+ (default: :obj:`None`)
87
+ num_players (int): Number of players in the game.
88
+ (default: :obj:`2`)
89
+ **kwargs: Additional environment parameters.
90
+ """
91
+ if extractor is None:
92
+ extractor = BaseExtractor(pipeline=[[ActionExtractor()]])
93
+
94
+ super().__init__(extractor, max_steps, **kwargs)
95
+
96
+ self.game_name = game_name
97
+ self.num_players = num_players
98
+ self.rlcard_env = None
99
+ self.current_player_id = None
100
+ self.agents: Optional[List[Optional['RandomAgent']]] = None
101
+
102
+ async def _setup(self) -> None:
103
+ r"""Set up the RLCard environment.
104
+
105
+ This method initializes the RLCard environment with the specified game
106
+ and parameters.
107
+ """
108
+ import rlcard
109
+
110
+ try:
111
+ # Create the RLCard environment
112
+ self.rlcard_env = rlcard.make(
113
+ self.game_name,
114
+ config={
115
+ 'game_num_players': self.num_players,
116
+ 'allow_step_back': True,
117
+ **self._metadata.get('rlcard_config', {}),
118
+ },
119
+ )
120
+
121
+ from rlcard.agents import RandomAgent
122
+
123
+ # Initialize random agents for opponents
124
+ self.agents = [None] * self.num_players
125
+ assert self.rlcard_env is not None
126
+
127
+ for i in range(1, self.num_players): # Skip player 0 (LLM agent)
128
+ self.agents[i] = RandomAgent(
129
+ num_actions=self.rlcard_env.num_actions
130
+ )
131
+
132
+ logger.info(
133
+ f"RLCard environment for {self.game_name} initialized "
134
+ f"successfully"
135
+ )
136
+ except Exception as e:
137
+ logger.error(f"Failed to initialize RLCard environment: {e}")
138
+ raise
139
+
140
+ async def _close(self) -> None:
141
+ r"""Clean up the RLCard environment."""
142
+ self.rlcard_env = None
143
+ self.agents = None
144
+
145
+ def _get_initial_state(self) -> Dict[str, Any]:
146
+ r"""Get the initial state of the environment.
147
+
148
+ Returns:
149
+ Dict[str, Any]: A dictionary containing the initial state with
150
+ game state, player info, and game status flags.
151
+ """
152
+ return {
153
+ "rlcard_state": None,
154
+ "legal_actions": [],
155
+ "game_over": False,
156
+ "winner": None,
157
+ "payoffs": None,
158
+ "last_action": None,
159
+ "last_action_illegal": False,
160
+ "extraction_error": None,
161
+ }
162
+
163
+ async def _update_state(self, action: Action) -> None:
164
+ r"""Update the environment state based on the agent's action.
165
+
166
+ This method processes the agent's action, updates the game state,
167
+ and handles opponent moves if necessary.
168
+
169
+ Args:
170
+ action (Action): The action containing the LLM's response with the
171
+ chosen move.
172
+ """
173
+ assert self.rlcard_env is not None
174
+
175
+ if self._state["game_over"]:
176
+ return
177
+
178
+ # Extract action from LLM response
179
+ extraction_result = await self.extractor.extract(action.llm_response)
180
+ if not extraction_result:
181
+ self._state["last_action_illegal"] = True
182
+ self._state["extraction_error"] = (
183
+ "Could not extract a valid action"
184
+ )
185
+ return
186
+
187
+ # Convert extracted action to RLCard action format
188
+ rlcard_action = self._convert_to_rlcard_action(extraction_result)
189
+ if (
190
+ rlcard_action is None
191
+ or rlcard_action not in self._state["legal_actions"]
192
+ ):
193
+ self._state["last_action_illegal"] = True
194
+ self._state["extraction_error"] = (
195
+ f"'{extraction_result}' is not a valid action"
196
+ )
197
+ return
198
+
199
+ # Reset illegal action flag
200
+ self._state["last_action_illegal"] = False
201
+ self._state["extraction_error"] = None
202
+ self._state["last_action"] = extraction_result
203
+
204
+ # Take the action in the environment
205
+ next_state, self.current_player_id = self.rlcard_env.step(
206
+ rlcard_action
207
+ )
208
+
209
+ # Update state with new information
210
+ self._state["rlcard_state"] = next_state
211
+ self._state["legal_actions"] = next_state['legal_actions'][
212
+ self.current_player_id
213
+ ]
214
+
215
+ # Check if game is over
216
+ if self.rlcard_env.is_over():
217
+ self._state["game_over"] = True
218
+ self._state["payoffs"] = self.rlcard_env.get_payoffs()
219
+ # Determine winner based on payoffs
220
+ payoffs = self._state["payoffs"]
221
+ if payoffs[0] > 0:
222
+ self._state["winner"] = "player"
223
+ elif any(p > 0 for p in payoffs[1:]):
224
+ self._state["winner"] = "opponent"
225
+ else:
226
+ self._state["winner"] = "draw"
227
+ return
228
+
229
+ # If next player is not the LLM agent (player 0), let opponents play
230
+ while self.current_player_id != 0 and not self._state["game_over"]:
231
+ # Get action from the corresponding agent
232
+ agent_action = self.agents[self.current_player_id].eval_step(
233
+ next_state
234
+ )
235
+ # Take the action
236
+ next_state, self.current_player_id = self.rlcard_env.step(
237
+ agent_action
238
+ )
239
+
240
+ # Update state
241
+ self._state["rlcard_state"] = next_state
242
+ if self.current_player_id == 0: # Back to LLM agent
243
+ self._state["legal_actions"] = next_state['legal_actions'][0]
244
+
245
+ # Check if game is over after opponent's move
246
+ if self.rlcard_env.is_over():
247
+ self._state["game_over"] = True
248
+ self._state["payoffs"] = self.rlcard_env.get_payoffs()
249
+ # Determine winner based on payoffs
250
+ payoffs = self._state["payoffs"]
251
+ if payoffs[0] > 0:
252
+ self._state["winner"] = "player"
253
+ elif any(p > 0 for p in payoffs[1:]):
254
+ self._state["winner"] = "opponent"
255
+ else:
256
+ self._state["winner"] = "draw"
257
+
258
+ def _get_next_observation(self) -> Observation:
259
+ r"""Get the next observation based on the current state.
260
+
261
+ This method generates a text observation describing the current state
262
+ of the game and prompting the agent to make a move.
263
+
264
+ Returns:
265
+ Observation: An Observation object containing the game state
266
+ description.
267
+ """
268
+ assert self.rlcard_env is not None
269
+
270
+ if self._state["rlcard_state"] is None:
271
+ # Initial observation before the game starts
272
+ state, self.current_player_id = self.rlcard_env.reset()
273
+ self._state["rlcard_state"] = state
274
+ # Safely get legal actions, default to empty list if key
275
+ # is missing or value is None
276
+ legal_actions_dict = state.get('legal_actions', {})
277
+ player_legal_actions = legal_actions_dict.get(
278
+ self.current_player_id
279
+ )
280
+ self._state["legal_actions"] = (
281
+ player_legal_actions
282
+ if player_legal_actions is not None
283
+ else []
284
+ )
285
+
286
+ # Generate observation text
287
+ if self._state["last_action_illegal"]:
288
+ # Safely retrieve last_action to prevent None value
289
+ last_action = self._state.get("last_action", "None")
290
+ error_msg = self._state.get("extraction_error", "Unknown error")
291
+
292
+ inter_state_space = self._format_state_for_observation(
293
+ self._state['rlcard_state']
294
+ )
295
+ inter_action_space = self._format_legal_actions(
296
+ self._state['legal_actions']
297
+ )
298
+ obs_text = (
299
+ f"You are playing {self.game_name}.\n"
300
+ f"Your last action '{last_action}' was illegal.\n"
301
+ f"Error: {error_msg}\n"
302
+ f"Current game state:\n"
303
+ f"{inter_state_space}\n"
304
+ f"Legal actions: {inter_action_space}\n"
305
+ f"Please choose an action and end your response with "
306
+ f"<Action> [your action]"
307
+ )
308
+ else:
309
+ inter_state_space = self._format_state_for_observation(
310
+ self._state['rlcard_state']
311
+ )
312
+ inter_action_space = self._format_legal_actions(
313
+ self._state['legal_actions']
314
+ )
315
+ obs_text = (
316
+ f"You are playing {self.game_name}.\n"
317
+ f"Current game state:\n"
318
+ f"{inter_state_space}\n"
319
+ f"Legal actions: {inter_action_space}\n"
320
+ f"Please choose an action and end your response with "
321
+ f"<Action> [your action]"
322
+ )
323
+
324
+ return Observation(
325
+ question=obs_text,
326
+ context={
327
+ "game_name": self.game_name,
328
+ "raw_state": self._state["rlcard_state"],
329
+ "legal_actions": self._state["legal_actions"],
330
+ },
331
+ metadata={},
332
+ )
333
+
334
+ def _get_terminal_observation(self) -> Observation:
335
+ r"""Get the final observation when the game is over.
336
+
337
+ This method generates a text observation describing the final state
338
+ of the game and the game result (win, loss, or draw).
339
+
340
+ Returns:
341
+ Observation: An Observation object containing the final game state
342
+ description.
343
+ """
344
+ result_message = ""
345
+ if self._state["winner"] == "player":
346
+ result_message = "Congratulations, you won!"
347
+ elif self._state["winner"] == "opponent":
348
+ result_message = "Sorry, you lost!"
349
+ else:
350
+ result_message = "It's a draw!"
351
+
352
+ # Safely handle errors to prevent errors caused by None type
353
+ payoffs = self._state.get("payoffs", [])
354
+ payoffs_str = (
355
+ ", ".join([f"{p:.2f}" for p in payoffs]) if payoffs else "N/A"
356
+ )
357
+
358
+ obs_text = (
359
+ f"Game Over. {result_message}\n"
360
+ f"Final game state:\n"
361
+ f"{self._format_state_for_observation(self._state['rlcard_state'])}\n"
362
+ f"Payoffs: [{payoffs_str}]\n"
363
+ )
364
+
365
+ return Observation(
366
+ question=obs_text,
367
+ context={
368
+ "game_name": self.game_name,
369
+ "raw_state": self._state["rlcard_state"],
370
+ "payoffs": self._state["payoffs"],
371
+ "winner": self._state["winner"],
372
+ },
373
+ metadata={},
374
+ )
375
+
376
+ async def compute_reward(
377
+ self,
378
+ ) -> Tuple[float, Dict[str, float]]:
379
+ r"""Compute the reward for the current state.
380
+
381
+ Returns:
382
+ Tuple[float, Dict[str, float]]: A tuple containing the total
383
+ reward and a dictionary of reward components:
384
+ - 1.0 for a win
385
+ - 0.0 for a loss or illegal move
386
+ - 0.5 for a draw
387
+ - For ongoing games, returns a small step penalty
388
+ """
389
+ if self._state["game_over"]:
390
+ if self._state["winner"] == "player":
391
+ return 1.0, {"win": 1.0}
392
+ elif self._state["winner"] == "opponent":
393
+ return 0.0, {"loss": 0.0}
394
+ else:
395
+ return 0.5, {"draw": 0.5}
396
+ elif self._state["last_action_illegal"]:
397
+ return 0.0, {"illegal_move": 0.0}
398
+ else:
399
+ # Small negative reward for each step to encourage efficiency
400
+ step_penalty = -0.01
401
+ return step_penalty, {"step_penalty": step_penalty}
402
+
403
+ def _is_done(self) -> bool:
404
+ r"""Check if the episode is done.
405
+
406
+ Returns:
407
+ bool: True if the game is over, False otherwise.
408
+ """
409
+ return self._state["game_over"]
410
+
411
+ @abstractmethod
412
+ def _convert_to_rlcard_action(self, action_str: str) -> Any:
413
+ r"""Convert a string action to the format expected by RLCard.
414
+
415
+ This method must be implemented by subclasses to handle the specific
416
+ action format of each game.
417
+
418
+ Args:
419
+ action_str (str): The string representation of the action.
420
+
421
+ Returns:
422
+ Any: The action in the format expected by the RLCard environment.
423
+ """
424
+ pass
425
+
426
+ @abstractmethod
427
+ def _format_state_for_observation(self, state: Dict[str, Any]) -> str:
428
+ r"""Format the RLCard state for human-readable observation.
429
+
430
+ This method must be implemented by subclasses to create a
431
+ human-readable representation of the game state.
432
+
433
+ Args:
434
+ state (Dict[str, Any]): The RLCard state dictionary.
435
+
436
+ Returns:
437
+ str: A human-readable representation of the state.
438
+ """
439
+ pass
440
+
441
+ @abstractmethod
442
+ def _format_legal_actions(self, legal_actions: List[Any]) -> str:
443
+ r"""Format the legal actions for human-readable observation.
444
+
445
+ This method must be implemented by subclasses to create a
446
+ human-readable representation of the legal actions.
447
+
448
+ Args:
449
+ legal_actions (List[Any]): The list of legal actions.
450
+
451
+ Returns:
452
+ str: A human-readable representation of the legal actions.
453
+ """
454
+ pass
455
+
456
+
457
+ class BlackjackEnv(RLCardsEnv):
458
+ r"""A Blackjack environment for reinforcement learning with LLMs.
459
+
460
+ This environment implements a standard Blackjack game where the LLM agent
461
+ plays against a dealer.
462
+ """
463
+
464
+ def __init__(
465
+ self,
466
+ extractor: Optional[BaseExtractor] = None,
467
+ max_steps: Optional[int] = None,
468
+ **kwargs,
469
+ ) -> None:
470
+ r"""Initialize the Blackjack environment.
471
+
472
+ Args:
473
+ extractor (Optional[BaseExtractor]): Extractor to process LLM
474
+ responses. If None, a default extractor will be used.
475
+ (default: :obj:`None`)
476
+ max_steps (Optional[int]): Maximum steps per episode.
477
+ (default: :obj:`None`)
478
+ **kwargs: Additional environment parameters.
479
+ """
480
+ super().__init__(
481
+ "blackjack", extractor, max_steps, num_players=1, **kwargs
482
+ )
483
+
484
+ def _convert_to_rlcard_action(self, action_str: str) -> int:
485
+ r"""Convert a string action to the format expected by RLCard Blackjack.
486
+
487
+ Args:
488
+ action_str (str): The string representation of the action.
489
+ Expected to be 'hit' or 'stand'.
490
+
491
+ Returns:
492
+ int: 0 for 'hit', 1 for 'stand'.
493
+ """
494
+ action_str = action_str.lower().strip()
495
+ if action_str == "hit":
496
+ return 0
497
+ elif action_str == "stand":
498
+ return 1
499
+ raise ValueError()
500
+
501
+ def _format_state_for_observation(self, state: Dict[str, Any]) -> str:
502
+ r"""Format the Blackjack state for human-readable observation.
503
+
504
+ Args:
505
+ state (Dict[str, Any]): The RLCard state dictionary.
506
+
507
+ Returns:
508
+ str: A human-readable representation of the state.
509
+ """
510
+ if state is None:
511
+ return "Game not started yet."
512
+
513
+ # Extract state information safely
514
+ raw_obs = state.get('raw_obs', {})
515
+ if raw_obs is None:
516
+ raw_obs = {}
517
+ player_hand = raw_obs.get('player', [])
518
+ dealer_hand = raw_obs.get('dealer', [])
519
+
520
+ # 确保player_hand和dealer_hand是列表
521
+ if player_hand is None:
522
+ player_hand = []
523
+ if dealer_hand is None:
524
+ dealer_hand = []
525
+
526
+ # Format hands
527
+ player_cards = self._format_cards(player_hand)
528
+ dealer_cards = self._format_cards(dealer_hand)
529
+
530
+ # Calculate hand values
531
+ player_value = self._calculate_hand_value(player_hand)
532
+ dealer_value = self._calculate_hand_value(dealer_hand)
533
+
534
+ return (
535
+ f"Your hand: {player_cards} (Value: {player_value})\n"
536
+ f"Dealer's hand: {dealer_cards} (Value: {dealer_value})"
537
+ )
538
+
539
+ def _format_legal_actions(self, legal_actions: List[int]) -> str:
540
+ r"""Format the legal actions for Blackjack.
541
+
542
+ Args:
543
+ legal_actions (List[int]): The list of legal actions.
544
+
545
+ Returns:
546
+ str: A human-readable representation of the legal actions.
547
+ """
548
+ if not legal_actions:
549
+ return "No legal actions available"
550
+
551
+ action_map = {0: "hit", 1: "stand"}
552
+ return ", ".join([action_map.get(a, str(a)) for a in legal_actions])
553
+
554
+ def _format_cards(self, cards: List[str]) -> str:
555
+ r"""Format a list of cards for display.
556
+
557
+ Args:
558
+ cards (List[str]): List of card strings.
559
+
560
+ Returns:
561
+ str: Formatted card string.
562
+ """
563
+ return ", ".join(cards)
564
+
565
+ def _calculate_hand_value(self, cards: List[str]) -> int:
566
+ r"""Calculate the value of a hand in Blackjack.
567
+
568
+ Args:
569
+ cards (List[str]): List of card strings.
570
+
571
+ Returns:
572
+ int: The value of the hand.
573
+ """
574
+ value = 0
575
+ num_aces = 0
576
+
577
+ for card in cards:
578
+ # Extract the rank (first character(s) before the suit)
579
+ rank = card[:1]
580
+ if rank == 'A':
581
+ num_aces += 1
582
+ value += 11
583
+ elif rank in ['J', 'Q', 'K', 'T']:
584
+ value += 10
585
+ else:
586
+ value += int(rank)
587
+
588
+ # Adjust for aces if needed
589
+ while value > 21 and num_aces > 0:
590
+ value -= 10 # Change an ace from 11 to 1
591
+ num_aces -= 1
592
+
593
+ return value
594
+
595
+
596
+ class LeducHoldemEnv(RLCardsEnv):
597
+ r"""A Leduc Hold'em environment for reinforcement learning with LLMs.
598
+
599
+ This environment implements a Leduc Hold'em poker game where the LLM agent
600
+ plays against one or more opponents.
601
+ """
602
+
603
+ def __init__(
604
+ self,
605
+ extractor: Optional[BaseExtractor] = None,
606
+ max_steps: Optional[int] = None,
607
+ num_players: int = 2,
608
+ **kwargs,
609
+ ) -> None:
610
+ r"""Initialize the Leduc Hold'em environment.
611
+
612
+ Args:
613
+ extractor (Optional[BaseExtractor]): Extractor to process LLM
614
+ responses. If None, a default extractor will be used.
615
+ (default: :obj:`None`)
616
+ max_steps (Optional[int]): Maximum steps per episode.
617
+ (default: :obj:`None`)
618
+ num_players (int): Number of players in the game.
619
+ (default: :obj:`2`)
620
+ **kwargs: Additional environment parameters.
621
+ """
622
+ super().__init__(
623
+ "leduc-holdem",
624
+ extractor,
625
+ max_steps,
626
+ num_players=num_players,
627
+ **kwargs,
628
+ )
629
+
630
+ def _convert_to_rlcard_action(self, action_str: str) -> int:
631
+ r"""Convert a string action to the format expected by RLCard
632
+ Leduc Hold'em.
633
+
634
+ Args:
635
+ action_str (str): The string representation of the action.
636
+ Expected to be 'fold', 'check', 'call', or 'raise'.
637
+
638
+ Returns:
639
+ int: 0 for 'fold', 1 for 'check/call', 2 for 'raise'.
640
+ """
641
+ action_str = action_str.lower().strip()
642
+ if action_str == "fold":
643
+ return 0
644
+ elif action_str in ["check", "call"]:
645
+ return 1
646
+ elif action_str == "raise":
647
+ return 2
648
+ else:
649
+ raise ValueError()
650
+
651
+ def _format_state_for_observation(self, state: Dict[str, Any]) -> str:
652
+ r"""Format the Leduc Hold'em state for human-readable observation.
653
+
654
+ Args:
655
+ state (Dict[str, Any]): The RLCard state dictionary.
656
+
657
+ Returns:
658
+ str: A human-readable representation of the state.
659
+ """
660
+ if state is None:
661
+ return "Game not started yet."
662
+
663
+ raw_obs = state.get('raw_obs', {})
664
+ if raw_obs is None:
665
+ raw_obs = {}
666
+
667
+ hand = raw_obs.get('hand', [])
668
+ public_card = raw_obs.get('public_card', None)
669
+ all_chips = raw_obs.get('all_chips', [])
670
+ my_chips = all_chips[0] if all_chips else 0
671
+ opponent_chips = all_chips[1:] if len(all_chips) > 1 else []
672
+ stage = raw_obs.get('stage', 0)
673
+ current_round = "pre-flop" if stage == 0 else "flop"
674
+
675
+ # Format the observation
676
+ obs_text = f"Round: {current_round}\n"
677
+ obs_text += f"Your hand: {hand}\n"
678
+ if public_card:
679
+ obs_text += f"Public card: {public_card}\n"
680
+ else:
681
+ obs_text += "Public card: None\n"
682
+
683
+ obs_text += f"Your chips: {my_chips}\n"
684
+ for i, chips in enumerate(opponent_chips):
685
+ obs_text += f"Opponent {i+1} chips: {chips}\n"
686
+
687
+ return obs_text
688
+
689
+ def _format_legal_actions(self, legal_actions: List[int]) -> str:
690
+ r"""Format the legal actions for Leduc Hold'em.
691
+
692
+ Args:
693
+ legal_actions (List[int]): The list of legal actions.
694
+
695
+ Returns:
696
+ str: A human-readable representation of the legal actions.
697
+ """
698
+ action_map = {0: "fold", 1: "check/call", 2: "raise"}
699
+ return ", ".join([action_map[a] for a in legal_actions])
700
+
701
+
702
+ class DoudizhuEnv(RLCardsEnv):
703
+ r"""A Doudizhu environment for reinforcement learning with LLMs.
704
+
705
+ This environment implements a standard Doudizhu game where the LLM agent
706
+ plays against two AI opponents.
707
+ """
708
+
709
+ def __init__(
710
+ self,
711
+ extractor: Optional[BaseExtractor] = None,
712
+ max_steps: Optional[int] = None,
713
+ **kwargs,
714
+ ) -> None:
715
+ r"""Initialize the Doudizhu environment.
716
+
717
+ Args:
718
+ extractor (Optional[BaseExtractor]): Extractor to process LLM
719
+ responses. If None, a default extractor will be used.
720
+ (default: :obj:`None`)
721
+ max_steps (Optional[int]): Maximum steps per episode.
722
+ (default: :obj:`None`)
723
+ **kwargs: Additional environment parameters.
724
+ """
725
+ super().__init__(
726
+ "doudizhu", extractor, max_steps, num_players=3, **kwargs
727
+ )
728
+
729
+ def _convert_to_rlcard_action(self, action_str: str) -> Any:
730
+ r"""Convert a string action to the format expected by RLCard Doudizhu.
731
+
732
+ Args:
733
+ action_str (str): The string representation of the action.
734
+ Expected to be a card combination or 'pass'.
735
+
736
+ Returns:
737
+ str: The action string in the format expected by RLCard.
738
+ """
739
+ action_str = action_str.lower().strip()
740
+ if action_str == "pass":
741
+ return "pass"
742
+
743
+ # For card combinations, we need to convert them to the RLCard format
744
+ # This is a simplified implementation and might need to be adjusted
745
+ # based on the exact format expected by RLCard
746
+
747
+ # Remove spaces and convert to uppercase for consistency
748
+ action_str = action_str.replace(" ", "").upper()
749
+
750
+ # Check if the action is in the legal actions
751
+ if action_str in self._state["legal_actions"]:
752
+ return action_str
753
+
754
+ return None
755
+
756
+ def _format_state_for_observation(self, state: Dict[str, Any]) -> str:
757
+ r"""Format the Doudizhu state for human-readable observation.
758
+
759
+ Args:
760
+ state (Dict[str, Any]): The RLCard state dictionary.
761
+
762
+ Returns:
763
+ str: A human-readable representation of the state.
764
+ """
765
+ if state is None:
766
+ return "Game not started yet."
767
+
768
+ # Extract state information
769
+ raw_obs = state['raw_obs']
770
+ current_hand = raw_obs['current_hand']
771
+ # potentially useful for debugging
772
+ # played_cards = raw_obs['played_cards']
773
+ landlord = raw_obs['landlord']
774
+ landlord_up_played_cards = raw_obs['landlord_up_played_cards']
775
+ landlord_down_played_cards = raw_obs['landlord_down_played_cards']
776
+ landlord_played_cards = raw_obs['landlord_played_cards']
777
+ bomb_num = raw_obs['bomb_num']
778
+
779
+ # Format the observation
780
+ obs_text = ""
781
+
782
+ # Player role
783
+ if landlord == 0:
784
+ obs_text += "You are the Landlord.\n"
785
+ else:
786
+ obs_text += (
787
+ f"You are a Peasant. Player {landlord} is the Landlord.\n"
788
+ )
789
+
790
+ # Current hand
791
+ obs_text += f"Your hand: {self._format_cards(current_hand)}\n"
792
+
793
+ # Last played cards by each player
794
+ obs_text += "Last played cards:\n"
795
+ if landlord == 0:
796
+ inter_text = self._format_cards(landlord_played_cards)
797
+ obs_text += f" You (Landlord): {inter_text}\n"
798
+ inter_text = self._format_cards(landlord_up_played_cards)
799
+ obs_text += f" Peasant 1: {inter_text}\n"
800
+ inter_text = self._format_cards(landlord_down_played_cards)
801
+ obs_text += f" Peasant 2: {inter_text}\n"
802
+ elif landlord == 1:
803
+ obs_text += (
804
+ f" You: {self._format_cards(landlord_up_played_cards)}\n"
805
+ )
806
+ obs_text += (
807
+ f" Landlord: {self._format_cards(landlord_played_cards)}\n"
808
+ )
809
+
810
+ inter_text = self._format_cards(landlord_down_played_cards)
811
+ obs_text += f" Other Peasant: {inter_text}\n"
812
+ else: # landlord == 2
813
+ obs_text += (
814
+ f" You: {self._format_cards(landlord_down_played_cards)}\n"
815
+ )
816
+ obs_text += (
817
+ f" Landlord: {self._format_cards(landlord_played_cards)}\n"
818
+ )
819
+
820
+ inter_text = self._format_cards(landlord_up_played_cards)
821
+ obs_text += f" Other Peasant: {inter_text}\n"
822
+
823
+ # Bomb count
824
+ obs_text += f"Number of bombs played: {bomb_num}\n"
825
+
826
+ return obs_text
827
+
828
+ def _format_legal_actions(self, legal_actions: List[str]) -> str:
829
+ r"""Format the legal actions for Doudizhu.
830
+
831
+ Args:
832
+ legal_actions (List[str]): The list of legal actions.
833
+
834
+ Returns:
835
+ str: A human-readable representation of the legal actions.
836
+ """
837
+ # For simplicity, we'll just list the first few legal
838
+ # actions if there are many
839
+ if len(legal_actions) > 10:
840
+ action_str = (
841
+ ", ".join(legal_actions[:10])
842
+ + f" and {len(legal_actions) - 10} more options"
843
+ )
844
+ else:
845
+ action_str = ", ".join(legal_actions)
846
+
847
+ return action_str
848
+
849
+ def _format_cards(self, cards: List[str]) -> str:
850
+ r"""Format a list of cards for display.
851
+
852
+ Args:
853
+ cards (List[str]): List of card strings.
854
+
855
+ Returns:
856
+ str: Formatted card string.
857
+ """
858
+ if not cards:
859
+ return "None"
860
+ return " ".join(cards)