versionhq 1.1.13.0__py3-none-any.whl → 1.2.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- versionhq/__init__.py +18 -3
- versionhq/agent/model.py +91 -39
- versionhq/llm/llm_vars.py +0 -1
- versionhq/llm/model.py +1 -1
- versionhq/network/__init__.py +0 -0
- versionhq/network/model.py +500 -0
- versionhq/task/evaluate.py +1 -1
- versionhq/task/formation.py +1 -1
- versionhq/task/model.py +120 -86
- versionhq/task/structured_response.py +16 -14
- versionhq/team/model.py +11 -10
- versionhq/team/team_planner.py +2 -4
- {versionhq-1.1.13.0.dist-info → versionhq-1.2.0.1.dist-info}/METADATA +149 -35
- {versionhq-1.1.13.0.dist-info → versionhq-1.2.0.1.dist-info}/RECORD +17 -15
- {versionhq-1.1.13.0.dist-info → versionhq-1.2.0.1.dist-info}/LICENSE +0 -0
- {versionhq-1.1.13.0.dist-info → versionhq-1.2.0.1.dist-info}/WHEEL +0 -0
- {versionhq-1.1.13.0.dist-info → versionhq-1.2.0.1.dist-info}/top_level.txt +0 -0
versionhq/task/model.py
CHANGED
@@ -3,6 +3,7 @@ import threading
|
|
3
3
|
import datetime
|
4
4
|
import uuid
|
5
5
|
import inspect
|
6
|
+
import enum
|
6
7
|
from concurrent.futures import Future
|
7
8
|
from hashlib import md5
|
8
9
|
from typing import Any, Dict, List, Set, Optional, Callable, Type
|
@@ -11,18 +12,25 @@ from typing_extensions import Annotated, Self
|
|
11
12
|
from pydantic import UUID4, BaseModel, Field, PrivateAttr, field_validator, model_validator, InstanceOf, field_validator
|
12
13
|
from pydantic_core import PydanticCustomError
|
13
14
|
|
14
|
-
|
15
|
+
import versionhq as vhq
|
15
16
|
from versionhq.task.log_handler import TaskOutputStorageHandler
|
16
17
|
from versionhq.task.evaluate import Evaluation, EvaluationItem
|
17
18
|
from versionhq.tool.model import Tool, ToolSet
|
18
19
|
from versionhq._utils import process_config, Logger
|
19
20
|
|
20
21
|
|
22
|
+
class TaskExecutionType(enum.Enum):
|
23
|
+
"""
|
24
|
+
Enumeration to store task execution types of independent tasks without dependencies.
|
25
|
+
"""
|
26
|
+
SYNC = 1
|
27
|
+
ASYNC = 2
|
28
|
+
|
29
|
+
|
21
30
|
class ResponseField(BaseModel):
|
22
31
|
"""
|
23
|
-
A class to store
|
24
|
-
|
25
|
-
https://community.openai.com/t/official-documentation-for-supported-schemas-for-response-format-parameter-in-calls-to-client-beta-chats-completions-parse/932422/3
|
32
|
+
A class to store a response format that will generate a JSON schema.
|
33
|
+
One layer of nested child is acceptable.
|
26
34
|
"""
|
27
35
|
|
28
36
|
title: str = Field(default=None, description="title of the field")
|
@@ -31,7 +39,7 @@ class ResponseField(BaseModel):
|
|
31
39
|
properties: Optional[List[BaseModel]] = Field(default=None, description="store dict items in ResponseField format")
|
32
40
|
required: bool = Field(default=True)
|
33
41
|
nullable: bool = Field(default=False)
|
34
|
-
config: Optional[Dict[str, Any]] = Field(
|
42
|
+
config: Optional[Dict[str, Any]] = Field(default_factory=dict, description="additional rules")
|
35
43
|
|
36
44
|
|
37
45
|
@model_validator(mode="after")
|
@@ -57,36 +65,44 @@ class ResponseField(BaseModel):
|
|
57
65
|
|
58
66
|
def _format_props(self) -> Dict[str, Any]:
|
59
67
|
"""
|
60
|
-
Structure valid properties.
|
68
|
+
Structure valid properties from the ResponseField object. 1 layer of nested child is accepted.
|
61
69
|
"""
|
62
70
|
from versionhq.llm.llm_vars import SchemaType
|
63
71
|
|
64
72
|
schema_type = SchemaType(type=self.data_type).convert()
|
65
73
|
props: Dict[str, Any] = {}
|
66
74
|
|
67
|
-
if self.data_type is list
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
if self.data_type is list:
|
76
|
+
if self.items is dict:
|
77
|
+
nested_p, nested_r = dict(), list()
|
78
|
+
|
79
|
+
if self.properties:
|
80
|
+
for item in self.properties:
|
81
|
+
nested_p.update(**item._format_props())
|
82
|
+
nested_r.append(item.title)
|
83
|
+
|
84
|
+
props = {
|
85
|
+
"type": schema_type,
|
86
|
+
"items": {
|
87
|
+
"type": SchemaType(type=self.items).convert(),
|
88
|
+
"properties": nested_p,
|
89
|
+
"required": nested_r,
|
90
|
+
"additionalProperties": False
|
91
|
+
}
|
92
|
+
}
|
75
93
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
94
|
+
elif self.items is list:
|
95
|
+
props = {
|
96
|
+
"type": schema_type,
|
97
|
+
"items": { "type": SchemaType(type=self.items).convert(), "items": { "type": SchemaType(type=str).convert() }},
|
98
|
+
}
|
80
99
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
"type": SchemaType(type=self.items).convert(),
|
85
|
-
"properties": nested_p,
|
86
|
-
"required": nested_r,
|
87
|
-
"additionalProperties": False
|
100
|
+
else:
|
101
|
+
props = {
|
102
|
+
"type": schema_type,
|
103
|
+
"items": { "type": SchemaType(type=self.items).convert() },
|
88
104
|
}
|
89
|
-
|
105
|
+
|
90
106
|
|
91
107
|
elif self.data_type is dict:
|
92
108
|
p, r = dict(), list()
|
@@ -184,17 +200,16 @@ class TaskOutput(BaseModel):
|
|
184
200
|
return json.dumps(self.json_dict) if self.json_dict else self.raw[0: 127]
|
185
201
|
|
186
202
|
|
187
|
-
def evaluate(self, task
|
203
|
+
def evaluate(self, task) -> Evaluation:
|
188
204
|
"""
|
189
205
|
Evaluate the output based on the criteria, score each from 0 to 1 scale, and raise suggestions for future improvement.
|
190
206
|
"""
|
191
207
|
from versionhq.task.TEMPLATES.Description import EVALUATE
|
192
208
|
|
193
|
-
if not self.evaluation
|
194
|
-
self.evaluation = Evaluation()
|
209
|
+
self.evaluation = Evaluation() if not self.evaluation else self.evaluation
|
195
210
|
|
196
|
-
self.evaluation.latency = latency if latency is not None else task.latency
|
197
|
-
self.evaluation.tokens = tokens if tokens is not None else task.tokens
|
211
|
+
# self.evaluation.latency = latency if latency is not None else task.latency
|
212
|
+
# self.evaluation.tokens = tokens if tokens is not None else task.tokens
|
198
213
|
|
199
214
|
eval_criteria = task.eval_criteria if task.eval_criteria else ["Overall competitiveness", ]
|
200
215
|
|
@@ -203,7 +218,7 @@ class TaskOutput(BaseModel):
|
|
203
218
|
description=EVALUATE.format(task_description=task.description, task_output=self.raw, eval_criteria=str(item)),
|
204
219
|
pydantic_output=EvaluationItem
|
205
220
|
)
|
206
|
-
res = task_eval.
|
221
|
+
res = task_eval.execute(agent=self.evaluation.eval_by)
|
207
222
|
|
208
223
|
if res.pydantic:
|
209
224
|
item = EvaluationItem(score=res.pydantic.score, suggestion=res.pydantic.suggestion, criteria=res.pydantic.criteria)
|
@@ -241,7 +256,7 @@ class TaskOutput(BaseModel):
|
|
241
256
|
|
242
257
|
class Task(BaseModel):
|
243
258
|
"""
|
244
|
-
A class that stores independent task information.
|
259
|
+
A class that stores independent task information and handles task executions.
|
245
260
|
"""
|
246
261
|
|
247
262
|
__hash__ = object.__hash__
|
@@ -255,8 +270,8 @@ class Task(BaseModel):
|
|
255
270
|
description: str = Field(description="Description of the actual task")
|
256
271
|
|
257
272
|
# output
|
258
|
-
pydantic_output: Optional[
|
259
|
-
response_fields: List[ResponseField] = Field(default_factory=list, description="store
|
273
|
+
pydantic_output: Optional[Type[BaseModel]] = Field(default=None, description="store Pydantic class as structured response format")
|
274
|
+
response_fields: Optional[List[ResponseField]] = Field(default_factory=list, description="store list of ResponseField as structured response format")
|
260
275
|
output: Optional[TaskOutput] = Field(default=None, description="store the final task output in TaskOutput class")
|
261
276
|
|
262
277
|
# task setup
|
@@ -269,8 +284,8 @@ class Task(BaseModel):
|
|
269
284
|
tool_res_as_final: bool = Field(default=False, description="when set True, tools res will be stored in the `TaskOutput`")
|
270
285
|
|
271
286
|
# execution rules
|
287
|
+
execution_type: TaskExecutionType = Field(default=TaskExecutionType.SYNC)
|
272
288
|
allow_delegation: bool = Field(default=False, description="ask other agents for help and run the task instead")
|
273
|
-
async_execution: bool = Field(default=False,description="whether the task should be executed asynchronously or not")
|
274
289
|
callback: Optional[Callable] = Field(default=None, description="callback to be executed after the task is completed.")
|
275
290
|
callback_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict, description="kwargs for the callback when the callback is callable")
|
276
291
|
|
@@ -408,11 +423,7 @@ Ref. Output image: {output_formats_to_follow}
|
|
408
423
|
properties, required_fields = {}, []
|
409
424
|
for i, item in enumerate(self.response_fields):
|
410
425
|
if item:
|
411
|
-
|
412
|
-
properties.update(item._format_props())
|
413
|
-
else:
|
414
|
-
properties.update(item._format_props())
|
415
|
-
|
426
|
+
properties.update(item._format_props())
|
416
427
|
required_fields.append(item.title)
|
417
428
|
|
418
429
|
response_schema = {
|
@@ -421,7 +432,6 @@ Ref. Output image: {output_formats_to_follow}
|
|
421
432
|
"required": required_fields,
|
422
433
|
"additionalProperties": False,
|
423
434
|
}
|
424
|
-
|
425
435
|
response_format = {
|
426
436
|
"type": "json_schema",
|
427
437
|
"json_schema": { "name": "outcome", "schema": response_schema }
|
@@ -429,7 +439,7 @@ Ref. Output image: {output_formats_to_follow}
|
|
429
439
|
|
430
440
|
|
431
441
|
elif self.pydantic_output:
|
432
|
-
response_format = StructuredOutput(response_format=self.pydantic_output)._format()
|
442
|
+
response_format = StructuredOutput(response_format=self.pydantic_output, provider=model_provider)._format()
|
433
443
|
|
434
444
|
return response_format
|
435
445
|
|
@@ -481,7 +491,6 @@ Ref. Output image: {output_formats_to_follow}
|
|
481
491
|
|
482
492
|
for k, v in json_dict.items():
|
483
493
|
setattr(output_pydantic, k, v)
|
484
|
-
|
485
494
|
except:
|
486
495
|
pass
|
487
496
|
|
@@ -536,9 +545,35 @@ Ref. Output image: {output_formats_to_follow}
|
|
536
545
|
self._logger.log(level="error", message=f"Failed to add to the memory: {str(e)}", color="red")
|
537
546
|
pass
|
538
547
|
|
548
|
+
def _build_agent_from_task(self, task_description: str = None) -> InstanceOf["vhq.Agent"]:
|
549
|
+
task_description = task_description if task_description else self.description
|
550
|
+
if not task_description:
|
551
|
+
self._logger.log(level="error", message="Task is missing the description.", color="red")
|
552
|
+
pass
|
553
|
+
|
554
|
+
agent = vhq.Agent(goal=task_description, role=task_description, maxit=1) #! REFINEME
|
555
|
+
return agent
|
556
|
+
|
539
557
|
|
540
558
|
# task execution
|
541
|
-
def
|
559
|
+
def execute(self, type: TaskExecutionType = None, agent: Optional[Any] = None, context: Optional[str] = None) -> TaskOutput | Future[TaskOutput]:
|
560
|
+
"""
|
561
|
+
A main method to handle task execution. Build an agent when the agent is not given.
|
562
|
+
"""
|
563
|
+
type = type if type else self.execution_type if self.execution_type else TaskExecutionType.SYNC
|
564
|
+
|
565
|
+
if not agent:
|
566
|
+
agent = self._build_agent_from_task(task_description=self.description)
|
567
|
+
|
568
|
+
match type:
|
569
|
+
case TaskExecutionType.SYNC:
|
570
|
+
return self._execute_sync(agent=agent, context=context)
|
571
|
+
|
572
|
+
case TaskExecutionType.ASYNC:
|
573
|
+
return self._execute_async(agent=agent, context=context)
|
574
|
+
|
575
|
+
|
576
|
+
def _execute_sync(self, agent, context: Optional[str | List[Any]] = None) -> TaskOutput:
|
542
577
|
"""
|
543
578
|
Execute the task synchronously.
|
544
579
|
When the task has context, make sure we have executed all the tasks in the context first.
|
@@ -553,7 +588,7 @@ Ref. Output image: {output_formats_to_follow}
|
|
553
588
|
return self._execute_core(agent, context)
|
554
589
|
|
555
590
|
|
556
|
-
def
|
591
|
+
def _execute_async(self, agent, context: Optional[str] = None) -> Future[TaskOutput]:
|
557
592
|
"""
|
558
593
|
Execute the task asynchronously.
|
559
594
|
"""
|
@@ -565,7 +600,7 @@ Ref. Output image: {output_formats_to_follow}
|
|
565
600
|
|
566
601
|
def _execute_task_async(self, agent, context: Optional[str], future: Future[TaskOutput]) -> None:
|
567
602
|
"""
|
568
|
-
|
603
|
+
Executes the task asynchronously with context handling.
|
569
604
|
"""
|
570
605
|
|
571
606
|
result = self._execute_core(agent, context)
|
@@ -574,8 +609,8 @@ Ref. Output image: {output_formats_to_follow}
|
|
574
609
|
|
575
610
|
def _execute_core(self, agent, context: Optional[str]) -> TaskOutput:
|
576
611
|
"""
|
577
|
-
|
578
|
-
|
612
|
+
A core method for task execution.
|
613
|
+
Handles 1. agent delegation, 2. tools, 3. context to add to the prompt, and 4. callbacks
|
579
614
|
"""
|
580
615
|
|
581
616
|
from versionhq.agent.model import Agent
|
@@ -634,13 +669,13 @@ Ref. Output image: {output_formats_to_follow}
|
|
634
669
|
json_dict=json_dict_output
|
635
670
|
)
|
636
671
|
|
637
|
-
|
638
672
|
self.latency = (ended_at - started_at).total_seconds()
|
673
|
+
task_output.evaluation = Evaluation(latency=self.latency, tokens=self.tokens)
|
639
674
|
self.output = task_output
|
640
675
|
self.processed_agents.add(agent.role)
|
641
676
|
|
642
677
|
if self.should_evaluate:
|
643
|
-
task_output.evaluate(task=self
|
678
|
+
task_output.evaluate(task=self)
|
644
679
|
|
645
680
|
self._create_short_and_long_term_memories(agent=agent, task_output=task_output)
|
646
681
|
|
@@ -648,7 +683,7 @@ Ref. Output image: {output_formats_to_follow}
|
|
648
683
|
kwargs = { **self.callback_kwargs, **task_output.json_dict }
|
649
684
|
sig = inspect.signature(self.callback)
|
650
685
|
valid_keys = [param.name for param in sig.parameters.values() if param.kind == param.POSITIONAL_OR_KEYWORD]
|
651
|
-
valid_kwargs = { k: kwargs[k] for k in valid_keys }
|
686
|
+
valid_kwargs = { k: kwargs[k] if k in kwargs else None for k in valid_keys }
|
652
687
|
callback_res = self.callback(**valid_kwargs)
|
653
688
|
task_output.callback_output = callback_res
|
654
689
|
|
@@ -687,50 +722,49 @@ Task ID: {str(self.id)}
|
|
687
722
|
|
688
723
|
|
689
724
|
|
690
|
-
class ConditionalTask(Task):
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
725
|
+
# class ConditionalTask(Task):
|
726
|
+
# """
|
727
|
+
# A task that can be conditionally executed based on the output of another task.
|
728
|
+
# When the `condition` return True, execute the task, else skipped with `skipped task output`.
|
729
|
+
# """
|
695
730
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
731
|
+
# condition: Callable[[TaskOutput], bool] = Field(
|
732
|
+
# default=None,
|
733
|
+
# description="max. number of retries for an agent to execute a task when an error occurs",
|
734
|
+
# )
|
700
735
|
|
701
736
|
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
self._logger = Logger(verbose=True)
|
737
|
+
# def __init__(self, condition: Callable[[Any], bool], **kwargs):
|
738
|
+
# super().__init__(**kwargs)
|
739
|
+
# self.condition = condition
|
706
740
|
|
707
741
|
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
742
|
+
# def should_execute(self, context: TaskOutput) -> bool:
|
743
|
+
# """
|
744
|
+
# Decide whether the conditional task should be executed based on the provided context.
|
745
|
+
# Return `True` if it should be executed.
|
746
|
+
# """
|
747
|
+
# return self.condition(context)
|
714
748
|
|
715
749
|
|
716
|
-
|
717
|
-
|
750
|
+
# def get_skipped_task_output(self):
|
751
|
+
# return TaskOutput(task_id=self.id, raw="", pydantic=None, json_dict={})
|
718
752
|
|
719
753
|
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
754
|
+
# def _handle_conditional_task(self, task_outputs: List[TaskOutput], task_index: int, was_replayed: bool) -> Optional[TaskOutput]:
|
755
|
+
# """
|
756
|
+
# When the conditional task should be skipped, return `skipped_task_output` as task_output else return None
|
757
|
+
# """
|
724
758
|
|
725
|
-
|
759
|
+
# previous_output = task_outputs[task_index - 1] if task_outputs and len(task_outputs) > 1 else None
|
726
760
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
761
|
+
# if previous_output and not self.should_execute(previous_output):
|
762
|
+
# self._logger.log(level="warning", message=f"Skipping conditional task: {self.description}", color="yellow")
|
763
|
+
# skipped_task_output = self.get_skipped_task_output()
|
764
|
+
# self.output = skipped_task_output
|
731
765
|
|
732
|
-
|
733
|
-
|
734
|
-
|
766
|
+
# if not was_replayed:
|
767
|
+
# self._store_execution_log(self, task_index=task_index, was_replayed=was_replayed, inputs={})
|
768
|
+
# return skipped_task_output
|
735
769
|
|
736
|
-
|
770
|
+
# return None
|
@@ -3,11 +3,11 @@ from typing import Dict, Type, List, Any
|
|
3
3
|
from pydantic import BaseModel, Field, InstanceOf
|
4
4
|
|
5
5
|
from versionhq.llm.llm_vars import SchemaType
|
6
|
-
from versionhq.llm.model import LLM
|
6
|
+
from versionhq.llm.model import LLM, DEFAULT_MODEL_PROVIDER_NAME
|
7
7
|
|
8
8
|
|
9
9
|
"""
|
10
|
-
|
10
|
+
Generate a JSON schema from the given Pydantic model.
|
11
11
|
"""
|
12
12
|
|
13
13
|
|
@@ -15,7 +15,7 @@ class StructuredObject:
|
|
15
15
|
"""
|
16
16
|
A class to store the structured dictionary.
|
17
17
|
"""
|
18
|
-
provider: str =
|
18
|
+
provider: str = None
|
19
19
|
field: Type[Field]
|
20
20
|
|
21
21
|
title: str
|
@@ -24,20 +24,21 @@ class StructuredObject:
|
|
24
24
|
required: List[str] = list()
|
25
25
|
additionalProperties: bool = False
|
26
26
|
|
27
|
-
def __init__(self, name, field: Type[Field], provider: str | InstanceOf[LLM] =
|
27
|
+
def __init__(self, name, field: Type[Field], provider: str | InstanceOf[LLM] = None):
|
28
28
|
self.title = name
|
29
29
|
self.field = field
|
30
30
|
self.dtype = "object"
|
31
31
|
self.additionalProperties = False
|
32
|
-
self.provider = provider if isinstance(provider, str) else provider.provider
|
32
|
+
self.provider = provider if isinstance(provider, str) else provider.provider if isinstance(provider, LLM) else DEFAULT_MODEL_PROVIDER_NAME
|
33
33
|
|
34
34
|
def _format(self):
|
35
35
|
if not self.field:
|
36
36
|
pass
|
37
37
|
else:
|
38
38
|
description = self.field.description if hasattr(self.field, "description") and self.field.description is not None else ""
|
39
|
-
self.
|
40
|
-
self.
|
39
|
+
field_name = self.field.__name__ if hasattr(self.field, "__name__") and self.field.__name__ else self.title
|
40
|
+
self.properties.update({ field_name : { "type": SchemaType(self.field.annotation.__args__).convert() }})
|
41
|
+
self.required.append(field_name)
|
41
42
|
|
42
43
|
return {
|
43
44
|
self.title: {
|
@@ -55,13 +56,13 @@ class StructuredList:
|
|
55
56
|
"""
|
56
57
|
A class to store a structured list with 1 nested object.
|
57
58
|
"""
|
58
|
-
provider: str =
|
59
|
+
provider: str = DEFAULT_MODEL_PROVIDER_NAME
|
59
60
|
field: Type[Field]
|
60
61
|
title: str = ""
|
61
62
|
dtype: str = "array"
|
62
63
|
items: Dict[str, Dict[str, str]] = dict()
|
63
64
|
|
64
|
-
def __init__(self, name, field: Type[Field], provider: str | LLM =
|
65
|
+
def __init__(self, name, field: Type[Field], provider: str | LLM = DEFAULT_MODEL_PROVIDER_NAME):
|
65
66
|
self.provider = provider if isinstance(provider, str) else provider.provider
|
66
67
|
self.field = field
|
67
68
|
self.title = name
|
@@ -77,15 +78,15 @@ class StructuredList:
|
|
77
78
|
description = "" if field.description is None else field.description
|
78
79
|
props = {}
|
79
80
|
|
80
|
-
for item in field.annotation.__args__:
|
81
|
+
for i, item in enumerate(field.annotation.__args__):
|
81
82
|
nested_object_type = item.__origin__ if hasattr(item, "__origin__") else item
|
82
83
|
|
83
84
|
if nested_object_type == dict:
|
84
85
|
props.update({
|
85
86
|
# "nest": {
|
86
87
|
"type": "object",
|
87
|
-
"properties": { "
|
88
|
-
"required": ["
|
88
|
+
"properties": { f"{str(i)}": { "type": "string"} },
|
89
|
+
"required": [f"{str(i)}",],
|
89
90
|
"additionalProperties": False
|
90
91
|
# }
|
91
92
|
})
|
@@ -94,13 +95,14 @@ class StructuredList:
|
|
94
95
|
props.update({
|
95
96
|
# "nest": {
|
96
97
|
"type": "array",
|
97
|
-
"items": { "type": "string" }
|
98
|
+
"items": { "type": "string" },
|
98
99
|
# }
|
99
100
|
})
|
100
101
|
else:
|
101
102
|
props.update({ "type": SchemaType(nested_object_type).convert() })
|
102
103
|
|
103
104
|
self.items = { **props }
|
105
|
+
|
104
106
|
return {
|
105
107
|
self.title: {
|
106
108
|
"type": self.dtype,
|
@@ -112,7 +114,7 @@ class StructuredList:
|
|
112
114
|
|
113
115
|
class StructuredOutput(BaseModel):
|
114
116
|
response_format: Any = None # pydantic base model
|
115
|
-
provider: str =
|
117
|
+
provider: str = None
|
116
118
|
applicable_models: List[InstanceOf[LLM] | str] = list()
|
117
119
|
name: str = ""
|
118
120
|
schema: Dict[str, Any] = dict(type="object", additionalProperties=False, properties=dict(), required=list())
|
versionhq/team/model.py
CHANGED
@@ -10,7 +10,7 @@ from pydantic._internal._generate_schema import GenerateSchema
|
|
10
10
|
from pydantic_core import PydanticCustomError, core_schema
|
11
11
|
|
12
12
|
from versionhq.agent.model import Agent
|
13
|
-
from versionhq.task.model import Task, TaskOutput,
|
13
|
+
from versionhq.task.model import Task, TaskOutput, TaskExecutionType
|
14
14
|
from versionhq.task.formatter import create_raw_outputs
|
15
15
|
from versionhq.team.team_planner import TeamPlanner
|
16
16
|
from versionhq._utils.logger import Logger
|
@@ -215,7 +215,7 @@ class Team(BaseModel):
|
|
215
215
|
for task in reversed(self.tasks):
|
216
216
|
if not task:
|
217
217
|
break
|
218
|
-
elif task.
|
218
|
+
elif task.execution_type == TaskExecutionType.ASYNC:
|
219
219
|
async_task_count += 1
|
220
220
|
else:
|
221
221
|
break
|
@@ -341,7 +341,7 @@ class Team(BaseModel):
|
|
341
341
|
for task_index, task in enumerate(tasks):
|
342
342
|
if start_index is not None and task_index < start_index:
|
343
343
|
if task.output:
|
344
|
-
if task.
|
344
|
+
if task.execution_type == TaskExecutionType.ASYNC:
|
345
345
|
task_outputs.append(task.output)
|
346
346
|
else:
|
347
347
|
task_outputs = [task.output]
|
@@ -352,20 +352,21 @@ class Team(BaseModel):
|
|
352
352
|
if responsible_agent is None:
|
353
353
|
self._assign_tasks()
|
354
354
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
355
|
+
## commented out - this will be handled by node objects
|
356
|
+
# if isinstance(task, ConditionalTask):
|
357
|
+
# skipped_task_output = task._handle_conditional_task(task_outputs, futures, task_index, was_replayed)
|
358
|
+
# if skipped_task_output:
|
359
|
+
# continue
|
359
360
|
|
360
361
|
# self._log_task_start(task, responsible_agent)
|
361
362
|
|
362
|
-
if task.
|
363
|
+
if task.execution_type == TaskExecutionType.ASYNC:
|
363
364
|
context = create_raw_outputs(tasks=[task, ], task_outputs=([last_sync_output,] if last_sync_output else []))
|
364
|
-
future = task.
|
365
|
+
future = task._execute_async(agent=responsible_agent, context=context)
|
365
366
|
futures.append((task, future, task_index))
|
366
367
|
else:
|
367
368
|
context = create_raw_outputs(tasks=[task,], task_outputs=([last_sync_output,] if last_sync_output else [] ))
|
368
|
-
task_output = task.
|
369
|
+
task_output = task.execute(agent=responsible_agent, context=context)
|
369
370
|
if self.managers and responsible_agent in [manager.agent for manager in self.managers]:
|
370
371
|
lead_task_output = task_output
|
371
372
|
|
versionhq/team/team_planner.py
CHANGED
@@ -3,8 +3,6 @@ from typing import Any, List, Optional, Dict
|
|
3
3
|
from pydantic import BaseModel, Field
|
4
4
|
|
5
5
|
|
6
|
-
|
7
|
-
|
8
6
|
class TeamPlanner:
|
9
7
|
"""
|
10
8
|
A class to handle agent formations based on the given task description.
|
@@ -50,7 +48,7 @@ class TeamPlanner:
|
|
50
48
|
ResponseField(title="role", data_type=str, required=True),
|
51
49
|
],
|
52
50
|
)
|
53
|
-
res = task.
|
51
|
+
res = task.execute(agent=agent_creator)
|
54
52
|
agent = Agent(
|
55
53
|
role=res.json_dict["role"] if "role" in res.json_dict else res.raw,
|
56
54
|
goal=res.json_dict["goal"] if "goal" in res.json_dict else task.description
|
@@ -90,5 +88,5 @@ class TeamPlanner:
|
|
90
88
|
""",
|
91
89
|
pydantic_output=TeamPlanIdea
|
92
90
|
)
|
93
|
-
output = task.
|
91
|
+
output = task.execute(agent=team_planner, context=context, tools=tools)
|
94
92
|
return output
|