versionhq 1.2.1.16__tar.gz → 1.2.1.17__tar.gz

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 (143) hide show
  1. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/PKG-INFO +1 -1
  2. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/agent/ref.md +1 -1
  3. versionhq-1.2.1.17/docs/core/agent-network/index.md +43 -0
  4. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/tool.md +1 -1
  5. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/quickstart.md +43 -42
  6. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/mkdocs.yml +6 -4
  7. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/pyproject.toml +1 -1
  8. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/__init__.py +2 -2
  9. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent/model.py +2 -2
  10. versionhq-1.2.1.17/src/versionhq/agent_network/formation.py +157 -0
  11. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent_network/model.py +7 -8
  12. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task/model.py +32 -14
  13. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq.egg-info/PKG-INFO +1 -1
  14. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq.egg-info/SOURCES.txt +2 -1
  15. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/agent_network/agent_network_test.py +28 -27
  16. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/doc_test.py +2 -2
  17. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/formation_test.py +7 -8
  18. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/memory/memory_test.py +1 -1
  19. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/usecase_test.py +6 -1
  20. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/uv.lock +1 -1
  21. versionhq-1.2.1.16/src/versionhq/task/formation.py +0 -159
  22. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.env.sample +0 -0
  23. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.github/workflows/deploy_docs.yml +0 -0
  24. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.github/workflows/publish.yml +0 -0
  25. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.github/workflows/publish_testpypi.yml +0 -0
  26. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.github/workflows/run_tests.yml +0 -0
  27. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.github/workflows/security_check.yml +0 -0
  28. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.gitignore +0 -0
  29. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.pre-commit-config.yaml +0 -0
  30. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/.python-version +0 -0
  31. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/LICENSE +0 -0
  32. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/README.md +0 -0
  33. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/SECURITY.md +0 -0
  34. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/db/preprocess.py +0 -0
  35. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/CNAME +0 -0
  36. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/_logos/favicon.ico +0 -0
  37. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/_logos/logo192.png +0 -0
  38. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/agent/config.md +0 -0
  39. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/agent/index.md +0 -0
  40. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/agent/task-handling.md +0 -0
  41. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/llm/index.md +0 -0
  42. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/task/evaluation.md +0 -0
  43. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/task/index.md +0 -0
  44. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/task/response-field.md +0 -0
  45. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/task/task-output.md +0 -0
  46. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/core/task-graph/index.md +0 -0
  47. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/index.md +0 -0
  48. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/stylesheets/main.css +0 -0
  49. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/docs/tags.md +0 -0
  50. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/requirements-dev.txt +0 -0
  51. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/requirements.txt +0 -0
  52. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/runtime.txt +0 -0
  53. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/setup.cfg +0 -0
  54. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/_utils/__init__.py +0 -0
  55. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/_utils/i18n.py +0 -0
  56. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/_utils/logger.py +0 -0
  57. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/_utils/process_config.py +0 -0
  58. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/_utils/usage_metrics.py +0 -0
  59. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/_utils/vars.py +0 -0
  60. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent/TEMPLATES/Backstory.py +0 -0
  61. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent/TEMPLATES/__init__.py +0 -0
  62. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent/__init__.py +0 -0
  63. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent/inhouse_agents.py +0 -0
  64. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent/parser.py +0 -0
  65. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent/rpm_controller.py +0 -0
  66. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/agent_network/__init__.py +0 -0
  67. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/cli/__init__.py +0 -0
  68. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/clients/__init__.py +0 -0
  69. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/clients/customer/__init__.py +0 -0
  70. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/clients/customer/model.py +0 -0
  71. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/clients/product/__init__.py +0 -0
  72. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/clients/product/model.py +0 -0
  73. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/clients/workflow/__init__.py +0 -0
  74. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/clients/workflow/model.py +0 -0
  75. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/knowledge/__init__.py +0 -0
  76. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/knowledge/_utils.py +0 -0
  77. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/knowledge/embedding.py +0 -0
  78. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/knowledge/model.py +0 -0
  79. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/knowledge/source.py +0 -0
  80. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/knowledge/source_docling.py +0 -0
  81. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/knowledge/storage.py +0 -0
  82. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/llm/__init__.py +0 -0
  83. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/llm/llm_vars.py +0 -0
  84. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/llm/model.py +0 -0
  85. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/memory/__init__.py +0 -0
  86. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/memory/contextual_memory.py +0 -0
  87. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/memory/model.py +0 -0
  88. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/storage/__init__.py +0 -0
  89. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/storage/base.py +0 -0
  90. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/storage/ltm_sqlite_storage.py +0 -0
  91. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/storage/mem0_storage.py +0 -0
  92. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/storage/rag_storage.py +0 -0
  93. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/storage/task_output_storage.py +0 -0
  94. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/storage/utils.py +0 -0
  95. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task/TEMPLATES/Description.py +0 -0
  96. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task/__init__.py +0 -0
  97. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task/evaluate.py +0 -0
  98. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task/formatter.py +0 -0
  99. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task/log_handler.py +0 -0
  100. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task/structured_response.py +0 -0
  101. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task_graph/__init__.py +0 -0
  102. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task_graph/colors.py +0 -0
  103. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task_graph/draft.py +0 -0
  104. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/task_graph/model.py +0 -0
  105. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/tool/__init__.py +0 -0
  106. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/tool/cache_handler.py +0 -0
  107. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/tool/composio_tool.py +0 -0
  108. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/tool/composio_tool_vars.py +0 -0
  109. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/tool/decorator.py +0 -0
  110. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/tool/model.py +0 -0
  111. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq/tool/tool_handler.py +0 -0
  112. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq.egg-info/dependency_links.txt +0 -0
  113. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq.egg-info/requires.txt +0 -0
  114. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/src/versionhq.egg-info/top_level.txt +0 -0
  115. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/__init__.py +0 -0
  116. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/agent/__init__.py +0 -0
  117. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/agent/agent_test.py +0 -0
  118. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/agent/doc_test.py +0 -0
  119. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/agent_network/Prompts/Demo_test.py +0 -0
  120. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/agent_network/__init__.py +0 -0
  121. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/cli/__init__.py +0 -0
  122. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/clients/customer_test.py +0 -0
  123. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/clients/product_test.py +0 -0
  124. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/clients/workflow_test.py +0 -0
  125. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/conftest.py +0 -0
  126. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/knowledge/__init__.py +0 -0
  127. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/knowledge/knowledge_test.py +0 -0
  128. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/knowledge/mock_report_compressed.pdf +0 -0
  129. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/llm/__init__.py +0 -0
  130. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/llm/llm_test.py +0 -0
  131. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/memory/__init__.py +0 -0
  132. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task/__init__.py +0 -0
  133. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task/doc_taskoutput_test.py +0 -0
  134. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task/doc_test.py +0 -0
  135. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task/llm_connection_test.py +0 -0
  136. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task/task_test.py +0 -0
  137. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task_graph/__init__.py +0 -0
  138. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task_graph/doc_test.py +0 -0
  139. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/task_graph/task_graph_test.py +0 -0
  140. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/tool/__init__.py +0 -0
  141. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/tool/composio_test.py +0 -0
  142. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/tool/doc_test.py +0 -0
  143. {versionhq-1.2.1.16 → versionhq-1.2.1.17}/tests/tool/tool_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: versionhq
