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.
- evoagentx/__init__.py +2 -0
- evoagentx/actions/__init__.py +5 -0
- evoagentx/actions/action.py +256 -0
- evoagentx/actions/agent_generation.py +198 -0
- evoagentx/actions/code_extraction.py +276 -0
- evoagentx/actions/code_verification.py +63 -0
- evoagentx/actions/customize_action.py +508 -0
- evoagentx/actions/task_planning.py +80 -0
- evoagentx/agents/__init__.py +6 -0
- evoagentx/agents/action_agent.py +502 -0
- evoagentx/agents/agent.py +531 -0
- evoagentx/agents/agent_generator.py +23 -0
- evoagentx/agents/agent_manager.py +505 -0
- evoagentx/agents/customize_agent.py +522 -0
- evoagentx/agents/long_term_memory_agent.py +470 -0
- evoagentx/agents/task_planner.py +35 -0
- evoagentx/agents/workflow_reviewer.py +14 -0
- evoagentx/app/__init__.py +0 -0
- evoagentx/app/api.py +329 -0
- evoagentx/app/config.py +83 -0
- evoagentx/app/db.py +177 -0
- evoagentx/app/main.py +177 -0
- evoagentx/app/schemas.py +168 -0
- evoagentx/app/security.py +172 -0
- evoagentx/app/services.py +463 -0
- evoagentx/benchmark/WorfBench.py +155 -0
- evoagentx/benchmark/__init__.py +21 -0
- evoagentx/benchmark/benchmark.py +313 -0
- evoagentx/benchmark/gsm8k.py +156 -0
- evoagentx/benchmark/hotpotqa.py +109 -0
- evoagentx/benchmark/humaneval.py +227 -0
- evoagentx/benchmark/lcb_utils/__init__.py +0 -0
- evoagentx/benchmark/lcb_utils/code_execution.py +67 -0
- evoagentx/benchmark/lcb_utils/code_generation.py +149 -0
- evoagentx/benchmark/lcb_utils/evaluation.py +1133 -0
- evoagentx/benchmark/lcb_utils/test_output_prediction.py +70 -0
- evoagentx/benchmark/lcb_utils/utils.py +40 -0
- evoagentx/benchmark/livecodebench.py +153 -0
- evoagentx/benchmark/math_benchmark.py +223 -0
- evoagentx/benchmark/mbpp.py +285 -0
- evoagentx/benchmark/measures.py +282 -0
- evoagentx/benchmark/nq.py +89 -0
- evoagentx/config.py +67 -0
- evoagentx/core/__init__.py +10 -0
- evoagentx/core/base_config.py +67 -0
- evoagentx/core/callbacks.py +176 -0
- evoagentx/core/decorators.py +35 -0
- evoagentx/core/logging.py +33 -0
- evoagentx/core/message.py +140 -0
- evoagentx/core/module.py +477 -0
- evoagentx/core/module_utils.py +334 -0
- evoagentx/core/parser.py +26 -0
- evoagentx/core/registry.py +198 -0
- evoagentx/evaluators/__init__.py +3 -0
- evoagentx/evaluators/aflow_evaluator.py +101 -0
- evoagentx/evaluators/evaluator.py +516 -0
- evoagentx/hitl/__init__.py +61 -0
- evoagentx/hitl/approval_manager.py +481 -0
- evoagentx/hitl/hitl.py +50 -0
- evoagentx/hitl/hitl_gui.py +341 -0
- evoagentx/hitl/interceptor_agent.py +299 -0
- evoagentx/hitl/special_hitl_agent.py +320 -0
- evoagentx/hitl/workflow_editor.py +214 -0
- evoagentx/memory/__init__.py +5 -0
- evoagentx/memory/long_term_memory.py +276 -0
- evoagentx/memory/memory.py +198 -0
- evoagentx/memory/memory_manager.py +250 -0
- evoagentx/models/__init__.py +9 -0
- evoagentx/models/aliyun_model.py +386 -0
- evoagentx/models/base_model.py +615 -0
- evoagentx/models/litellm_model.py +188 -0
- evoagentx/models/model_configs.py +210 -0
- evoagentx/models/model_utils.py +143 -0
- evoagentx/models/openai_model.py +203 -0
- evoagentx/models/openrouter_model.py +186 -0
- evoagentx/models/siliconflow_model.py +132 -0
- evoagentx/models/siliconflow_model_cost.py +132 -0
- evoagentx/optimizers/__init__.py +6 -0
- evoagentx/optimizers/aflow_optimizer.py +303 -0
- evoagentx/optimizers/engine/__init__.py +0 -0
- evoagentx/optimizers/engine/base.py +70 -0
- evoagentx/optimizers/engine/decorators.py +94 -0
- evoagentx/optimizers/engine/registry.py +432 -0
- evoagentx/optimizers/example_optimizer.py +70 -0
- evoagentx/optimizers/mipro_optimizer.py +1598 -0
- evoagentx/optimizers/optimizer.py +45 -0
- evoagentx/optimizers/optimizer_core.py +310 -0
- evoagentx/optimizers/sew_optimizer.py +932 -0
- evoagentx/optimizers/textgrad_optimizer.py +661 -0
- evoagentx/prompts/__init__.py +3 -0
- evoagentx/prompts/agent_generator.py +375 -0
- evoagentx/prompts/code_extraction.py +39 -0
- evoagentx/prompts/code_verification.py +70 -0
- evoagentx/prompts/context_extraction.py +56 -0
- evoagentx/prompts/memory/manager.py +54 -0
- evoagentx/prompts/operators.py +74 -0
- evoagentx/prompts/optimizers/aflow_optimizer.py +68 -0
- evoagentx/prompts/optimizers/textgrad_optimizer.py +107 -0
- evoagentx/prompts/rag/graph_extract.py +51 -0
- evoagentx/prompts/rag/graph_synonym.py +11 -0
- evoagentx/prompts/rag/hyde.py +15 -0
- evoagentx/prompts/task_planner.py +339 -0
- evoagentx/prompts/template.py +566 -0
- evoagentx/prompts/tool_calling.py +118 -0
- evoagentx/prompts/utils.py +2 -0
- evoagentx/prompts/workflow/sew_optimizer.py +100 -0
- evoagentx/prompts/workflow/sew_workflow.py +113 -0
- evoagentx/prompts/workflow/workflow_editor.py +32 -0
- evoagentx/prompts/workflow/workflow_manager.py +155 -0
- evoagentx/rag/__init__.py +17 -0
- evoagentx/rag/chunkers/__init__.py +61 -0
- evoagentx/rag/chunkers/base.py +31 -0
- evoagentx/rag/chunkers/hierachical_chunker.py +152 -0
- evoagentx/rag/chunkers/semantic_chunker.py +91 -0
- evoagentx/rag/chunkers/simple_chunker.py +111 -0
- evoagentx/rag/embeddings/__init__.py +51 -0
- evoagentx/rag/embeddings/base.py +84 -0
- evoagentx/rag/embeddings/huggingface_embedding.py +130 -0
- evoagentx/rag/embeddings/ollama_embedding.py +151 -0
- evoagentx/rag/embeddings/openai_embedding.py +167 -0
- evoagentx/rag/indexings/__init__.py +61 -0
- evoagentx/rag/indexings/base.py +46 -0
- evoagentx/rag/indexings/graph_index.py +218 -0
- evoagentx/rag/indexings/summary_index.py +6 -0
- evoagentx/rag/indexings/tree_index.py +8 -0
- evoagentx/rag/indexings/vector_index.py +203 -0
- evoagentx/rag/postprocessors/__init__.py +42 -0
- evoagentx/rag/postprocessors/base.py +23 -0
- evoagentx/rag/postprocessors/simple_reranker.py +53 -0
- evoagentx/rag/rag.py +637 -0
- evoagentx/rag/rag_config.py +66 -0
- evoagentx/rag/readers/__init__.py +3 -0
- evoagentx/rag/readers/base.py +182 -0
- evoagentx/rag/retrievers/__init__.py +51 -0
- evoagentx/rag/retrievers/base.py +29 -0
- evoagentx/rag/retrievers/graph_retriever.py +224 -0
- evoagentx/rag/retrievers/vector_retriever.py +80 -0
- evoagentx/rag/schema.py +583 -0
- evoagentx/rag/transforms/graph_extract.py +244 -0
- evoagentx/rag/transforms/query/HyDE.py +102 -0
- evoagentx/rag/transforms/query/base.py +36 -0
- evoagentx/storages/__init__.py +3 -0
- evoagentx/storages/base.py +344 -0
- evoagentx/storages/db_stores/__init__.py +59 -0
- evoagentx/storages/db_stores/base.py +74 -0
- evoagentx/storages/db_stores/posgre_sql.py +0 -0
- evoagentx/storages/db_stores/sqlite.py +450 -0
- evoagentx/storages/graph_stores/__init__.py +40 -0
- evoagentx/storages/graph_stores/base.py +36 -0
- evoagentx/storages/graph_stores/neo4j.py +277 -0
- evoagentx/storages/schema.py +68 -0
- evoagentx/storages/storages_config.py +51 -0
- evoagentx/storages/vectore_stores/__init__.py +32 -0
- evoagentx/storages/vectore_stores/base.py +22 -0
- evoagentx/storages/vectore_stores/chroma.py +0 -0
- evoagentx/storages/vectore_stores/faiss.py +59 -0
- evoagentx/storages/vectore_stores/qdrant.py +0 -0
- evoagentx/tools/__init__.py +56 -0
- evoagentx/tools/browser_tool.py +1795 -0
- evoagentx/tools/browser_use.py +223 -0
- evoagentx/tools/cmd_toolkit.py +340 -0
- evoagentx/tools/database_base.py +321 -0
- evoagentx/tools/database_faiss.py +1006 -0
- evoagentx/tools/database_mongodb.py +1258 -0
- evoagentx/tools/database_postgresql.py +1203 -0
- evoagentx/tools/file_tool.py +428 -0
- evoagentx/tools/image_analysis.py +157 -0
- evoagentx/tools/images_flux_generation.py +138 -0
- evoagentx/tools/images_openai_generation.py +152 -0
- evoagentx/tools/interpreter_base.py +15 -0
- evoagentx/tools/interpreter_docker.py +467 -0
- evoagentx/tools/interpreter_python.py +472 -0
- evoagentx/tools/mcp.py +419 -0
- evoagentx/tools/request.py +96 -0
- evoagentx/tools/request_arxiv.py +382 -0
- evoagentx/tools/request_base.py +336 -0
- evoagentx/tools/rss_feed.py +363 -0
- evoagentx/tools/search_base.py +116 -0
- evoagentx/tools/search_ddgs.py +187 -0
- evoagentx/tools/search_google.py +168 -0
- evoagentx/tools/search_google_f.py +141 -0
- evoagentx/tools/search_serpapi.py +473 -0
- evoagentx/tools/search_serperapi.py +422 -0
- evoagentx/tools/search_wiki.py +169 -0
- evoagentx/tools/storage_base.py +984 -0
- evoagentx/tools/storage_file.py +565 -0
- evoagentx/tools/storage_handler.py +1139 -0
- evoagentx/tools/tool.py +113 -0
- evoagentx/utils/__init__.py +0 -0
- evoagentx/utils/aflow_utils/convergence_utils.py +119 -0
- evoagentx/utils/aflow_utils/data_utils.py +221 -0
- evoagentx/utils/aflow_utils/evaluation_utils.py +41 -0
- evoagentx/utils/aflow_utils/experience_utils.py +98 -0
- evoagentx/utils/aflow_utils/graph_utils.py +173 -0
- evoagentx/utils/mipro_utils/module_utils.py +554 -0
- evoagentx/utils/mipro_utils/register_utils.py +92 -0
- evoagentx/utils/mipro_utils/signature_utils.py +371 -0
- evoagentx/utils/sanitize.py +178 -0
- evoagentx/utils/utils.py +104 -0
- evoagentx/workflow/__init__.py +20 -0
- evoagentx/workflow/action_graph.py +141 -0
- evoagentx/workflow/controller.py +23 -0
- evoagentx/workflow/environment.py +111 -0
- evoagentx/workflow/operators.py +466 -0
- evoagentx/workflow/workflow.py +435 -0
- evoagentx/workflow/workflow_generator.py +215 -0
- evoagentx/workflow/workflow_graph.py +1225 -0
- evoagentx/workflow/workflow_manager.py +493 -0
- evoagentx-0.1.0.dist-info/METADATA +528 -0
- evoagentx-0.1.0.dist-info/RECORD +289 -0
- evoagentx-0.1.0.dist-info/WHEEL +5 -0
- evoagentx-0.1.0.dist-info/licenses/LICENSE +82 -0
- evoagentx-0.1.0.dist-info/top_level.txt +3 -0
- examples/action_agent.py +405 -0
- examples/aflow/code_generation/graph.py +30 -0
- examples/aflow/code_generation/prompt.py +5 -0
- examples/aflow/hotpotqa/graph.py +27 -0
- examples/aflow/hotpotqa/prompt.py +6 -0
- examples/aflow/math/graph.py +26 -0
- examples/aflow/math/prompt.py +5 -0
- examples/agent_with_memory_action.py +423 -0
- examples/agent_with_multiple_actions.py +461 -0
- examples/benchmark_and_evaluation.py +73 -0
- examples/customize_agent.py +524 -0
- examples/hitl/hitl_example.py +179 -0
- examples/hitl/hitl_example2.py +180 -0
- examples/hitl/hitl_multi_conversation_example.py +70 -0
- examples/mcp_agent.py +49 -0
- examples/models/lite_azure_model_test.py +36 -0
- examples/models/openrouter_example.py +160 -0
- examples/models/workflow_demo_lite_azure.py +233 -0
- examples/optimization/aflow/aflow_hotpotqa.py +96 -0
- examples/optimization/aflow/aflow_humaneval.py +64 -0
- examples/optimization/aflow/aflow_math.py +92 -0
- examples/optimization/aflow/aflow_mbpp.py +89 -0
- examples/optimization/mipro/math_mipro.py +104 -0
- examples/optimization/mipro/math_plug_and_play.py +124 -0
- examples/optimization/textgrad/hotpotqa_textgrad.py +112 -0
- examples/optimization/textgrad/math_textgrad.py +107 -0
- examples/optimization/textgrad/mbpp_textgrad.py +107 -0
- examples/rag.py +274 -0
- examples/sequential_workflow.py +94 -0
- examples/sew_optimizer.py +77 -0
- examples/tools/hello_world.py +2 -0
- examples/tools/tools_browser.py +253 -0
- examples/tools/tools_database.py +455 -0
- examples/tools/tools_files.py +382 -0
- examples/tools/tools_images.py +229 -0
- examples/tools/tools_integration.py +94 -0
- examples/tools/tools_interpreter.py +283 -0
- examples/tools/tools_search.py +416 -0
- examples/workflow/arxiv_workflow.py +89 -0
- examples/workflow/invest/catl_data_functions.py +764 -0
- examples/workflow/invest/csv_to_llm_converter.py +341 -0
- examples/workflow/invest/generate_report.py +47 -0
- examples/workflow/invest/html_report_generator.py +1636 -0
- examples/workflow/invest/stock_analysis.py +323 -0
- examples/workflow/invest/stock_chart_tools.py +541 -0
- examples/workflow/workflow_demo.py +86 -0
- examples/workflow/workflow_direction.py +86 -0
- examples/workflow_demo_with_tools.py +224 -0
- tests/__init__.py +0 -0
- tests/data/benchmark/benchmark_ex_02.py +515 -0
- tests/src/agents/test_agent.py +111 -0
- tests/src/agents/test_agent_manager.py +86 -0
- tests/src/agents/test_customize_agent.py +208 -0
- tests/src/benchmark/lcb_solutions.py +235 -0
- tests/src/benchmark/test_gsm8k.py +92 -0
- tests/src/benchmark/test_hotpotqa.py +153 -0
- tests/src/benchmark/test_humaneval.py +56 -0
- tests/src/benchmark/test_livecodebench.py +76 -0
- tests/src/benchmark/test_math.py +86 -0
- tests/src/benchmark/test_mbpp.py +66 -0
- tests/src/benchmark/test_nq.py +92 -0
- tests/src/core/test_base_config.py +29 -0
- tests/src/core/test_message.py +48 -0
- tests/src/core/test_module.py +252 -0
- tests/src/evaluator/test_evaluator.py +192 -0
- tests/src/hitl/__init__.py +1 -0
- tests/src/hitl/test_workflow_editor.py +384 -0
- tests/src/models/mock_response.py +59 -0
- tests/src/models/test_openai_model.py +50 -0
- tests/src/optimizers/test_sew_workflow_scheme.py +109 -0
- tests/src/rag/test_rag.py +441 -0
- tests/src/storages/test_storagehandler.py +513 -0
- tests/src/workflow/test_action_graph.py +81 -0
- tests/src/workflow/test_sequential_graph.py +199 -0
- tests/src/workflow/test_workflow_graph.py +263 -0
- tests/src/workflow/test_workflow_manager.py +296 -0
evoagentx/__init__.py
ADDED
|
@@ -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
|
+
|