evoagentx 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. evoagentx/__init__.py +2 -0
  2. evoagentx/actions/__init__.py +5 -0
  3. evoagentx/actions/action.py +256 -0
  4. evoagentx/actions/agent_generation.py +198 -0
  5. evoagentx/actions/code_extraction.py +276 -0
  6. evoagentx/actions/code_verification.py +63 -0
  7. evoagentx/actions/customize_action.py +508 -0
  8. evoagentx/actions/task_planning.py +80 -0
  9. evoagentx/agents/__init__.py +6 -0
  10. evoagentx/agents/action_agent.py +502 -0
  11. evoagentx/agents/agent.py +531 -0
  12. evoagentx/agents/agent_generator.py +23 -0
  13. evoagentx/agents/agent_manager.py +505 -0
  14. evoagentx/agents/customize_agent.py +522 -0
  15. evoagentx/agents/long_term_memory_agent.py +470 -0
  16. evoagentx/agents/task_planner.py +35 -0
  17. evoagentx/agents/workflow_reviewer.py +14 -0
  18. evoagentx/app/__init__.py +0 -0
  19. evoagentx/app/api.py +329 -0
  20. evoagentx/app/config.py +83 -0
  21. evoagentx/app/db.py +177 -0
  22. evoagentx/app/main.py +177 -0
  23. evoagentx/app/schemas.py +168 -0
  24. evoagentx/app/security.py +172 -0
  25. evoagentx/app/services.py +463 -0
  26. evoagentx/benchmark/WorfBench.py +155 -0
  27. evoagentx/benchmark/__init__.py +21 -0
  28. evoagentx/benchmark/benchmark.py +313 -0
  29. evoagentx/benchmark/gsm8k.py +156 -0
  30. evoagentx/benchmark/hotpotqa.py +109 -0
  31. evoagentx/benchmark/humaneval.py +227 -0
  32. evoagentx/benchmark/lcb_utils/__init__.py +0 -0
  33. evoagentx/benchmark/lcb_utils/code_execution.py +67 -0
  34. evoagentx/benchmark/lcb_utils/code_generation.py +149 -0
  35. evoagentx/benchmark/lcb_utils/evaluation.py +1133 -0
  36. evoagentx/benchmark/lcb_utils/test_output_prediction.py +70 -0
  37. evoagentx/benchmark/lcb_utils/utils.py +40 -0
  38. evoagentx/benchmark/livecodebench.py +153 -0
  39. evoagentx/benchmark/math_benchmark.py +223 -0
  40. evoagentx/benchmark/mbpp.py +285 -0
  41. evoagentx/benchmark/measures.py +282 -0
  42. evoagentx/benchmark/nq.py +89 -0
  43. evoagentx/config.py +67 -0
  44. evoagentx/core/__init__.py +10 -0
  45. evoagentx/core/base_config.py +67 -0
  46. evoagentx/core/callbacks.py +176 -0
  47. evoagentx/core/decorators.py +35 -0
  48. evoagentx/core/logging.py +33 -0
  49. evoagentx/core/message.py +140 -0
  50. evoagentx/core/module.py +477 -0
  51. evoagentx/core/module_utils.py +334 -0
  52. evoagentx/core/parser.py +26 -0
  53. evoagentx/core/registry.py +198 -0
  54. evoagentx/evaluators/__init__.py +3 -0
  55. evoagentx/evaluators/aflow_evaluator.py +101 -0
  56. evoagentx/evaluators/evaluator.py +516 -0
  57. evoagentx/hitl/__init__.py +61 -0
  58. evoagentx/hitl/approval_manager.py +481 -0
  59. evoagentx/hitl/hitl.py +50 -0
  60. evoagentx/hitl/hitl_gui.py +341 -0
  61. evoagentx/hitl/interceptor_agent.py +299 -0
  62. evoagentx/hitl/special_hitl_agent.py +320 -0
  63. evoagentx/hitl/workflow_editor.py +214 -0
  64. evoagentx/memory/__init__.py +5 -0
  65. evoagentx/memory/long_term_memory.py +276 -0
  66. evoagentx/memory/memory.py +198 -0
  67. evoagentx/memory/memory_manager.py +250 -0
  68. evoagentx/models/__init__.py +9 -0
  69. evoagentx/models/aliyun_model.py +386 -0
  70. evoagentx/models/base_model.py +615 -0
  71. evoagentx/models/litellm_model.py +188 -0
  72. evoagentx/models/model_configs.py +210 -0
  73. evoagentx/models/model_utils.py +143 -0
  74. evoagentx/models/openai_model.py +203 -0
  75. evoagentx/models/openrouter_model.py +186 -0
  76. evoagentx/models/siliconflow_model.py +132 -0
  77. evoagentx/models/siliconflow_model_cost.py +132 -0
  78. evoagentx/optimizers/__init__.py +6 -0
  79. evoagentx/optimizers/aflow_optimizer.py +303 -0
  80. evoagentx/optimizers/engine/__init__.py +0 -0
  81. evoagentx/optimizers/engine/base.py +70 -0
  82. evoagentx/optimizers/engine/decorators.py +94 -0
  83. evoagentx/optimizers/engine/registry.py +432 -0
  84. evoagentx/optimizers/example_optimizer.py +70 -0
  85. evoagentx/optimizers/mipro_optimizer.py +1598 -0
  86. evoagentx/optimizers/optimizer.py +45 -0
  87. evoagentx/optimizers/optimizer_core.py +310 -0
  88. evoagentx/optimizers/sew_optimizer.py +932 -0
  89. evoagentx/optimizers/textgrad_optimizer.py +661 -0
  90. evoagentx/prompts/__init__.py +3 -0
  91. evoagentx/prompts/agent_generator.py +375 -0
  92. evoagentx/prompts/code_extraction.py +39 -0
  93. evoagentx/prompts/code_verification.py +70 -0
  94. evoagentx/prompts/context_extraction.py +56 -0
  95. evoagentx/prompts/memory/manager.py +54 -0
  96. evoagentx/prompts/operators.py +74 -0
  97. evoagentx/prompts/optimizers/aflow_optimizer.py +68 -0
  98. evoagentx/prompts/optimizers/textgrad_optimizer.py +107 -0
  99. evoagentx/prompts/rag/graph_extract.py +51 -0
  100. evoagentx/prompts/rag/graph_synonym.py +11 -0
  101. evoagentx/prompts/rag/hyde.py +15 -0
  102. evoagentx/prompts/task_planner.py +339 -0
  103. evoagentx/prompts/template.py +566 -0
  104. evoagentx/prompts/tool_calling.py +118 -0
  105. evoagentx/prompts/utils.py +2 -0
  106. evoagentx/prompts/workflow/sew_optimizer.py +100 -0
  107. evoagentx/prompts/workflow/sew_workflow.py +113 -0
  108. evoagentx/prompts/workflow/workflow_editor.py +32 -0
  109. evoagentx/prompts/workflow/workflow_manager.py +155 -0
  110. evoagentx/rag/__init__.py +17 -0
  111. evoagentx/rag/chunkers/__init__.py +61 -0
  112. evoagentx/rag/chunkers/base.py +31 -0
  113. evoagentx/rag/chunkers/hierachical_chunker.py +152 -0
  114. evoagentx/rag/chunkers/semantic_chunker.py +91 -0
  115. evoagentx/rag/chunkers/simple_chunker.py +111 -0
  116. evoagentx/rag/embeddings/__init__.py +51 -0
  117. evoagentx/rag/embeddings/base.py +84 -0
  118. evoagentx/rag/embeddings/huggingface_embedding.py +130 -0
  119. evoagentx/rag/embeddings/ollama_embedding.py +151 -0
  120. evoagentx/rag/embeddings/openai_embedding.py +167 -0
  121. evoagentx/rag/indexings/__init__.py +61 -0
  122. evoagentx/rag/indexings/base.py +46 -0
  123. evoagentx/rag/indexings/graph_index.py +218 -0
  124. evoagentx/rag/indexings/summary_index.py +6 -0
  125. evoagentx/rag/indexings/tree_index.py +8 -0
  126. evoagentx/rag/indexings/vector_index.py +203 -0
  127. evoagentx/rag/postprocessors/__init__.py +42 -0
  128. evoagentx/rag/postprocessors/base.py +23 -0
  129. evoagentx/rag/postprocessors/simple_reranker.py +53 -0
  130. evoagentx/rag/rag.py +637 -0
  131. evoagentx/rag/rag_config.py +66 -0
  132. evoagentx/rag/readers/__init__.py +3 -0
  133. evoagentx/rag/readers/base.py +182 -0
  134. evoagentx/rag/retrievers/__init__.py +51 -0
  135. evoagentx/rag/retrievers/base.py +29 -0
  136. evoagentx/rag/retrievers/graph_retriever.py +224 -0
  137. evoagentx/rag/retrievers/vector_retriever.py +80 -0
  138. evoagentx/rag/schema.py +583 -0
  139. evoagentx/rag/transforms/graph_extract.py +244 -0
  140. evoagentx/rag/transforms/query/HyDE.py +102 -0
  141. evoagentx/rag/transforms/query/base.py +36 -0
  142. evoagentx/storages/__init__.py +3 -0
  143. evoagentx/storages/base.py +344 -0
  144. evoagentx/storages/db_stores/__init__.py +59 -0
  145. evoagentx/storages/db_stores/base.py +74 -0
  146. evoagentx/storages/db_stores/posgre_sql.py +0 -0
  147. evoagentx/storages/db_stores/sqlite.py +450 -0
  148. evoagentx/storages/graph_stores/__init__.py +40 -0
  149. evoagentx/storages/graph_stores/base.py +36 -0
  150. evoagentx/storages/graph_stores/neo4j.py +277 -0
  151. evoagentx/storages/schema.py +68 -0
  152. evoagentx/storages/storages_config.py +51 -0
  153. evoagentx/storages/vectore_stores/__init__.py +32 -0
  154. evoagentx/storages/vectore_stores/base.py +22 -0
  155. evoagentx/storages/vectore_stores/chroma.py +0 -0
  156. evoagentx/storages/vectore_stores/faiss.py +59 -0
  157. evoagentx/storages/vectore_stores/qdrant.py +0 -0
  158. evoagentx/tools/__init__.py +56 -0
  159. evoagentx/tools/browser_tool.py +1795 -0
  160. evoagentx/tools/browser_use.py +223 -0
  161. evoagentx/tools/cmd_toolkit.py +340 -0
  162. evoagentx/tools/database_base.py +321 -0
  163. evoagentx/tools/database_faiss.py +1006 -0
  164. evoagentx/tools/database_mongodb.py +1258 -0
  165. evoagentx/tools/database_postgresql.py +1203 -0
  166. evoagentx/tools/file_tool.py +428 -0
  167. evoagentx/tools/image_analysis.py +157 -0
  168. evoagentx/tools/images_flux_generation.py +138 -0
  169. evoagentx/tools/images_openai_generation.py +152 -0
  170. evoagentx/tools/interpreter_base.py +15 -0
  171. evoagentx/tools/interpreter_docker.py +467 -0
  172. evoagentx/tools/interpreter_python.py +472 -0
  173. evoagentx/tools/mcp.py +419 -0
  174. evoagentx/tools/request.py +96 -0
  175. evoagentx/tools/request_arxiv.py +382 -0
  176. evoagentx/tools/request_base.py +336 -0
  177. evoagentx/tools/rss_feed.py +363 -0
  178. evoagentx/tools/search_base.py +116 -0
  179. evoagentx/tools/search_ddgs.py +187 -0
  180. evoagentx/tools/search_google.py +168 -0
  181. evoagentx/tools/search_google_f.py +141 -0
  182. evoagentx/tools/search_serpapi.py +473 -0
  183. evoagentx/tools/search_serperapi.py +422 -0
  184. evoagentx/tools/search_wiki.py +169 -0
  185. evoagentx/tools/storage_base.py +984 -0
  186. evoagentx/tools/storage_file.py +565 -0
  187. evoagentx/tools/storage_handler.py +1139 -0
  188. evoagentx/tools/tool.py +113 -0
  189. evoagentx/utils/__init__.py +0 -0
  190. evoagentx/utils/aflow_utils/convergence_utils.py +119 -0
  191. evoagentx/utils/aflow_utils/data_utils.py +221 -0
  192. evoagentx/utils/aflow_utils/evaluation_utils.py +41 -0
  193. evoagentx/utils/aflow_utils/experience_utils.py +98 -0
  194. evoagentx/utils/aflow_utils/graph_utils.py +173 -0
  195. evoagentx/utils/mipro_utils/module_utils.py +554 -0
  196. evoagentx/utils/mipro_utils/register_utils.py +92 -0
  197. evoagentx/utils/mipro_utils/signature_utils.py +371 -0
  198. evoagentx/utils/sanitize.py +178 -0
  199. evoagentx/utils/utils.py +104 -0
  200. evoagentx/workflow/__init__.py +20 -0
  201. evoagentx/workflow/action_graph.py +141 -0
  202. evoagentx/workflow/controller.py +23 -0
  203. evoagentx/workflow/environment.py +111 -0
  204. evoagentx/workflow/operators.py +466 -0
  205. evoagentx/workflow/workflow.py +435 -0
  206. evoagentx/workflow/workflow_generator.py +215 -0
  207. evoagentx/workflow/workflow_graph.py +1225 -0
  208. evoagentx/workflow/workflow_manager.py +493 -0
  209. evoagentx-0.1.0.dist-info/METADATA +528 -0
  210. evoagentx-0.1.0.dist-info/RECORD +289 -0
  211. evoagentx-0.1.0.dist-info/WHEEL +5 -0
  212. evoagentx-0.1.0.dist-info/licenses/LICENSE +82 -0
  213. evoagentx-0.1.0.dist-info/top_level.txt +3 -0
  214. examples/action_agent.py +405 -0
  215. examples/aflow/code_generation/graph.py +30 -0
  216. examples/aflow/code_generation/prompt.py +5 -0
  217. examples/aflow/hotpotqa/graph.py +27 -0
  218. examples/aflow/hotpotqa/prompt.py +6 -0
  219. examples/aflow/math/graph.py +26 -0
  220. examples/aflow/math/prompt.py +5 -0
  221. examples/agent_with_memory_action.py +423 -0
  222. examples/agent_with_multiple_actions.py +461 -0
  223. examples/benchmark_and_evaluation.py +73 -0
  224. examples/customize_agent.py +524 -0
  225. examples/hitl/hitl_example.py +179 -0
  226. examples/hitl/hitl_example2.py +180 -0
  227. examples/hitl/hitl_multi_conversation_example.py +70 -0
  228. examples/mcp_agent.py +49 -0
  229. examples/models/lite_azure_model_test.py +36 -0
  230. examples/models/openrouter_example.py +160 -0
  231. examples/models/workflow_demo_lite_azure.py +233 -0
  232. examples/optimization/aflow/aflow_hotpotqa.py +96 -0
  233. examples/optimization/aflow/aflow_humaneval.py +64 -0
  234. examples/optimization/aflow/aflow_math.py +92 -0
  235. examples/optimization/aflow/aflow_mbpp.py +89 -0
  236. examples/optimization/mipro/math_mipro.py +104 -0
  237. examples/optimization/mipro/math_plug_and_play.py +124 -0
  238. examples/optimization/textgrad/hotpotqa_textgrad.py +112 -0
  239. examples/optimization/textgrad/math_textgrad.py +107 -0
  240. examples/optimization/textgrad/mbpp_textgrad.py +107 -0
  241. examples/rag.py +274 -0
  242. examples/sequential_workflow.py +94 -0
  243. examples/sew_optimizer.py +77 -0
  244. examples/tools/hello_world.py +2 -0
  245. examples/tools/tools_browser.py +253 -0
  246. examples/tools/tools_database.py +455 -0
  247. examples/tools/tools_files.py +382 -0
  248. examples/tools/tools_images.py +229 -0
  249. examples/tools/tools_integration.py +94 -0
  250. examples/tools/tools_interpreter.py +283 -0
  251. examples/tools/tools_search.py +416 -0
  252. examples/workflow/arxiv_workflow.py +89 -0
  253. examples/workflow/invest/catl_data_functions.py +764 -0
  254. examples/workflow/invest/csv_to_llm_converter.py +341 -0
  255. examples/workflow/invest/generate_report.py +47 -0
  256. examples/workflow/invest/html_report_generator.py +1636 -0
  257. examples/workflow/invest/stock_analysis.py +323 -0
  258. examples/workflow/invest/stock_chart_tools.py +541 -0
  259. examples/workflow/workflow_demo.py +86 -0
  260. examples/workflow/workflow_direction.py +86 -0
  261. examples/workflow_demo_with_tools.py +224 -0
  262. tests/__init__.py +0 -0
  263. tests/data/benchmark/benchmark_ex_02.py +515 -0
  264. tests/src/agents/test_agent.py +111 -0
  265. tests/src/agents/test_agent_manager.py +86 -0
  266. tests/src/agents/test_customize_agent.py +208 -0
  267. tests/src/benchmark/lcb_solutions.py +235 -0
  268. tests/src/benchmark/test_gsm8k.py +92 -0
  269. tests/src/benchmark/test_hotpotqa.py +153 -0
  270. tests/src/benchmark/test_humaneval.py +56 -0
  271. tests/src/benchmark/test_livecodebench.py +76 -0
  272. tests/src/benchmark/test_math.py +86 -0
  273. tests/src/benchmark/test_mbpp.py +66 -0
  274. tests/src/benchmark/test_nq.py +92 -0
  275. tests/src/core/test_base_config.py +29 -0
  276. tests/src/core/test_message.py +48 -0
  277. tests/src/core/test_module.py +252 -0
  278. tests/src/evaluator/test_evaluator.py +192 -0
  279. tests/src/hitl/__init__.py +1 -0
  280. tests/src/hitl/test_workflow_editor.py +384 -0
  281. tests/src/models/mock_response.py +59 -0
  282. tests/src/models/test_openai_model.py +50 -0
  283. tests/src/optimizers/test_sew_workflow_scheme.py +109 -0
  284. tests/src/rag/test_rag.py +441 -0
  285. tests/src/storages/test_storagehandler.py +513 -0
  286. tests/src/workflow/test_action_graph.py +81 -0
  287. tests/src/workflow/test_sequential_graph.py +199 -0
  288. tests/src/workflow/test_workflow_graph.py +263 -0
  289. tests/src/workflow/test_workflow_manager.py +296 -0