3
- Version: 1.2.1.16
3
+ Version: 1.2.1.17
4
4
  Summary: An agentic orchestration framework for building agent networks that handle task automation.
5
5
  Author-email: Kuriko Iwai <kuriko@versi0n.io>
6
6
  License: MIT License
@@ -17,7 +17,7 @@
17
17
  | **`use_developer_prompt`** | bool | True | - | Whether to use the system (developer) prompt when calling the model. |
18
18
  | **`developer_promt_template`** | str | None | True | File path to the prompt template. |
19
19
  | **`user_promt_template`** | str | None | True | File path to the prompt template. |
20
- | **`network`** | [List[Any]] | None | True | Stores a list of agent networks that the agent belongs to. |
20
+ | **`networks`** | List[Any] | list() | True | Stores a list of agent networks that the agent belongs to. |
21
21
  | **`allow_delegation`** | bool | False | - | Whether the agent can delegate assinged tasks to another agent. |
22
22
  | **`max_retry_limit`** | int | 2 | - | Maximum number of retries when the task execution failed. |
23
23
  | **`maxit`** | int | 25 | - | Maximum number of total optimization loops conducted when an error occues during the task execution. |
@@ -0,0 +1,43 @@
1
+ ---
2
+ tags:
3
+ - Agent Network
4
+ ---
5
+
6
+ # Agent Network
7
+
8
+ <class>`class` versionhq.agent_network.model.<bold>AgentNetwork<bold></class>
9
+
10
+ A Pydantic class to store `AgentNetwork` objects that handle multiple agent formations for the task execution.
11
+
12
+ You can specify a desired formation or allow the agents to determine it autonomously (default).
13
+
14
+ | | **Solo Agent** | **Supervising** | **Squad** | **Random** |
15
+ | :--- | :--- | :--- | :--- | :--- |
16
+ | **Formation** | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/rbgxttfoeqqis1ettlfz.png" alt="solo" width="200"> | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/zhungor3elxzer5dum10.png" alt="solo" width="200"> | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/dnusl7iy7kiwkxwlpmg8.png" alt="solo" width="200"> | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/sndpczatfzbrosxz9ama.png" alt="solo" width="200"> |
17
+ | **Usage** | <ul><li>A single agent with tools, knowledge, and memory.</li><li>When self-learning mode is on - it will turn into **Random** formation.</li></ul> | <ul><li>Leader agent gives directions, while sharing its knowledge and memory.</li><li>Subordinates can be solo agents or networks.</li></ul> | <ul><li>Share tasks, knowledge, and memory among network members.</li></ul> | <ul><li>A single agent handles tasks, asking help from other agents without sharing its memory or knowledge.</li></ul> |
18
+ | **Use case** | An email agent drafts promo message for the given audience. | The leader agent strategizes an outbound campaign plan and assigns components such as media mix or message creation to subordinate agents. | An email agent and social media agent share the product knowledge and deploy multi-channel outbound campaign. | 1. An email agent drafts promo message for the given audience, asking insights on tones from other email agents which oversee other clusters. 2. An agent calls the external agent to deploy the campaign. |
19
+
20
+ <hr>
21
+
22
+ ## Quick Start
23
+
24
+ By default, lead agents will determine the best network formation autonomously based on the given task and its goal.
25
+
26
+ Calling `.launch()` method can start executing tasks by the network, then generate a response in text and JSON formats stored in the `TaskOutput` object.
27
+
28
+ ```python
29
+ import versionhq as vhq
30
+
31
+ network = vhq.form_agent_network(
32
+ task=f"create a promo plan to attract a client",
33
+ expected_outcome='media mix, key messages, and CTA targets.'
34
+ )
35
+
36
+ res = network.launch()
37
+
38
+ assert isinstance(res, vhq.TaskOutput)
39
+ ```
40
+
41
+ Ref. <a href="/core/task-output">TaskOutput</a> class
42
+
43
+ Visit <a href="https://versi0n.io">Playground</a>.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  tags:
3
- - Utils
3
+ - Utilities
4
4
  ---
