versionhq 1.2.1.16__tar.gz → 1.2.1.18__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.18}/PKG-INFO +1 -1
  2. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/agent/ref.md +1 -1
  3. versionhq-1.2.1.18/docs/core/agent-network/index.md +43 -0
  4. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/tool.md +1 -1
  5. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/quickstart.md +43 -42
  6. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/mkdocs.yml +6 -4
  7. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/pyproject.toml +1 -1
  8. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/__init__.py +2 -2
  9. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/_utils/process_config.py +1 -1
  10. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent/model.py +93 -90
  11. versionhq-1.2.1.18/src/versionhq/agent_network/formation.py +157 -0
  12. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent_network/model.py +7 -8
  13. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task/model.py +32 -14
  14. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq.egg-info/PKG-INFO +1 -1
  15. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq.egg-info/SOURCES.txt +2 -1
  16. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/agent_network/agent_network_test.py +28 -27
  17. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/doc_test.py +2 -2
  18. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/formation_test.py +7 -8
  19. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/memory/memory_test.py +1 -1
  20. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task/task_test.py +0 -8
  21. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/usecase_test.py +6 -1
  22. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/uv.lock +1 -1
  23. versionhq-1.2.1.16/src/versionhq/task/formation.py +0 -159
  24. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.env.sample +0 -0
  25. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.github/workflows/deploy_docs.yml +0 -0
  26. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.github/workflows/publish.yml +0 -0
  27. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.github/workflows/publish_testpypi.yml +0 -0
  28. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.github/workflows/run_tests.yml +0 -0
  29. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.github/workflows/security_check.yml +0 -0
  30. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.gitignore +0 -0
  31. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.pre-commit-config.yaml +0 -0
  32. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/.python-version +0 -0
  33. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/LICENSE +0 -0
  34. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/README.md +0 -0
  35. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/SECURITY.md +0 -0
  36. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/db/preprocess.py +0 -0
  37. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/CNAME +0 -0
  38. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/_logos/favicon.ico +0 -0
  39. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/_logos/logo192.png +0 -0
  40. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/agent/config.md +0 -0
  41. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/agent/index.md +0 -0
  42. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/agent/task-handling.md +0 -0
  43. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/llm/index.md +0 -0
  44. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/task/evaluation.md +0 -0
  45. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/task/index.md +0 -0
  46. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/task/response-field.md +0 -0
  47. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/task/task-output.md +0 -0
  48. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/core/task-graph/index.md +0 -0
  49. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/index.md +0 -0
  50. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/stylesheets/main.css +0 -0
  51. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/docs/tags.md +0 -0
  52. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/requirements-dev.txt +0 -0
  53. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/requirements.txt +0 -0
  54. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/runtime.txt +0 -0
  55. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/setup.cfg +0 -0
  56. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/_utils/__init__.py +0 -0
  57. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/_utils/i18n.py +0 -0
  58. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/_utils/logger.py +0 -0
  59. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/_utils/usage_metrics.py +0 -0
  60. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/_utils/vars.py +0 -0
  61. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent/TEMPLATES/Backstory.py +0 -0
  62. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent/TEMPLATES/__init__.py +0 -0
  63. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent/__init__.py +0 -0
  64. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent/inhouse_agents.py +0 -0
  65. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent/parser.py +0 -0
  66. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent/rpm_controller.py +0 -0
  67. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/agent_network/__init__.py +0 -0
  68. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/cli/__init__.py +0 -0
  69. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/clients/__init__.py +0 -0
  70. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/clients/customer/__init__.py +0 -0
  71. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/clients/customer/model.py +0 -0
  72. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/clients/product/__init__.py +0 -0
  73. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/clients/product/model.py +0 -0
  74. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/clients/workflow/__init__.py +0 -0
  75. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/clients/workflow/model.py +0 -0
  76. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/knowledge/__init__.py +0 -0
  77. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/knowledge/_utils.py +0 -0
  78. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/knowledge/embedding.py +0 -0
  79. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/knowledge/model.py +0 -0
  80. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/knowledge/source.py +0 -0
  81. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/knowledge/source_docling.py +0 -0
  82. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/knowledge/storage.py +0 -0
  83. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/llm/__init__.py +0 -0
  84. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/llm/llm_vars.py +0 -0
  85. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/llm/model.py +0 -0
  86. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/memory/__init__.py +0 -0
  87. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/memory/contextual_memory.py +0 -0
  88. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/memory/model.py +0 -0
  89. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/storage/__init__.py +0 -0
  90. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/storage/base.py +0 -0
  91. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/storage/ltm_sqlite_storage.py +0 -0
  92. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/storage/mem0_storage.py +0 -0
  93. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/storage/rag_storage.py +0 -0
  94. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/storage/task_output_storage.py +0 -0
  95. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/storage/utils.py +0 -0
  96. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task/TEMPLATES/Description.py +0 -0
  97. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task/__init__.py +0 -0
  98. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task/evaluate.py +0 -0
  99. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task/formatter.py +0 -0
  100. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task/log_handler.py +0 -0
  101. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task/structured_response.py +0 -0
  102. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task_graph/__init__.py +0 -0
  103. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task_graph/colors.py +0 -0
  104. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task_graph/draft.py +0 -0
  105. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/task_graph/model.py +0 -0
  106. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/tool/__init__.py +0 -0
  107. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/tool/cache_handler.py +0 -0
  108. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/tool/composio_tool.py +0 -0
  109. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/tool/composio_tool_vars.py +0 -0
  110. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/tool/decorator.py +0 -0
  111. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/tool/model.py +0 -0
  112. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq/tool/tool_handler.py +0 -0
  113. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq.egg-info/dependency_links.txt +0 -0
  114. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq.egg-info/requires.txt +0 -0
  115. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/src/versionhq.egg-info/top_level.txt +0 -0
  116. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/__init__.py +0 -0
  117. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/agent/__init__.py +0 -0
  118. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/agent/agent_test.py +0 -0
  119. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/agent/doc_test.py +0 -0
  120. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/agent_network/Prompts/Demo_test.py +0 -0
  121. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/agent_network/__init__.py +0 -0
  122. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/cli/__init__.py +0 -0
  123. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/clients/customer_test.py +0 -0
  124. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/clients/product_test.py +0 -0
  125. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/clients/workflow_test.py +0 -0
  126. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/conftest.py +0 -0
  127. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/knowledge/__init__.py +0 -0
  128. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/knowledge/knowledge_test.py +0 -0
  129. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/knowledge/mock_report_compressed.pdf +0 -0
  130. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/llm/__init__.py +0 -0
  131. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/llm/llm_test.py +0 -0
  132. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/memory/__init__.py +0 -0
  133. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task/__init__.py +0 -0
  134. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task/doc_taskoutput_test.py +0 -0
  135. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task/doc_test.py +0 -0
  136. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task/llm_connection_test.py +0 -0
  137. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task_graph/__init__.py +0 -0
  138. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task_graph/doc_test.py +0 -0
  139. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/task_graph/task_graph_test.py +0 -0
  140. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/tool/__init__.py +0 -0
  141. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/tool/composio_test.py +0 -0
  142. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/tests/tool/doc_test.py +0 -0
  143. {versionhq-1.2.1.16 → versionhq-1.2.1.18}/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.18
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.18"
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.18"
35
35
  __all__ = [
36
36
  "Agent",
37
37
 
@@ -8,7 +8,7 @@ def process_config(values_to_update: Dict[str, Any], model_class: Type[BaseModel
8
8
  Refer to the Pydantic model class for field validation.
9
9
  """
10
10
 
11
- config = values_to_update.pop("config", {})
11
+ config = values_to_update.pop("config") if "config" in values_to_update else {}
12
12
 
13
13
  if config:
14
14
  for k, v in config.items():
@@ -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")
@@ -149,94 +149,6 @@ class Agent(BaseModel):
149
149
  return self
150
150
 
151
151
 
152
- def _convert_to_llm_object(self, llm: Any = None) -> LLM:
153
- """
154
- Convert the given value to LLM object.
155
- When `llm` is dict or self.llm_config is not None, add these values to the LLM object after validating them.
156
- """
157
- llm = llm if llm else self.llm if self.llm else DEFAULT_MODEL_NAME
158
-
159
- if not llm:
160
- pass
161
-
162
- match llm:
163
- case LLM():
164
- return self._set_llm_params(llm=llm, config=self.llm_config)
165
-
166
- case str():
167
- llm_obj = LLM(model=llm)
168
- return self._set_llm_params(llm=llm_obj, config=self.llm_config)
169
-
170
- case dict():
171
- model_name = llm.pop("model_name", llm.pop("deployment_name", str(llm)))
172
- llm_obj = LLM(model=model_name if model_name else DEFAULT_MODEL_NAME)
173
- config = llm.update(self.llm_config) if self.llm_config else llm
174
- return self._set_llm_params(llm_obj, config=config)
175
-
176
- case _:
177
- model_name = (getattr(self.llm, "model_name") or getattr(self.llm, "deployment_name") or str(self.llm))
178
- llm_obj = LLM(model=model_name if model_name else DEFAULT_MODEL_NAME)
179
- llm_params = {
180
- "max_tokens": (getattr(llm, "max_tokens") or 3000),
181
- "timeout": getattr(llm, "timeout", self.max_execution_time),
182
- "callbacks": getattr(llm, "callbacks", None),
183
- "temperature": getattr(llm, "temperature", None),
184
- "logprobs": getattr(llm, "logprobs", None),
185
- "api_key": getattr(llm, "api_key", os.environ.get("LITELLM_API_KEY", None)),
186
- "base_url": getattr(llm, "base_url", None),
187
- }
188
- config = llm_params.update(self.llm_config) if self.llm_config else llm_params
189
- return self._set_llm_params(llm=llm_obj, config=config)
190
-
191
-
192
- def _set_llm_params(self, llm: LLM, config: Dict[str, Any] = None) -> LLM:
193
- """
194
- Add valid params to the LLM object.
195
- """
196
-
197
- import litellm
198
- from versionhq.llm.llm_vars import PARAMS
199
-
200
- valid_config = {k: v for k, v in config.items() if v} if config else {}
201
-
202
- if valid_config:
203
- valid_keys = list()
204
- try:
205
- valid_keys = litellm.get_supported_openai_params(model=llm.model, custom_llm_provider=self.endpoint_provider, request_type="chat_completion")
206
- if not valid_keys:
207
- valid_keys = PARAMS.get("common")
208
- except:
209
- valid_keys = PARAMS.get("common")
210
-
211
- valid_keys += PARAMS.get("litellm")
212
-
213
- for key in valid_keys:
214
- if key in valid_config and valid_config[key]:
215
- val = valid_config[key]
216
- if [key == k for k, v in LLM.model_fields.items()]:
217
- setattr(llm, key, val)
218
- else:
219
- llm.other_valid_config.update({ key: val})
220
-
221
-
222
- llm.timeout = self.max_execution_time if llm.timeout is None else llm.timeout
223
- # llm.max_tokens = self.max_tokens if self.max_tokens else llm.max_tokens
224
-
225
- if llm.provider is None:
226
- provider_name = llm.model.split("/")[0]
227
- valid_provider = provider_name if provider_name in PROVIDERS else None
228
- llm.provider = valid_provider
229
-
230
- if self.callbacks:
231
- llm.callbacks = self.callbacks
232
- llm._set_callbacks(llm.callbacks)
233
-
234
- if self.respect_context_window == False:
235
- llm.context_window_size = DEFAULT_CONTEXT_WINDOW_SIZE
236
-
237
- return llm
238
-
239
-
240
152
  @model_validator(mode="after")
241
153
  def set_up_tools(self) -> Self:
242
154
  """
@@ -369,6 +281,94 @@ class Agent(BaseModel):
369
281
  return self
370
282
 
371
283
 
284
+ def _convert_to_llm_object(self, llm: Any = None) -> LLM:
285
+ """
286
+ Convert the given value to LLM object.
287
+ When `llm` is dict or self.llm_config is not None, add these values to the LLM object after validating them.
288
+ """
289
+ llm = llm if llm else self.llm if self.llm else DEFAULT_MODEL_NAME
290
+
291
+ if not llm:
292
+ pass
293
+
294
+ match llm:
295
+ case LLM():
296
+ return self._set_llm_params(llm=llm, config=self.llm_config)
297
+
298
+ case str():
299
+ llm_obj = LLM(model=llm)
300
+ return self._set_llm_params(llm=llm_obj, config=self.llm_config)
301
+
302
+ case dict():
303
+ model_name = llm.pop("model_name", llm.pop("deployment_name", str(llm)))
304
+ llm_obj = LLM(model=model_name if model_name else DEFAULT_MODEL_NAME)
305
+ config = llm.update(self.llm_config) if self.llm_config else llm
306
+ return self._set_llm_params(llm_obj, config=config)
307
+
308
+ case _:
309
+ model_name = (getattr(self.llm, "model_name") or getattr(self.llm, "deployment_name") or str(self.llm))
310
+ llm_obj = LLM(model=model_name if model_name else DEFAULT_MODEL_NAME)
311
+ llm_params = {
312
+ "max_tokens": (getattr(llm, "max_tokens") or 3000),
313
+ "timeout": getattr(llm, "timeout", self.max_execution_time),
314
+ "callbacks": getattr(llm, "callbacks", None),
315
+ "temperature": getattr(llm, "temperature", None),
316
+ "logprobs": getattr(llm, "logprobs", None),
317
+ "api_key": getattr(llm, "api_key", os.environ.get("LITELLM_API_KEY", None)),
318
+ "base_url": getattr(llm, "base_url", None),
319
+ }
320
+ config = llm_params.update(self.llm_config) if self.llm_config else llm_params
321
+ return self._set_llm_params(llm=llm_obj, config=config)
322
+
323
+
324
+ def _set_llm_params(self, llm: LLM, config: Dict[str, Any] = None) -> LLM:
325
+ """
326
+ Add valid params to the LLM object.
327
+ """
328
+
329
+ import litellm
330
+ from versionhq.llm.llm_vars import PARAMS
331
+
332
+ valid_config = {k: v for k, v in config.items() if v} if config else {}
333
+
334
+ if valid_config:
335
+ valid_keys = list()
336
+ try:
337
+ valid_keys = litellm.get_supported_openai_params(model=llm.model, custom_llm_provider=self.endpoint_provider, request_type="chat_completion")
338
+ if not valid_keys:
339
+ valid_keys = PARAMS.get("common")
340
+ except:
341
+ valid_keys = PARAMS.get("common")
342
+
343
+ valid_keys += PARAMS.get("litellm")
344
+
345
+ for key in valid_keys:
346
+ if key in valid_config and valid_config[key]:
347
+ val = valid_config[key]
348
+ if [key == k for k, v in LLM.model_fields.items()]:
349
+ setattr(llm, key, val)
350
+ else:
351
+ llm.other_valid_config.update({ key: val})
352
+
353
+
354
+ llm.timeout = self.max_execution_time if llm.timeout is None else llm.timeout
355
+ # llm.max_tokens = self.max_tokens if self.max_tokens else llm.max_tokens
356
+
357
+ if llm.provider is None:
358
+ provider_name = llm.model.split("/")[0]
359
+ valid_provider = provider_name if provider_name in PROVIDERS else None
360
+ llm.provider = valid_provider
361
+
362
+ if self.callbacks:
363
+ llm.callbacks = self.callbacks
364
+ llm._set_callbacks(llm.callbacks)
365
+
366
+ if self.respect_context_window == False:
367
+ llm.context_window_size = DEFAULT_CONTEXT_WINDOW_SIZE
368
+
369
+ return llm
370
+
371
+
372
372
  def _update_llm(self, llm: Any = None, llm_config: Optional[Dict[str, Any]] = None) -> Self:
373
373
  """
374
374
  Update llm and llm_config of the exsiting agent. (Other conditions will remain the same.)
@@ -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)
@@ -599,3 +599,6 @@ class Agent(BaseModel):
599
599
 
600
600
  def __repr__(self):
601
601
  return f"Agent(role={self.role}, goal={self.goal}"
602
+
603
+ def __str__(self):
604
+ return super().__str__()
@@ -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().split(":")[0].replace(" ", "_")[0: 16]
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().split(":")[0].replace(" ", "_")[0: 16]
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.extend(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