evoagentx/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = '0.1.0'
@@ -0,0 +1,5 @@
1
+ from .action import Action, ActionInput, ActionOutput
2
+ from .code_verification import CodeVerification
3
+ from .code_extraction import CodeExtraction
4
+
5
+ __all__ = ["Action", "ActionInput", "ActionOutput", "CodeVerification", "CodeExtraction"]
@@ -0,0 +1,256 @@
1
+ import json
2
+ from pydantic import model_validator
3
+ from pydantic_core import PydanticUndefined
4
+ from typing import Optional, Type, Tuple, Union, List, Any
5
+
6
+ from ..core.module import BaseModule
7
+ from ..core.module_utils import get_type_name
8
+ from ..core.registry import MODULE_REGISTRY
9
+ # from ..core.base_config import Parameter
10
+ from ..core.parser import Parser
11
+ from ..core.message import Message
12
+ from ..models.base_model import BaseLLM, LLMOutputParser
13
+ from ..tools.tool import Toolkit
14
+ from ..prompts.context_extraction import CONTEXT_EXTRACTION
15
+ from ..prompts.template import PromptTemplate
16
+
17
+
18
+ class ActionInput(LLMOutputParser):
19
+ """Input specification and parsing for actions.
20
+
21
+ This class defines the input requirements for actions and provides methods
22
+ to generate structured input specifications. It inherits from LLMOutputParser
23
+ to allow parsing of LLM outputs into structured inputs for actions.
24
+
25
+ Notes:
26
+ Parameters in ActionInput should be defined in Pydantic Field format.
27
+ For optional variables, use format:
28
+ var: Optional[int] = Field(default=None, description="xxx")
29
+ Remember to add `default=None` for optional parameters.
30
+ """
31
+
32
+ @classmethod
33
+ def get_input_specification(cls, ignore_fields: List[str] = []) -> str:
34
+ """Generate a JSON specification of the input requirements.
35
+
36
+ Examines the class fields and produces a structured specification of
37
+ the input parameters, including their types, descriptions, and whether
38
+ they are required.
39
+
40
+ Args:
41
+ ignore_fields (List[str]): List of field names to exclude from the specification.
42
+
43
+ Returns:
44
+ A JSON string containing the input specification, or an empty string
45
+ if no fields are defined or all are ignored.
46
+ """
47
+ fields_info = {}
48
+ attrs = cls.get_attrs()
49
+ for field_name, field_info in cls.model_fields.items():
50
+ if field_name in ignore_fields:
51
+ continue
52
+ if field_name not in attrs:
53
+ continue
54
+ field_type = get_type_name(field_info.annotation)
55
+ field_desc = field_info.description if field_info.description is not None else None
56
+ # field_required = field_info.is_required()
57
+ field_default = str(field_info.default) if field_info.default is not PydanticUndefined else None
58
+ field_required = True if field_default is None else False
59
+ description = field_type + ", "
60
+ if field_desc is not None:
61
+ description += (field_desc.strip() + ", ")
62
+ description += ("required" if field_required else "optional")
63
+ if field_default is not None:
64
+ description += (", Default value: " + field_default)
65
+ fields_info[field_name] = description
66
+
67
+ if len(fields_info) == 0:
68
+ return ""
69
+ fields_info_str = json.dumps(fields_info, indent=4)
70
+ return fields_info_str
71
+
72
+ @classmethod
73
+ def get_required_input_names(cls) -> List[str]:
74
+ """Get a list of all required input parameter names.
75
+
76
+ Returns:
77
+ List[str]: Names of all parameters that are required (don't have default values).
78
+ """
79
+ required_fields = []
80
+ attrs = cls.get_attrs()
81
+ for field_name, field_info in cls.model_fields.items():
82
+ if field_name not in attrs:
83
+ continue
84
+ field_default = field_info.default
85
+ # A field is required if it doesn't have a default value
86
+ if field_default is PydanticUndefined:
87
+ required_fields.append(field_name)
88
+ return required_fields
89
+
90
+
91
+ class ActionOutput(LLMOutputParser):
92
+ """Output representation for actions.
93
+
94
+ This class handles the structured output of actions, providing methods
95
+ to convert the output to structured data. It inherits from LLMOutputParser
96
+ to support parsing of LLM outputs into structured action results.
97
+ """
98
+
99
+ def to_str(self) -> str:
100
+ """Convert the output to a formatted JSON string.
101
+
102
+ Returns:
103
+ A pretty-printed JSON string representation of the structured data.
104
+ """
105
+ return json.dumps(self.get_structured_data(), indent=4)
106
+
107
+
108
+ class Action(BaseModule):
109
+ """Base class for all actions in the EvoAgentX framework.
110
+
111
+ Actions represent discrete operations that can be performed by agents.
112
+ They define inputs, outputs, and execution behavior, and can optionally
113
+ use tools to accomplish their tasks.
114
+
115
+ Attributes:
116
+ name (str): Unique identifier for the action.
117
+ description (str): Human-readable description of what the action does.
118
+ prompt (Optional[str]): Optional prompt template for this action.
119
+ tools (Optional[List[Toolkit]]): Optional list of tools that can be used by this action.
120
+ inputs_format (Optional[Type[ActionInput]]): Optional class defining the expected input structure.
121
+ outputs_format (Optional[Type[Parser]]): Optional class defining the expected output structure.
122
+ """
123
+
124
+ name: str
125
+ description: str
126
+ prompt: Optional[str] = None
127
+ prompt_template: Optional[PromptTemplate] = None
128
+ tools: Optional[List[Toolkit]] = None # specify the possible tool for the action
129
+ inputs_format: Optional[Type[ActionInput]] = None # specify the input format of the action
130
+ outputs_format: Optional[Type[Parser]] = None # specify the possible structured output format
131
+
132
+ def init_module(self):
133
+ """Initialize the action module.
134
+
135
+ This method is called after the action is instantiated.
136
+ Subclasses can override this to perform custom initialization.
137
+ """
138
+ pass
139
+
140
+ def to_dict(self, exclude_none: bool = True, ignore: List[str] = [], **kwargs) -> dict:
141
+ """
142
+ Convert the action to a dictionary for saving.
143
+ """
144
+ data = super().to_dict(exclude_none=exclude_none, ignore=ignore, **kwargs)
145
+ if self.inputs_format:
146
+ data["inputs_format"] = self.inputs_format.__name__
147
+ if self.outputs_format:
148
+ data["outputs_format"] = self.outputs_format.__name__
149
+ # TODO: customize serialization for the tools
150
+ return data
151
+
152
+ @model_validator(mode="before")
153
+ @classmethod
154
+ def validate_data(cls, data: Any) -> Any:
155
+ if "inputs_format" in data and data["inputs_format"] and isinstance(data["inputs_format"], str):
156
+ # only used when loading from a file
157
+ data["inputs_format"] = MODULE_REGISTRY.get_module(data["inputs_format"])
158
+ if "outputs_format" in data and data["outputs_format"] and isinstance(data["outputs_format"], str):
159
+ # only used when loading from a file
160
+ data["outputs_format"] = MODULE_REGISTRY.get_module(data["outputs_format"])
161
+ # TODO: customize loading for the tools
162
+ return data
163
+
164
+ def execute(self, llm: Optional[BaseLLM] = None, inputs: Optional[dict] = None, sys_msg: Optional[str]=None, return_prompt: bool = False, **kwargs) -> Optional[Union[Parser, Tuple[Parser, str]]]:
165
+ """Execute the action to produce a result.
166
+
167
+ This is the main entry point for executing an action. Subclasses must
168
+ implement this method to define the action's behavior.
169
+
170
+ Args:
171
+ llm (Optional[BaseLLM]): The LLM used to execute the action.
172
+ inputs (Optional[dict]): Input data for the action execution. The input data should be a dictionary that matches the input format of the provided prompt.
173
+ For example, if the prompt contains a variable `{input_var}`, the `inputs` dictionary should have a key `input_var`, otherwise the variable will be set to empty string.
174
+ sys_msg (Optional[str]): Optional system message for the LLM.
175
+ return_prompt (bool): Whether to return the complete prompt passed to the LLM.
176
+ **kwargs (Any): Additional keyword arguments for the execution.
177
+
178
+ Returns:
179
+ If `return_prompt` is False, the method returns a Parser object containing the structured result of the action.
180
+ If `return_prompt` is True, the method returns a tuple containing the Parser object and the complete prompt passed to the LLM.
181
+ """
182
+ raise NotImplementedError(f"`execute` function of {type(self).__name__} is not implemented!")
183
+
184
+ async def async_execute(self, llm: Optional[BaseLLM] = None, inputs: Optional[dict] = None, sys_msg: Optional[str]=None, return_prompt: bool = False, **kwargs) -> Optional[Union[Parser, Tuple[Parser, str]]]:
185
+ """
186
+ Asynchronous execution of the action.
187
+
188
+ This method is the asynchronous counterpart of the `execute` method.
189
+ It allows the action to be executed asynchronously using an LLM.
190
+ """
191
+ raise NotImplementedError(f"`async_execute` function of {type(self).__name__} is not implemented!")
192
+
193
+ class ContextExtraction(Action):
194
+ """Action for extracting structured inputs from context.
195
+
196
+ This action analyzes a conversation context to extract relevant information
197
+ that can be used as inputs for other actions. It uses the LLM to interpret
198
+ unstructured contextual information and format it according to the target
199
+ action's input requirements.
200
+ """
201
+
202
+ def __init__(self, **kwargs):
203
+ name = kwargs.pop("name") if "name" in kwargs else CONTEXT_EXTRACTION["name"]
204
+ description = kwargs.pop("description") if "description" in kwargs else CONTEXT_EXTRACTION["description"]
205
+ super().__init__(name=name, description=description, **kwargs)
206
+
207
+ def get_context_from_messages(self, messages: List[Message]) -> str:
208
+ str_context = "\n\n".join([str(msg) for msg in messages])
209
+ return str_context
210
+
211
+ def execute(self, llm: Optional[BaseLLM] = None, action: Action = None, context: List[Message] = None, **kwargs) -> Union[dict, None]:
212
+ """Extract structured inputs for an action from conversation context.
213
+
214
+ This method uses the LLM to analyze the conversation context and extract
215
+ information that matches the input requirements of the target action.
216
+
217
+ Args:
218
+ llm: The language model to use for extraction.
219
+ action: The target action whose input requirements (`inputs_format`) define what to extract.
220
+ context: List of messages providing the conversation context.
221
+ **kwargs: Additional keyword arguments.
222
+
223
+ Returns:
224
+ A dictionary containing the extracted inputs for the target action,
225
+ or None if extraction is not possible (e.g., if the action doesn't
226
+ require inputs or if context is missing).
227
+ """
228
+ if action is None or context is None:
229
+ return None
230
+
231
+ action_inputs_cls: Type[ActionInput] = action.inputs_format
232
+ if action_inputs_cls is None:
233
+ # the action does not require inputs
234
+ return None
235
+
236
+ action_inputs_desc = action_inputs_cls.get_input_specification()
237
+ str_context = self.get_context_from_messages(messages=context)
238
+
239
+ if not action_inputs_desc or not str_context:
240
+ return None
241
+
242
+ prompt = CONTEXT_EXTRACTION["prompt"].format(
243
+ context=str_context,
244
+ action_name=action.name,
245
+ action_description=action.description,
246
+ action_inputs=action_inputs_desc
247
+ )
248
+
249
+ action_inputs = llm.generate(
250
+ prompt=prompt,
251
+ system_message=CONTEXT_EXTRACTION["system_prompt"],
252
+ parser=action_inputs_cls
253
+ )
254
+ action_inputs_data = action_inputs.get_structured_data()
255
+
256
+ return action_inputs_data
@@ -0,0 +1,198 @@
1
+ import re
2
+ from pydantic import Field, model_validator
3
+ from typing import Optional, List
4
+
5
+ from ..core.logging import logger
6
+ from ..core.module import BaseModule
7
+ from ..core.base_config import Parameter
8
+ from ..models.base_model import BaseLLM
9
+ from .action import Action, ActionInput, ActionOutput
10
+ from ..prompts.agent_generator import AGENT_GENERATION_ACTION
11
+ from ..prompts.tool_calling import AGENT_GENERATION_TOOLS_PROMPT
12
+ from ..utils.utils import normalize_text
13
+
14
+ class AgentGenerationInput(ActionInput):
15
+ """
16
+ Input specification for the agent generation action.
17
+ """
18
+
19
+ goal: str = Field(description="A detailed statement of the workflow's goal, explaining the objectives the entire workflow aims to achieve")
20
+ workflow: str = Field(description="An overview of the entire workflow, detailing all sub-tasks with their respective names, descriptions, inputs, and outputs")
21
+ task: str = Field(description="A detailed JSON representation of the sub-task requiring agent generation. It should include the task's name, description, inputs, and outputs.")
22
+
23
+ history: Optional[str] = Field(default=None, description="Optional field containing previously selected or generated agents.")
24
+ suggestion: Optional[str] = Field(default=None, description="Optional suggestions to refine the generated agents.")
25
+ existing_agents: Optional[str] = Field(default=None, description="Optional field containing the description of predefined agents, including each agent's name, role, and available actions.")
26
+ tools: Optional[str] = Field(default=None, description="Optional field containing the description of tools that agents can use, including each tool's name and functionality.")
27
+
28
+
29
+ class GeneratedAgent(BaseModule):
30
+ """
31
+ Representation of a generated agent with validation capabilities.
32
+ """
33
+
34
+ name: str
35
+ description: str
36
+ inputs: List[Parameter]
37
+ outputs: List[Parameter]
38
+ prompt: str
39
+ tool_names: Optional[List[str]] = None
40
+
41
+ @classmethod
42
+ def find_output_name(cls, text: str, outputs: List[str]):
43
+ def sim(t1: str, t2: str):
44
+ t1_words = normalize_text(t1).split()
45
+ t2_words = normalize_text(t2).split()
46
+ return len(set(t1_words)&set(t2_words))
47
+
48
+ similarities = [sim(text, output) for output in outputs]
49
+ max_sim = max(similarities)
50
+ return outputs[similarities.index(max_sim)]
51
+
52
+ @model_validator(mode="after")
53
+ @classmethod
54
+ def validate_prompt(cls, agent: 'GeneratedAgent'):
55
+ """Validate and fix the agent's prompt template.
56
+
57
+ This validator ensures that:
58
+ 1. All input parameters are properly referenced in the prompt
59
+ 2. Input references use the correct format with braces
60
+ 3. All output sections match the defined output parameters
61
+
62
+ If there are mismatches in the output sections, it attempts to
63
+ fix them by finding the most similar output name.
64
+
65
+ Args:
66
+ agent: The GeneratedAgent instance to validate.
67
+
68
+ Returns:
69
+ The validated and potentially modified GeneratedAgent.
70
+
71
+ Raises:
72
+ ValueError: If inputs are missing from the prompt or output sections don't match the defined outputs.
73
+ """
74
+ # check whether all the inputs are present in the prompt
75
+ input_names = [inp.name for inp in agent.inputs]
76
+ prompt_has_inputs = [name in agent.prompt for name in input_names]
77
+ if not all(prompt_has_inputs):
78
+ missing_input_names = [name for name, has_input in zip(input_names, prompt_has_inputs) if not has_input]
79
+ raise ValueError(f'The prompt miss inputs: {missing_input_names}')
80
+
81
+ # check the format of the prompt to make sure it is wrapped in brackets.
82
+ pattern = r"### Instructions(.*?)### Output Format"
83
+ prompt = agent.prompt
84
+
85
+ def replace_with_braces(match):
86
+ instructions = match.group(1)
87
+ for name in input_names:
88
+ instructions = re.sub(fr'<input>{{*\b{re.escape(name)}\b}}*</input>', fr'<input>{{{name}}}</input>', instructions)
89
+ return "### Instructions" + instructions + "### Output Format"
90
+
91
+ modified_prompt = re.sub(pattern, replace_with_braces, prompt, flags=re.DOTALL)
92
+ agent.prompt = modified_prompt
93
+
94
+ # check whether all the outputs are present in the prompt
95
+ prompt = agent.prompt
96
+ pattern = r"### Output Format(.*)"
97
+ outputs_names = [out.name for out in agent.outputs]
98
+
99
+ def fix_output_names(match):
100
+ output_format = match.group(1)
101
+ matches = re.findall(r"## ([^\n#]+)", output_format, flags=re.DOTALL)
102
+ generated_outputs = [m.strip() for m in matches if m.strip() != "Thought"]
103
+ # check the number of generated outputs and agent outputs
104
+ if len(generated_outputs) != len(outputs_names):
105
+ raise ValueError(f"The number of outputs in the prompt is different from that defined in the `outputs` field of the agent. The outputs in the prompt are: {generated_outputs}, while the outputs from the agent's `outputs` field are: {outputs_names}")
106
+ # check whether the generated output names are the same as agent outputs
107
+ for generated_output in generated_outputs:
108
+ if generated_output not in outputs_names:
109
+ most_similar_output_name = cls.find_output_name(text=generated_output, outputs=outputs_names)
110
+ output_format = output_format.replace(generated_output, most_similar_output_name)
111
+ logger.warning(f"Couldn't find output name in prompt ('{generated_output}') in agent's outputs. Replace it with the most similar agent output: '{most_similar_output_name}'")
112
+ return "### Output Format" + output_format
113
+
114
+ modified_prompt = re.sub(pattern, fix_output_names, prompt, flags=re.DOTALL)
115
+ agent.prompt = modified_prompt
116
+
117
+ return agent
118
+
119
+
120
+ class AgentGenerationOutput(ActionOutput):
121
+
122
+ selected_agents: List[str] = Field(description="A list of selected agent's names")
123
+ generated_agents: List[GeneratedAgent] = Field(description="A list of generated agetns to address a sub-task")
124
+
125
+
126
+ class AgentGeneration(Action):
127
+ """
128
+ Action for generating agent specifications for workflow tasks.
129
+
130
+ This action analyzes task requirements and generates appropriate agent
131
+ specifications, including their prompts, inputs, and outputs. It can either
132
+ select from existing agents or create new ones tailored to the task.
133
+ """
134
+
135
+ def __init__(self, **kwargs):
136
+ name = kwargs.pop("name") if "name" in kwargs else AGENT_GENERATION_ACTION["name"]
137
+ description = kwargs.pop("description") if "description" in kwargs else AGENT_GENERATION_ACTION["description"]
138
+ prompt = kwargs.pop("prompt") if "prompt" in kwargs else AGENT_GENERATION_ACTION["prompt"]
139
+ # inputs_format = kwargs.pop("inputs_format") if "inputs_format" in kwargs else AgentGenerationInput
140
+ # outputs_format = kwargs.pop("outputs_format") if "outputs_format" in kwargs else AgentGenerationOutput
141
+ inputs_format = kwargs.pop("inputs_format", None) or AgentGenerationInput
142
+ outputs_format = kwargs.pop("outputs_format", None) or AgentGenerationOutput
143
+ tools = kwargs.pop("tools", None)
144
+ super().__init__(name=name, description=description, prompt=prompt, inputs_format=inputs_format, outputs_format=outputs_format, **kwargs)
145
+ self.tools = tools
146
+
147
+ def execute(self, llm: Optional[BaseLLM] = None, inputs: Optional[dict] = None, sys_msg: Optional[str]=None, return_prompt: bool = False, **kwargs) -> AgentGenerationOutput:
148
+ """Execute the agent generation process.
149
+
150
+ This method uses the provided language model to generate agent specifications
151
+ based on the workflow context and task requirements.
152
+
153
+ Args:
154
+ llm: The language model to use for generation.
155
+ inputs: Input data containing workflow and task information.
156
+ sys_msg: Optional system message for the language model.
157
+ return_prompt: Whether to return both the generated agents and the prompt used.
158
+ **kwargs: Additional keyword arguments.
159
+
160
+ Returns:
161
+ If return_prompt is False (default): The generated agents output.
162
+ If return_prompt is True: A tuple of (generated agents, prompt used).
163
+
164
+ Raises:
165
+ ValueError: If the inputs are None or empty.
166
+ """
167
+ if not inputs:
168
+ logger.error("AgentGeneration action received invalid `inputs`: None or empty.")
169
+ raise ValueError('The `inputs` to AgentGeneration action is None or empty.')
170
+
171
+ inputs_format: AgentGenerationInput = self.inputs_format
172
+ outputs_format: AgentGenerationOutput = self.outputs_format
173
+
174
+ prompt_params_names = inputs_format.get_attrs()
175
+ prompt_params_values = {param: inputs.get(param, "") for param in prompt_params_names}
176
+ if self.tools:
177
+ tool_description = [
178
+ {
179
+ tool.name: [
180
+ s["function"]["description"] for s in tool.get_tool_schemas()
181
+ ],
182
+ }
183
+ for tool in self.tools
184
+ ]
185
+ prompt_params_values["tools"] = AGENT_GENERATION_TOOLS_PROMPT.format(tools_description=tool_description)
186
+ prompt = self.prompt.format(**prompt_params_values)
187
+ agents = llm.generate(
188
+ prompt = prompt,
189
+ system_message = sys_msg,
190
+ parser=outputs_format,
191
+ parse_mode="json"
192
+ )
193
+
194
+ if return_prompt:
195
+ return agents, prompt
196
+
197
+ return agents
198
+