5
5
 
6
6
  # Tool
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## Package installation
4
4
 
5
- ```
6
- pip install versionhq
7
- ```
5
+ ```
6
+ pip install versionhq
7
+ ```
8
8
 
9
9
  (Python 3.11, 3.12)
10
10
 
@@ -16,15 +16,15 @@ You can generate a network of multiple agents depending on your task complexity.
16
16
 
17
17
  Here is a code snippet:
18
18
 
19
- ```python
20
- import versionhq as vhq
19
+ ```python
20
+ import versionhq as vhq
21
21
 
22
- network = vhq.form_agent_network(
23
- task="YOUR AMAZING TASK OVERVIEW",
24
- expected_outcome="YOUR OUTCOME EXPECTATION",
25
- )
26
- res = network.launch()
27
- ```
22
+ network = vhq.form_agent_network(
23
+ task="YOUR AMAZING TASK OVERVIEW",
24
+ expected_outcome="YOUR OUTCOME EXPECTATION",
25
+ )
26
+ res = network.launch()
27
+ ```
28
28
 
29
29
  This will form a network with multiple agents on `Formation` and return results as a `TaskOutput` object, storing outputs in JSON, plane text, Pydantic model formats along with evaluation.
30
30
 
@@ -36,44 +36,45 @@ If you don't need to form a network or assign a specific agent to the network, y
36
36
  Agents can execute tasks using `Task` model and return JSON format by default with plane text and pydantic model formats as options.
37
37
 
38
38
 
39
- ```python
40
- import versionhq as vhq
41
- from pydantic import BaseModel
39
+ ```python
40
+ import versionhq as vhq
41
+ from pydantic import BaseModel
42
+
43
+ class CustomOutput(BaseModel):
44
+ test1: str
45
+ test2: list[str]
42
46
 
43
- class CustomOutput(BaseModel):
44
- test1: str
45
- test2: list[str]
47
+ def dummy_func(message: str, test1: str, test2: list[str]) -> str:
48
+ return f"""{message}: {test1}, {", ".join(test2)}"""
46
49
 
47
- def dummy_func(message: str, test1: str, test2: list[str]) -> str:
48
- return f"""{message}: {test1}, {", ".join(test2)}"""
49
50
 
51
+ agent = vhq.Agent(role="demo", goal="amazing project goal")
50
52
 
51
- agent = vhq.Agent(role="demo", goal="amazing project goal")
53
+ task = vhq.Task(
54
+ description="Amazing task",
55
+ pydantic_output=CustomOutput,
56
+ callback=dummy_func,
57
+ callback_kwargs=dict(message="Hi! Here is the result: ")
58
+ )
52
59
 
53
- task = vhq.Task(
54
- description="Amazing task",
55
- pydantic_output=CustomOutput,
56
- callback=dummy_func,
57
- callback_kwargs=dict(message="Hi! Here is the result: ")
58
- )
60
+ res = task.execute(agent=agent, context="amazing context to consider.")
59
61
 
60
- res = task.execute(agent=agent, context="amazing context to consider.")
61
- print(res)
62
- ```
62
+ assert isinstance(res, vhq.TaskOutput)
63
+ ```
63
64
 
