versionhq 1.2.2.2__tar.gz → 1.2.2.3__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.
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/PKG-INFO +1 -1
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/pyproject.toml +1 -1
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/__init__.py +4 -2
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent_network/model.py +7 -3
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task/evaluation.py +1 -1
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task_graph/model.py +73 -17
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq.egg-info/PKG-INFO +1 -1
- versionhq-1.2.2.3/tests/task_graph/task_graph_test.py +88 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/uv.lock +1 -1
- versionhq-1.2.2.2/tests/task_graph/task_graph_test.py +0 -27
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.env.sample +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.github/workflows/deploy_docs.yml +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.github/workflows/publish.yml +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.github/workflows/publish_testpypi.yml +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.github/workflows/run_tests.yml +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.github/workflows/security_check.yml +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.gitignore +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.pre-commit-config.yaml +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/.python-version +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/LICENSE +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/README.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/SECURITY.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/db/preprocess.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/CNAME +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/_logos/favicon.ico +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/_logos/logo192.png +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/agent/config.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/agent/index.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/agent/task-handling.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/agent-network/config.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/agent-network/form.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/agent-network/index.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/agent-network/ref.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/llm/index.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task/evaluation.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task/index.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task/response-field.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task/task-execution.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task/task-output.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task/task-ref.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task/task-strc-response.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/task-graph/index.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/core/tool.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/index.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/quickstart.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/stylesheets/main.css +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/docs/tags.md +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/mkdocs.yml +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/requirements-dev.txt +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/requirements.txt +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/runtime.txt +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/setup.cfg +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/_utils/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/_utils/i18n.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/_utils/logger.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/_utils/process_config.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/_utils/usage_metrics.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/_utils/vars.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent/TEMPLATES/Backstory.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent/TEMPLATES/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent/inhouse_agents.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent/parser.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent/rpm_controller.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent_network/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/agent_network/formation.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/cli/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/clients/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/clients/customer/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/clients/customer/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/clients/product/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/clients/product/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/clients/workflow/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/clients/workflow/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/knowledge/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/knowledge/_utils.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/knowledge/embedding.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/knowledge/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/knowledge/source.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/knowledge/source_docling.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/knowledge/storage.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/llm/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/llm/llm_vars.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/llm/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/memory/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/memory/contextual_memory.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/memory/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/storage/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/storage/base.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/storage/ltm_sqlite_storage.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/storage/mem0_storage.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/storage/rag_storage.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/storage/task_output_storage.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/storage/utils.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task/TEMPLATES/Description.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task/formatter.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task/structured_response.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task_graph/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task_graph/colors.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/task_graph/draft.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/tool/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/tool/cache_handler.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/tool/composio_tool.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/tool/composio_tool_vars.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/tool/decorator.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/tool/model.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq/tool/tool_handler.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq.egg-info/SOURCES.txt +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq.egg-info/dependency_links.txt +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq.egg-info/requires.txt +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/src/versionhq.egg-info/top_level.txt +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/agent/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/agent/agent_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/agent/doc_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/agent_network/Prompts/Demo_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/agent_network/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/agent_network/agent_network_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/agent_network/doc_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/cli/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/clients/customer_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/clients/product_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/clients/workflow_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/conftest.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/doc_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/formation_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/knowledge/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/knowledge/knowledge_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/knowledge/mock_report_compressed.pdf +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/llm/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/llm/llm_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/memory/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/memory/memory_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task/doc_taskoutput_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task/doc_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task/eval_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task/llm_connection_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task/task_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task_graph/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/task_graph/doc_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/tool/__init__.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/tool/composio_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/tool/doc_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/tool/tool_test.py +0 -0
- {versionhq-1.2.2.2 → versionhq-1.2.2.3}/tests/usecase_test.py +0 -0
@@ -15,7 +15,7 @@ exclude = ["test*", "__pycache__", "*.egg-info"]
|
|
15
15
|
|
16
16
|
[project]
|
17
17
|
name = "versionhq"
|
18
|
-
version = "1.2.2.
|
18
|
+
version = "1.2.2.3"
|
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"
|
@@ -17,7 +17,7 @@ from versionhq.clients.workflow.model import MessagingWorkflow, MessagingCompone
|
|
17
17
|
from versionhq.knowledge.model import Knowledge, KnowledgeStorage
|
18
18
|
from versionhq.knowledge.source import PDFKnowledgeSource, CSVKnowledgeSource, JSONKnowledgeSource, TextFileKnowledgeSource, ExcelKnowledgeSource, StringKnowledgeSource
|
19
19
|
from versionhq.knowledge.source_docling import DoclingSource
|
20
|
-
from versionhq.task_graph.model import TaskStatus, TaskGraph, Node, Edge, DependencyType
|
20
|
+
from versionhq.task_graph.model import TaskStatus, TaskGraph, Node, Edge, DependencyType, Condition, ConditionType
|
21
21
|
from versionhq.task.model import Task, TaskOutput, ResponseField, TaskExecutionType
|
22
22
|
from versionhq.task.evaluation import Evaluation, EvaluationItem
|
23
23
|
from versionhq.tool.model import Tool, ToolSet
|
@@ -31,7 +31,7 @@ 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.2.
|
34
|
+
__version__ = "1.2.2.3"
|
35
35
|
__all__ = [
|
36
36
|
"Agent",
|
37
37
|
|
@@ -67,6 +67,8 @@ __all__ = [
|
|
67
67
|
"Node",
|
68
68
|
"Edge",
|
69
69
|
"DependencyType",
|
70
|
+
"Condition",
|
71
|
+
"ConditionType",
|
70
72
|
|
71
73
|
"Task",
|
72
74
|
"TaskOutput",
|
@@ -13,7 +13,7 @@ from pydantic_core import PydanticCustomError, core_schema
|
|
13
13
|
|
14
14
|
from versionhq.agent.model import Agent
|
15
15
|
from versionhq.task.model import Task, TaskOutput, TaskExecutionType, ResponseField
|
16
|
-
from versionhq.task_graph.model import TaskGraph, Node, Edge, TaskStatus, DependencyType
|
16
|
+
from versionhq.task_graph.model import TaskGraph, Node, Edge, TaskStatus, DependencyType, Condition
|
17
17
|
from versionhq._utils.logger import Logger
|
18
18
|
# from versionhq.recording.usage_metrics import UsageMetrics
|
19
19
|
|
@@ -81,7 +81,7 @@ class AgentNetwork(BaseModel):
|
|
81
81
|
# task execution rules
|
82
82
|
prompt_file: str = Field(default="", description="absolute file path to the prompt file that stores jsonified prompts")
|
83
83
|
process: TaskHandlingProcess = Field(default=TaskHandlingProcess.SEQUENTIAL)
|
84
|
-
consent_trigger: Optional[Callable] = Field(default=None, description="returns bool")
|
84
|
+
consent_trigger: Optional[Callable | Condition] = Field(default=None, description="returns bool")
|
85
85
|
|
86
86
|
# callbacks
|
87
87
|
pre_launch_callbacks: List[Callable[..., Any]]= Field(default_factory=list, description="list of callback funcs called before the network launch")
|
@@ -111,6 +111,9 @@ class AgentNetwork(BaseModel):
|
|
111
111
|
Logger().log(level="error", message="Need to define the consent trigger function that returns bool", color="red")
|
112
112
|
raise PydanticCustomError("invalid_process", "Need to define the consent trigger function that returns bool", {})
|
113
113
|
|
114
|
+
|
115
|
+
if self.consent_trigger and isinstance(self.consent_trigger, Callable):
|
116
|
+
self.consent_trigger = Condition(methods={"0": self.consent_trigger})
|
114
117
|
return self
|
115
118
|
|
116
119
|
|
@@ -277,6 +280,7 @@ class AgentNetwork(BaseModel):
|
|
277
280
|
task_graph = TaskGraph(nodes={node.identifier: node for node in nodes})
|
278
281
|
|
279
282
|
for i in range(0, len(nodes) - 1):
|
283
|
+
condition = self.consent_trigger if isinstance(self.consent_trigger, Condition) else Condition(methods={"0": self.consent_trigger }) if self.consent_trigger else None
|
280
284
|
task_graph.add_edge(
|
281
285
|
source=nodes[i].identifier,
|
282
286
|
target=nodes[i+1].identifier,
|
@@ -284,7 +288,7 @@ class AgentNetwork(BaseModel):
|
|
284
288
|
weight=3 if nodes[i].task in self.manager_tasks else 1,
|
285
289
|
dependency_type=DependencyType.FINISH_TO_START if self.process == TaskHandlingProcess.HIERARCHY else DependencyType.START_TO_START,
|
286
290
|
required=bool(self.process == TaskHandlingProcess.CONSENSUAL),
|
287
|
-
condition=
|
291
|
+
condition=condition,
|
288
292
|
data_transfer=bool(self.process == TaskHandlingProcess.HIERARCHY),
|
289
293
|
)
|
290
294
|
)
|
@@ -103,7 +103,7 @@ class Evaluation(BaseModel):
|
|
103
103
|
new_res = filter(lambda x: "score" in x["metadata"], res)
|
104
104
|
new_res = list(new_res)
|
105
105
|
new_res.sort(key=lambda x: x["metadata"]["score"], reverse=True)
|
106
|
-
if new_res[0]['data']:
|
106
|
+
if new_res and new_res[0]['data']:
|
107
107
|
c = new_res[0]['data']['task_output']
|
108
108
|
w = new_res[len(new_res)-1]['data']['task_output'] if new_res[len(new_res)-1]['metadata']['score'] < new_res[0]['metadata']['score'] else ""
|
109
109
|
shot_prompt = SHOTS.format(c=c, w=w)
|
@@ -8,14 +8,63 @@ import matplotlib.pyplot as plt
|
|
8
8
|
from abc import ABC
|
9
9
|
from concurrent.futures import Future
|
10
10
|
from typing import List, Any, Optional, Callable, Dict, Type, Tuple
|
11
|
+
from typing_extensions import Self
|
11
12
|
|
12
|
-
from pydantic import BaseModel, InstanceOf, Field, UUID4, field_validator
|
13
|
+
from pydantic import BaseModel, InstanceOf, Field, UUID4, field_validator, model_validator
|
13
14
|
from pydantic_core import PydanticCustomError
|
14
15
|
|
15
16
|
from versionhq.agent.model import Agent
|
16
|
-
from versionhq.task.model import Task, TaskOutput
|
17
|
+
from versionhq.task.model import Task, TaskOutput, Evaluation
|
17
18
|
from versionhq._utils.logger import Logger
|
18
19
|
|
20
|
+
class ConditionType(enum.Enum):
|
21
|
+
AND = 1
|
22
|
+
OR = 2
|
23
|
+
|
24
|
+
|
25
|
+
class Condition(BaseModel):
|
26
|
+
"""
|
27
|
+
A Pydantic class to store edge conditions and their args and types.
|
28
|
+
"""
|
29
|
+
# edge_id: UUID4 = uuid.uuid4()
|
30
|
+
methods: Dict[str, Callable | "Condition"] = dict()
|
31
|
+
args: Dict[str, Dict[str, Any]] = dict()
|
32
|
+
type: ConditionType = None
|
33
|
+
|
34
|
+
@model_validator(mode="after")
|
35
|
+
def validate_type(self) -> Self:
|
36
|
+
if len(self.methods.keys()) > 1 and self.type is None:
|
37
|
+
raise PydanticCustomError("missing_type", "Missing type", {})
|
38
|
+
return self
|
39
|
+
|
40
|
+
def _execute_method(self, key: str, method: Callable | "Condition") -> bool:
|
41
|
+
match method:
|
42
|
+
case Condition():
|
43
|
+
return method.condition_met()
|
44
|
+
case _:
|
45
|
+
args = self.args[key] if key in self.args else None
|
46
|
+
res = method(**args) if args else method()
|
47
|
+
return res
|
48
|
+
|
49
|
+
|
50
|
+
def condition_met(self) -> bool:
|
51
|
+
if not self.methods:
|
52
|
+
return True
|
53
|
+
|
54
|
+
if len(self.methods) == 1:
|
55
|
+
for k, v in self.methods.items():
|
56
|
+
return self._execute_method(key=k, method=v)
|
57
|
+
|
58
|
+
else:
|
59
|
+
cond_list = []
|
60
|
+
for k, v in self.methods.items():
|
61
|
+
res = self._execute_method(key=k, method=v)
|
62
|
+
if self.type == ConditionType.OR and res == True:
|
63
|
+
return True
|
64
|
+
elif self.type == ConditionType.AND and res == False:
|
65
|
+
return False
|
66
|
+
return bool(len([item for item in cond_list if item == True]) == len(cond_list))
|
67
|
+
|
19
68
|
|
20
69
|
class TaskStatus(enum.Enum):
|
21
70
|
"""
|
@@ -52,7 +101,6 @@ class Node(BaseModel):
|
|
52
101
|
assigned_to: InstanceOf[Agent] = Field(default=None)
|
53
102
|
status: TaskStatus = Field(default=TaskStatus.NOT_STARTED)
|
54
103
|
|
55
|
-
|
56
104
|
@field_validator("id", mode="before")
|
57
105
|
@classmethod
|
58
106
|
def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
|
@@ -83,7 +131,6 @@ class Node(BaseModel):
|
|
83
131
|
self.status = TaskStatus.COMPLETED if res else TaskStatus.ERROR
|
84
132
|
return res
|
85
133
|
|
86
|
-
|
87
134
|
@property
|
88
135
|
def in_degrees(self) -> int:
|
89
136
|
return len(self.in_degree_nodes) if self.in_degree_nodes else 0
|
@@ -101,6 +148,11 @@ class Node(BaseModel):
|
|
101
148
|
"""Unique identifier of the node"""
|
102
149
|
return f"{str(self.id)}"
|
103
150
|
|
151
|
+
@property
|
152
|
+
def label(self) -> str:
|
153
|
+
"""Human friendly label for visualization"""
|
154
|
+
return self.task.name if self.task.name else self.task.description[0: 8]
|
155
|
+
|
104
156
|
def __str__(self):
|
105
157
|
if self.task:
|
106
158
|
return f"{self.identifier}: {self.task.name if self.task.name else self.task.description[0: 12]}"
|
@@ -110,19 +162,17 @@ class Node(BaseModel):
|
|
110
162
|
|
111
163
|
class Edge(BaseModel):
|
112
164
|
"""
|
113
|
-
A class to store an edge object that connects source and target nodes.
|
165
|
+
A Pydantic class to store an edge object that connects source and target nodes.
|
114
166
|
"""
|
115
167
|
|
116
168
|
source: Node = Field(default=None)
|
117
169
|
target: Node = Field(default=None)
|
118
170
|
|
119
171
|
description: Optional[str] = Field(default=None)
|
120
|
-
weight: Optional[float | int] = Field(default=1, description="est. duration
|
121
|
-
|
172
|
+
weight: Optional[float | int] = Field(default=1, description="est. duration of the task execution or respective weight of the target node at any scale i.e., 1 low - 10 high priority")
|
122
173
|
dependency_type: DependencyType = Field(default=DependencyType.FINISH_TO_START)
|
123
174
|
required: bool = Field(default=True, description="whether to consider the source's status")
|
124
|
-
condition: Optional[
|
125
|
-
condition_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
175
|
+
condition: Optional[Condition] = Field(default=None)
|
126
176
|
|
127
177
|
lag: Optional[float | int] = Field(default=None, description="lag time (sec) from the dependency met to the task execution")
|
128
178
|
data_transfer: bool = Field(default=True, description="whether the data transfer is required. by default transfer plane text output from in-degree nodes as context")
|
@@ -150,35 +200,36 @@ class Edge(BaseModel):
|
|
150
200
|
False Given Condition True (predecessor status irrelevant) Yes
|
151
201
|
"""
|
152
202
|
|
153
|
-
if
|
154
|
-
return self.condition(
|
203
|
+
if self.required == False:
|
204
|
+
return self.condition.condition_met() if self.condition else True
|
205
|
+
# return self.condition(**self.condition_kwargs) if self.condition else True
|
155
206
|
|
156
207
|
match self.dependency_type:
|
157
208
|
case DependencyType.FINISH_TO_START:
|
158
209
|
"""target starts after source finishes"""
|
159
210
|
if not self.source or self.source.status == TaskStatus.COMPLETED:
|
160
|
-
return self.condition(
|
211
|
+
return self.condition.condition_met() if self.condition else True
|
161
212
|
else:
|
162
213
|
return False
|
163
214
|
|
164
215
|
case DependencyType.START_TO_START:
|
165
216
|
"""target starts when source starts"""
|
166
217
|
if not self.source or self.source.status != TaskStatus.NOT_STARTED:
|
167
|
-
return self.condition(
|
218
|
+
return self.condition.condition_met() if self.condition else True
|
168
219
|
else:
|
169
220
|
return False
|
170
221
|
|
171
222
|
case DependencyType.FINISH_TO_FINISH:
|
172
223
|
"""target finish when source start"""
|
173
224
|
if not self.source or self.source.status != TaskStatus.COMPLETED:
|
174
|
-
return self.condition(
|
225
|
+
return self.condition.condition_met() if self.condition else True
|
175
226
|
else:
|
176
227
|
return False
|
177
228
|
|
178
229
|
case DependencyType.START_TO_FINISH:
|
179
230
|
"""target finishes when source start"""
|
180
231
|
if not self.source or self.source.status == TaskStatus.IN_PROGRESS:
|
181
|
-
return self.condition(
|
232
|
+
return self.condition.condition_met() if self.condition else True
|
182
233
|
else:
|
183
234
|
return False
|
184
235
|
|
@@ -204,6 +255,11 @@ class Edge(BaseModel):
|
|
204
255
|
res = self.target.handle_task_execution(context=context, response_format=response_format)
|
205
256
|
return res
|
206
257
|
|
258
|
+
@property
|
259
|
+
def label(self):
|
260
|
+
"""Human friendly label for visualization."""
|
261
|
+
return f"e{self.source.label}-{self.target.label}"
|
262
|
+
|
207
263
|
|
208
264
|
class Graph(ABC, BaseModel):
|
209
265
|
"""
|
@@ -383,7 +439,7 @@ class TaskGraph(Graph):
|
|
383
439
|
edge = Edge()
|
384
440
|
for k in Edge.model_fields.keys():
|
385
441
|
v = edge_attributes.get(k, None)
|
386
|
-
if v:
|
442
|
+
if v is not None:
|
387
443
|
setattr(edge, k, v)
|
388
444
|
else:
|
389
445
|
pass
|
@@ -564,7 +620,7 @@ class TaskGraph(Graph):
|
|
564
620
|
return res, self.outputs
|
565
621
|
|
566
622
|
|
567
|
-
def evaluate(self, eval_criteria: List[str] = None):
|
623
|
+
def evaluate(self, eval_criteria: List[str] = None) -> Evaluation | None:
|
568
624
|
"""Evaluates the conclusion based on the given eval criteria."""
|
569
625
|
|
570
626
|
if not isinstance(self.concl, TaskOutput):
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from unittest.mock import patch
|
2
|
+
|
3
|
+
|
4
|
+
def test_draft():
|
5
|
+
import versionhq as vhq
|
6
|
+
from pydantic import BaseModel
|
7
|
+
|
8
|
+
class Trip(BaseModel):
|
9
|
+
name: str
|
10
|
+
location: str
|
11
|
+
description: str
|
12
|
+
date: str
|
13
|
+
cousine: str
|
14
|
+
why_its_suitable: str
|
15
|
+
|
16
|
+
|
17
|
+
with patch('builtins.input', return_value='Y'):
|
18
|
+
task_graph = vhq.workflow(
|
19
|
+
Trip,
|
20
|
+
context="Planning a suprise day trip for my friend to celebrate her birthday. We live in CA and we like to have Korean food.",
|
21
|
+
human=True
|
22
|
+
)
|
23
|
+
|
24
|
+
assert task_graph
|
25
|
+
assert [k == node.identifier and node.task and isinstance(node, vhq.Node) for k, node in task_graph.nodes.items()]
|
26
|
+
assert [isinstance(edge.dependency_type, vhq.DependencyType) and isinstance(edge, vhq.Edge) for k, edge in task_graph.edges.items()]
|
27
|
+
assert [k in task_graph.nodes.keys() and v.status == vhq.TaskStatus.NOT_STARTED for k, v in task_graph.nodes.items()]
|
28
|
+
|
29
|
+
|
30
|
+
def test_condition():
|
31
|
+
import versionhq as vhq
|
32
|
+
|
33
|
+
task_a = vhq.Task(description="draft a message")
|
34
|
+
task_b = vhq.Task(description="ask human for feedback")
|
35
|
+
task_c = vhq.Task(description="send a message")
|
36
|
+
|
37
|
+
tg = vhq.TaskGraph(directed=True)
|
38
|
+
|
39
|
+
node_a = tg.add_task(task=task_a)
|
40
|
+
node_b = tg.add_task(task=task_b)
|
41
|
+
node_c = tg.add_task(task=task_c)
|
42
|
+
|
43
|
+
def cond_method(): return True
|
44
|
+
|
45
|
+
def cond_method_with_args(**kwargs):
|
46
|
+
item = kwargs.get("item", None)
|
47
|
+
return True if item else False
|
48
|
+
|
49
|
+
complex_condition = vhq.Condition(
|
50
|
+
methods={"0": cond_method, "1": cond_method_with_args, "2": cond_method_with_args},
|
51
|
+
args={"2": dict(item="Hi!"), },
|
52
|
+
type=vhq.ConditionType.AND
|
53
|
+
)
|
54
|
+
|
55
|
+
tg.add_dependency(
|
56
|
+
node_a.identifier, node_b.identifier,
|
57
|
+
dependency_type=vhq.DependencyType.FINISH_TO_START,
|
58
|
+
required=False,
|
59
|
+
condition=vhq.Condition(methods={ "0": cond_method, }),
|
60
|
+
)
|
61
|
+
|
62
|
+
edge = [v for v in tg.edges.values()][0]
|
63
|
+
assert isinstance(edge, vhq.Edge)
|
64
|
+
assert edge.required == False
|
65
|
+
assert edge.dependency_type == vhq.DependencyType.FINISH_TO_START
|
66
|
+
assert edge.condition == vhq.Condition(methods={ "0": cond_method, })
|
67
|
+
assert edge.dependency_met() == True
|
68
|
+
|
69
|
+
|
70
|
+
tg.add_dependency(
|
71
|
+
node_b.identifier, node_c.identifier,
|
72
|
+
dependency_type=vhq.DependencyType.FINISH_TO_START,
|
73
|
+
required=False,
|
74
|
+
condition=vhq.Condition(
|
75
|
+
methods={ "0": cond_method, "1": cond_method_with_args, "2": complex_condition },
|
76
|
+
type=vhq.ConditionType.AND
|
77
|
+
),
|
78
|
+
)
|
79
|
+
|
80
|
+
edge = [v for v in tg.edges.values()][1]
|
81
|
+
assert isinstance(edge, vhq.Edge)
|
82
|
+
assert edge.required == False
|
83
|
+
assert edge.dependency_type == vhq.DependencyType.FINISH_TO_START
|
84
|
+
assert edge.condition == vhq.Condition(
|
85
|
+
methods={ "0": cond_method, "1": cond_method_with_args, "2": complex_condition },
|
86
|
+
type=vhq.ConditionType.AND
|
87
|
+
)
|
88
|
+
assert edge.dependency_met() == False
|
@@ -1,27 +0,0 @@
|
|
1
|
-
from unittest.mock import patch
|
2
|
-
|
3
|
-
|
4
|
-
def test_draft():
|
5
|
-
import versionhq as vhq
|
6
|
-
from pydantic import BaseModel
|
7
|
-
|
8
|
-
class Trip(BaseModel):
|
9
|
-
name: str
|
10
|
-
location: str
|
11
|
-
description: str
|
12
|
-
date: str
|
13
|
-
cousine: str
|
14
|
-
why_its_suitable: str
|
15
|
-
|
16
|
-
|
17
|
-
with patch('builtins.input', return_value='Y'):
|
18
|
-
task_graph = vhq.workflow(
|
19
|
-
Trip,
|
20
|
-
context="Planning a suprise day trip for my friend to celebrate her birthday. We live in CA and we like to have Korean food.",
|
21
|
-
human=True
|
22
|
-
)
|
23
|
-
|
24
|
-
assert task_graph
|
25
|
-
assert [k == node.identifier and node.task and isinstance(node, vhq.Node) for k, node in task_graph.nodes.items()]
|
26
|
-
assert [isinstance(edge.dependency_type, vhq.DependencyType) and isinstance(edge, vhq.Edge) for k, edge in task_graph.edges.items()]
|
27
|
-
assert [k in task_graph.nodes.keys() and v.status == vhq.TaskStatus.NOT_STARTED for k, v in task_graph.nodes.items()]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|