64
65
  This will return a `TaskOutput` object that stores response in plane text, JSON, and Pydantic model: `CustomOutput` formats with a callback result, tool output (if given), and evaluation results (if given).
65
66
 
66
- ```python
67
- res == TaskOutput(
68
- task_id=UUID('<TASK UUID>'),
69
- raw='{\"test1\":\"random str\", \"test2\":[\"str item 1\", \"str item 2\", \"str item 3\"]}',
70
- json_dict={'test1': 'random str', 'test2': ['str item 1', 'str item 2', 'str item 3']},
71
- pydantic=<class '__main__.CustomOutput'>,
72
- tool_output=None,
73
- callback_output='Hi! Here is the result: random str, str item 1, str item 2, str item 3', # returned a plain text summary
74
- evaluation=None
75
- )
76
- ```
67
+ ```python
68
+ res == TaskOutput(
69
+ task_id=UUID('<TASK UUID>'),
70
+ raw='{\"test1\":\"random str\", \"test2\":[\"str item 1\", \"str item 2\", \"str item 3\"]}',
71
+ json_dict={'test1': 'random str', 'test2': ['str item 1', 'str item 2', 'str item 3']},
72
+ pydantic=<class '__main__.CustomOutput'>,
73
+ tool_output=None,
74
+ callback_output='Hi! Here is the result: random str, str item 1, str item 2, str item 3', # returned a plain text summary
75
+ evaluation=None
76
+ )
77
+ ```
77
78
 
78
79
  ## Supervising
79
80
 
@@ -106,8 +107,8 @@ network =vhq.AgentNetwork(
106
107
  res = network.launch()
107
108
 
108
109
  assert isinstance(res, vhq.NetworkOutput)
109
- assert not [item for item in task_1.processed_agents if "vhq-Delegated-Agent" == item]
110
- assert [item for item in task_1.processed_agents if "agent b" == item]
110
+ assert "agent b" in task_1.processed_agents # agent_b delegated by agent_a
111
+ assert "agent b" in task_2.processed_agents
111
112
  ```
112
113
 
113
114
  This will return a list with dictionaries with keys defined in the `ResponseField` of each task.
@@ -110,7 +110,8 @@ theme:
110
110
  nav:
111
111
  - Home: 'index.md'
112
112
  - Quick Start: 'quickstart.md'
113
- - Core:
113
+ - Agent Network:
114
+ - 'core/agent-network/index.md'
114
115
  - Agent:
115
116
  - 'core/agent/index.md'
116
117
  - Configuration: 'core/agent/config.md'
@@ -118,15 +119,16 @@ nav:
118
119
  - Reference: 'core/agent/ref.md'
119
120
  - LLM:
120
121
  - 'core/llm/index.md'
121
- - Task Graph:
122
- - 'core/task-graph/index.md'
122
+ - Task Graph:
123
+ - 'core/task-graph/index.md'
123
124
  - Task:
124
125
  - 'core/task/index.md'
125
126
  - TaskOutput: 'core/task/task-output.md'
126
127
  - ResponseField: 'core/task/response-field.md'
127
128
  - Evaluation: 'core/task/evaluation.md'
129
+ - Components:
128
130
  - Tool: 'core/tool.md'
129
- - Tags: 'tags.md'
131
+ - Archive: 'tags.md'
130
132
  - Cases:
131
133
  - Playground: https://versi0n.io/playground
132
134
  - Experiment - Agent Performance: https://github.com/versionHQ/exp-agent-performance
@@ -15,7 +15,7 @@ exclude = ["test*", "__pycache__", "*.egg-info"]
15
15
 
16
16
  [project]
17
17
  name = "versionhq"
18
- version = "1.2.1.16"
18
+ version = "1.2.1.17"
19
19
  authors = [{ name = "Kuriko Iwai", email = "kuriko@versi0n.io" }]
20
20
  description = "An agentic orchestration framework for building agent networks that handle task automation."
21
21
  readme = "README.md"
@@ -27,11 +27,11 @@ from versionhq.tool.composio_tool import ComposioHandler
27
27
  from versionhq.memory.contextual_memory import ContextualMemory
28
28
  from versionhq.memory.model import ShortTermMemory,LongTermMemory, UserMemory, MemoryItem
29
29
 
30
- from versionhq.task.formation import form_agent_network
30
+ from versionhq.agent_network.formation import form_agent_network
31
31
  from versionhq.task_graph.draft import workflow
32
32
 
33
33
 
34
- __version__ = "1.2.1.16"
34
+ __version__ = "1.2.1.17"
35
35
  __all__ = [
36
36
  "Agent",
37
37
 
@@ -91,7 +91,7 @@ class Agent(BaseModel):
91
91
  user_prompt_template: Optional[str] = Field(default=None, description="abs. file path to user prompt template")
92
92
 
93
93
  # task execution rules
94
- network: Optional[List[Any]] = Field(default=None, description="store a list of agent networks that the agent belong as a member")
94
+ networks: Optional[List[Any]] = Field(default_factory=list, description="store a list of agent networks that the agent belong as a member")
95
95
  allow_delegation: bool = Field(default=False, description="whether to delegate the task to another agent")
96
96
  max_retry_limit: int = Field(default=2, description="max. number of task retries when an error occurs")
97
97
  maxit: Optional[int] = Field(default=25, description="max. number of total optimization loops conducted when an error occurs")
@@ -567,7 +567,7 @@ class Agent(BaseModel):
567
567
 
568
568
 
569
569
  ## comment out for now
570
- # if self.network and self.network._train:
570
+ # if self.networks and self.networks._train:
571
571
  # task_prompt = self._training_handler(task_prompt=task_prompt)
572
572
  # else:
573
573
  # task_prompt = self._use_trained_data(task_prompt=task_prompt)
@@ -0,0 +1,157 @@
1
+ from typing import List, Type
2
+ from enum import Enum
3
+
4
+ from pydantic import BaseModel, create_model, Field
5
+
6
+ from versionhq.task.model import Task
7
+ from versionhq.agent.model import Agent
8
+ from versionhq.agent_network.model import AgentNetwork, Member, Formation
9
+ from versionhq.agent.inhouse_agents import vhq_formation_planner
10
+ from versionhq._utils import Logger
11
+
12
+
13
+ def form_agent_network(
14
+ task: str,
15
+ expected_outcome: str | Type[BaseModel],
16
+ agents: List[Agent] = None,
17
+ context: str = None,
18
+ formation: Type[Formation] = None
19
+ ) -> AgentNetwork | None:
20
+ """
21
+ Make a formation of agents from the given task description, expected outcome, agents (optional), and context (optional).
22
+ """
23
+
24
+ if not task:
25
+ Logger(verbose=True).log(level="error", message="Missing task description.", color="red")
26
+ return None
27
+
28
+ if not expected_outcome:
29
+ Logger(verbose=True).log(level="error", message="Missing expected outcome.", color="red")
30
+ return None
31
+
32
+ if formation:
33
+ try:
34
+ match formation:
35
+ case Formation():
36
+ pass
37
+
38
+ case str():
39
+ matched = [item for item in Formation.s_ if item == formation.upper()]
40
+ if matched:
41
+ formation = getattr(Formation, matched[0])
42
+ else:
43
+ # Formation._generate_next_value_(name=f"CUSTOM_{formation.upper()}", start=100, count=6, last_values=Formation.HYBRID.name)
44
+ Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid. We'll recreate a valid formation.", color="yellow")
45
+ formation = None
46
+
47
+ case int() | float():
48
+ formation = Formation(int(formation))
49
+
50
+ case _:
51
+ Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid. We'll recreate a valid formation.", color="yellow")
52
+ formation = None
53
+
54
+ except Exception as e:
55
+ Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid: {str(e)}. We'll recreate a formation.", color="yellow")
56
+ formation = None
57
+
58
+ # try:
59
+ prompt_formation = formation.name if formation and isinstance(formation, Formation) else f"Select the best formation to effectively execute the tasks from the given Enum sets: {str(Formation.__dict__)}."
60
+
61
+ prompt_expected_outcome = expected_outcome if isinstance(expected_outcome, str) else expected_outcome.model_dump_json()
62
+
63
+ class Outcome(BaseModel):
64
+ formation: Enum
65
+ agent_roles: list[str]
66
+ task_descriptions: list[str]
67
+ task_outcomes: list[list[str]]
68
+ leader_agent: str
69
+
70
+ vhq_task = Task(
71
+ description=f"Design a team of specialized agents to fully automate the following task and achieve the expected outcome. For each agent, define its role, task description, and expected outputs via the task with items in a list. Then specify the team formation if the formation is not given. If you think SUPERVISING or HYBRID is the best formation, include a leader_agent role, else leave the leader_agent role blank.\nTask: {str(task)}\nExpected outcome: {prompt_expected_outcome}\nFormation: {prompt_formation}",
72
+ pydantic_output=Outcome
73
+ )
74
+
75
+ if agents:
76
+ vhq_task.description += "Consider adding following agents in the formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
77
+
78
+ res = vhq_task.execute(agent=vhq_formation_planner, context=context)
79
+
80
+ formation_keys = ([k for k in Formation._member_map_.keys() if k == res.pydantic.formation.upper()]
81
+ if res.pydantic else [k for k in Formation._member_map_.keys() if k == res.json_dict["formation"].upper()])
82
+ _formation = Formation[formation_keys[0]] if formation_keys else Formation.SUPERVISING
83
+
84
+ network_tasks = []
85
+ members = []
86
+ leader = str(res.pydantic.leader_agent) if res.pydantic else str(res.json_dict["leader_agent"])
87
+
88
+ created_agents = [Agent(role=item, goal=item) for item in res.pydantic.agent_roles]
89
+ created_tasks = []
90
+
91
+ if res.pydantic:
92
+ for i, item in enumerate(res.pydantic.task_outcomes):
93
+ if len(res.pydantic.task_descriptions) > i and res.pydantic.task_descriptions[i]:
94
+ fields = {}
95
+ for ob in item:
96
+ try:
97
+ field_name = str(ob).lower().replace(" ", "_").replace(":", "_")[0: 10]
98
+ fields[field_name] = (str, Field(default=None))
99
+ except:
100
+ pass
101
+ output = create_model("Output", **fields) if fields else None
102
+ _task = Task(description=res.pydantic.task_descriptions[i], pydantic_output=output)
103
+ created_tasks.append(_task)
104
+
105
+ elif res.json_dict:
106
+ for i, item in enumerate(res["task_outcomes"]):
107
+ if len(res["task_descriptions"]) > i and res["task_descriptions"][i]:
108
+ fields = {}
109
+ for ob in item:
110
+ try:
111
+ field_name = str(ob).lower().replace(" ", "_").replace(":", "_")[0: 10]
112
+ fields[field_name] = (str, Field(default=None))
113
+ except:
114
+ pass
115
+ output = create_model("Output", **fields) if fields else None
116
+ _task = Task(description=res["task_descriptions"][i], pydantic_output=output)
117
+ created_tasks.append(_task)
118
+
119
+
120
+ if len(created_tasks) <= len(created_agents):
121
+ for i in range(len(created_tasks)):
122
+ is_manager = bool(created_agents[i].role.lower() == leader.lower())
123
+ member = Member(agent=created_agents[i], is_manager=is_manager, tasks=[created_tasks[i]])
124
+ members.append(member)
125
+
126
+ for i in range(len(created_tasks), len(created_agents)):
127
+ try:
128
+ is_manager = bool(created_agents[i].role.lower() == leader.lower())
129
+ member_w_o_task = Member(agent=created_agents[i], is_manager=is_manager)
130
+ members.append(member_w_o_task)
131
+ except:
132
+ pass
133
+
134
+ elif len(created_tasks) > len(created_agents):
135
+ for i in range(len(created_agents)):
136
+ is_manager = bool(created_agents[i].role.lower() == leader.lower())
137
+ member = Member(agent=created_agents[i], is_manager=is_manager, tasks=[created_tasks[i]])
138
+ members.append(member)
139
+
140
+ network_tasks.append(created_tasks[len(created_agents):len(created_tasks)])
141
+
142
+
143
+ if _formation == Formation.SUPERVISING and not [member for member in members if member.is_manager]:
144
+ manager = Member(agent=Agent(role=leader, goal=leader), is_manager=True)
145
+ members.append(manager)
146
+
147
+ members.sort(key=lambda x: x.is_manager == False)
148
+ network = AgentNetwork(members=members, formation=_formation, network_tasks=network_tasks)
149
+
150
+ Logger().log(level="info", message=f"Successfully created a agent network: {str(network.id)} with {len(network.members)} agents.", color="blue")
151
+
152
+ return network
153
+
154
+
155
+ # except Exception as e:
156
+ # Logger().log(level="error", message=f"Failed to create a agent network - return None. You can try with solo agent. Error: {str(e)}", color="red")
157
+ # return None
@@ -28,7 +28,6 @@ warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")
28
28
 
29
29
 
30
30
  class Formation(str, Enum):
31
- UNDEFINED = 0
32
31
  SOLO = 1
33
32
  SUPERVISING = 2
34
33
  SQUAD = 3
@@ -96,7 +95,6 @@ class AgentNetwork(BaseModel):
96
95
 
97
96
  __hash__ = object.__hash__
98
97
  _execution_span: Any = PrivateAttr()
99
- _logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=True))
100
98
  _inputs: Optional[Dict[str, Any]] = PrivateAttr(default=None)
101
99
 
102
100
  id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
@@ -167,12 +165,12 @@ class AgentNetwork(BaseModel):
167
165
  """
168
166
  if self.process == TaskHandlingProcess.HIERARCHY or self.formation == Formation.SUPERVISING:
169
167
  if not self.managers:
170
- self._logger.log(level="error", message="The process or formation created needs at least 1 manager agent.", color="red")
168
+ Logger().log(level="error", message="The process or formation created needs at least 1 manager agent.", color="red")
171
169
  raise PydanticCustomError("missing_manager", "`manager` is required when using hierarchical process.", {})
172
170
 
173
171
  ## comment out for the formation flexibilities
174
172
  # if self.managers and (self.manager_tasks is None or self.network_tasks is None):
175
- # self._logger.log(level="error", message="The manager is idling. At least 1 task needs to be assigned to the manager.", color="red")
173
+ # Logger().log(level="error", message="The manager is idling. At least 1 task needs to be assigned to the manager.", color="red")
176
174
  # raise PydanticCustomError("missing_manager_task", "manager needs to have at least one manager task or network task.", {})
177
175
 
178
176
  return self
@@ -186,10 +184,11 @@ class AgentNetwork(BaseModel):
186
184
  if self.process == TaskHandlingProcess.SEQUENT and self.network_tasks is None:
187
185
  for task in self.tasks:
188
186
  if not [member.task == task for member in self.members]:
189
- self._logger.log(level="error", message=f"The following task needs a dedicated agent to be assinged: {task.description}", color="red")
187
+ Logger().log(level="error", message=f"The following task needs a dedicated agent to be assinged: {task.description}", color="red")
190
188
  raise PydanticCustomError("missing_agent_in_task", "Sequential process error: Agent is missing the task", {})
191
189
  return self
192
190
 
191
+
193
192
  @model_validator(mode="after")
194
193
  def validate_end_with_at_most_one_async_task(self):
195
194
  """
@@ -371,7 +370,7 @@ class AgentNetwork(BaseModel):
371
370
  task_outputs = self._process_async_tasks(futures, was_replayed)
372
371
 
373
372
  if not task_outputs:
374
- self._logger.log(level="error", message="Missing task outputs.", color="red")
373
+ Logger().log(level="error", message="Missing task outputs.", color="red")
375
374
  raise ValueError("Missing task outputs")
376
375
 
377
376
  final_task_output = lead_task_output if lead_task_output is not None else task_outputs[0] #! REFINEME
@@ -399,12 +398,12 @@ class AgentNetwork(BaseModel):
399
398
  self._assign_tasks()
400
399
 
401
400
  if kwargs_pre is not None:
402
- for func in self.pre_launch_callbacks:
401
+ for func in self.pre_launch_callbacks: # signature check
403
402
  func(**kwargs_pre)
404
403
 
405
404
  for member in self.members:
406
405
  agent = member.agent
407
- agent.network = self
406
+ agent.networks.append(self)
408
407
 
409
408
  if self.step_callback:
410
409
  agent.callbacks.append(self.step_callback)
@@ -281,7 +281,7 @@ class Task(BaseModel):
281
281
 
282
282
  # executing
283
283
  execution_type: TaskExecutionType = Field(default=TaskExecutionType.SYNC)
284
- allow_delegation: bool = Field(default=False, description="ask other agents for help and run the task instead")
284
+ allow_delegation: bool = Field(default=False, description="whether to delegate the task to another agent")
285
285
  callback: Optional[Callable] = Field(default=None, description="callback to be executed after the task is completed.")
286
286
  callback_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict, description="kwargs for the callback when the callback is callable")
287
287
 
@@ -574,6 +574,36 @@ Ref. Output image: {output_formats_to_follow}
574
574
  return agent
575
575
 
576
576
 
577
+ def _select_agent_to_delegate(self, agent: Any = None) -> Any | None: # return agent object or None
578
+ """
579
+ Creates or selects an agent to delegate the given task and returns Agent object else None.
580
+ """
581
+
582
+ from versionhq.agent.model import Agent
583
+
584
+ if not self.allow_delegation:
585
+ return None
586
+
587
+ agent_to_delegate: InstanceOf[Agent] = None
588
+
589
+ if not agent:
590
+ agent_to_delegate = self._build_agent_from_task()
591
+
592
+ elif agent and not agent.networks:
593
+ agent_to_delegate = Agent(role="vhq-Delegated-Agent", goal=agent.goal, llm=agent.llm)
594
+
595
+ else:
596
+ _managers = []
597
+ _members = []
598
+ for network in agent.networks:
599
+ _managers.extend(member.agent for member in network.members if member.is_manager)
600
+ _members.extend(member.agent for member in network.members if not member.is_manager)
601
+
602
+ agent_to_delegate = _managers[0] if _managers else _members[0] if _members else Agent(role="vhq-Delegated-Agent", goal=agent.goal, llm=agent.llm)
603
+
604
+ return agent_to_delegate
605
+
606
+
577
607
  # task execution
578
608
  def execute(
579
609
  self, type: TaskExecutionType = None, agent: Optional["vhq.Agent"] = None, context: Optional[Any] = None
@@ -635,19 +665,7 @@ Ref. Output image: {output_formats_to_follow}
635
665
  task_tools.append(item)
636
666
 
637
667
  if self.allow_delegation == True:
638
- agent_to_delegate = None
639
-
640
- if hasattr(agent, "network") and isinstance(agent.network, AgentNetwork):
641
- if agent.network.managers:
642
- idling_manager_agents = [manager.agent for manager in agent.network.managers if manager.is_idling]
643
- agent_to_delegate = idling_manager_agents[0] if idling_manager_agents else agent.network.managers[0]
644
- else:
645
- peers = [member.agent for member in agent.network.members if member.is_manager == False and member.agent.id is not agent.id]
646
- if len(peers) > 0:
647
- agent_to_delegate = peers[0]
648
- else:
649
- agent_to_delegate = Agent(role="vhq-Delegated-Agent", goal=agent.goal, llm=agent.llm)
650
-
668
+ agent_to_delegate = self._select_agent_to_delegate(agent=agent)
651
669
  agent = agent_to_delegate
652
670
  self.delegations += 1
653
671
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: versionhq
3
- Version: 1.2.1.16
3
+ Version: 1.2.1.17
4
4
  Summary: An agentic orchestration framework for building agent networks that handle task automation.
5
5
  Author-email: Kuriko Iwai <kuriko@versi0n.io>
6
6
  License: MIT License
@@ -28,6 +28,7 @@ docs/core/agent/config.md
28
28
  docs/core/agent/index.md
29
29
  docs/core/agent/ref.md
30
30
  docs/core/agent/task-handling.md
31
+ docs/core/agent-network/index.md
31
32
  docs/core/llm/index.md
32
33
  docs/core/task/evaluation.md
33
34
  docs/core/task/index.md
@@ -55,6 +56,7 @@ src/versionhq/agent/rpm_controller.py
55
56
  src/versionhq/agent/TEMPLATES/Backstory.py
56
57
  src/versionhq/agent/TEMPLATES/__init__.py
57
58
  src/versionhq/agent_network/__init__.py
59
+ src/versionhq/agent_network/formation.py
58
60
  src/versionhq/agent_network/model.py
59
61
  src/versionhq/cli/__init__.py
60
62
  src/versionhq/clients/__init__.py
@@ -86,7 +88,6 @@ src/versionhq/storage/task_output_storage.py
86
88
  src/versionhq/storage/utils.py
87
89
  src/versionhq/task/__init__.py
88
90
  src/versionhq/task/evaluate.py
89
- src/versionhq/task/formation.py
90
91
  src/versionhq/task/formatter.py
91
92
  src/versionhq/task/log_handler.py
92
93
  src/versionhq/task/model.py
@@ -152,34 +152,35 @@ def test_launch_with_task_callback():
152
152
  assert "pytest" in demo_list[1]
153
153
 
154
154
 
155
- def test_delegate_in_network():
156
- """
157
- When the agent belongs to the agent network, its manager or peers are prioritized to delegete the task.
158
- """
159
-
160
- agent_a = Agent(role="agent a", goal="My amazing goals", llm=DEFAULT_MODEL_NAME)
161
- agent_b = Agent(role="agent b", goal="My amazing goals", llm=DEFAULT_MODEL_NAME)
162
- task_1 = Task(
163
- description="Analyze the client's business model.",
164
- response_fields=[ResponseField(title="test1", data_type=str, required=True),],
165
- allow_delegation=True
166
- )
167
- task_2 = Task(
168
- description="Define the cohort.",
169
- response_fields=[ResponseField(title="test1", data_type=int, required=True),],
170
- allow_delegation=False
171
- )
172
- network = AgentNetwork(
173
- members=[
174
- Member(agent=agent_a, is_manager=False, tasks=[task_1,]),
175
- Member(agent=agent_b, is_manager=False, tasks=[task_2,]),
176
- ],
177
- )
178
- res = network.launch()
155
+ # def test_delegate_in_network():
156
+ # """
157
+ # When the agent belongs to the agent network, its manager or peers are prioritized to delegete the task.
158
+ # """
159
+
160
+ # agent_a = Agent(role="agent a", goal="My amazing goals", llm=DEFAULT_MODEL_NAME)
161
+ # agent_b = Agent(role="agent b", goal="My amazing goals", llm=DEFAULT_MODEL_NAME)
162
+ # task_1 = Task(
163
+ # description="Analyze the client's business model.",
164
+ # response_fields=[ResponseField(title="test1", data_type=str, required=True),],
165
+ # allow_delegation=True
166
+ # )
167
+ # task_2 = Task(
168
+ # description="Define the cohort.",
169
+ # response_fields=[ResponseField(title="test1", data_type=int, required=True),],
170
+ # allow_delegation=False
171
+ # )
172
+ # network = AgentNetwork(
173
+ # members=[
174
+ # Member(agent=agent_a, is_manager=False, tasks=[task_1,]),
175
+ # Member(agent=agent_b, is_manager=False, tasks=[task_2,]),
176
+ # ],
177
+ # )
178
+ # res = network.launch()
179
+ #
180
+ # assert res.raw is not None
181
+ # assert res.json_dict is not None
182
+ # assert "vhq-Delegated-Agent" in task_1.processed_agents
179
183
 
180
- assert res.raw is not None
181
- assert res.json_dict is not None
182
- assert "agent b" in task_1.processed_agents
183
184
 
184
185
 
185
186
  def test_launch_with_leader():