versionhq 1.1.6.4__py3-none-any.whl → 1.1.7.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 +1 -1
- versionhq/_utils/usage_metrics.py +4 -12
- versionhq/agent/TEMPLATES/Backstory.py +3 -0
- versionhq/agent/TEMPLATES/__init__.py +0 -0
- versionhq/agent/model.py +96 -159
- versionhq/task/model.py +195 -157
- versionhq/team/model.py +78 -153
- versionhq/team/team_planner.py +2 -2
- versionhq/tool/model.py +6 -16
- {versionhq-1.1.6.4.dist-info → versionhq-1.1.7.1.dist-info}/METADATA +63 -44
- {versionhq-1.1.6.4.dist-info → versionhq-1.1.7.1.dist-info}/RECORD +14 -12
- {versionhq-1.1.6.4.dist-info → versionhq-1.1.7.1.dist-info}/LICENSE +0 -0
- {versionhq-1.1.6.4.dist-info → versionhq-1.1.7.1.dist-info}/WHEEL +0 -0
- {versionhq-1.1.6.4.dist-info → versionhq-1.1.7.1.dist-info}/top_level.txt +0 -0
versionhq/task/model.py
CHANGED
@@ -3,16 +3,10 @@ import threading
|
|
3
3
|
import uuid
|
4
4
|
from concurrent.futures import Future
|
5
5
|
from hashlib import md5
|
6
|
-
from typing import Any, Dict, List, Set, Optional, Tuple, Callable
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
BaseModel,
|
11
|
-
Field,
|
12
|
-
PrivateAttr,
|
13
|
-
field_validator,
|
14
|
-
model_validator,
|
15
|
-
)
|
6
|
+
from typing import Any, Dict, List, Set, Optional, Tuple, Callable, Union, Type
|
7
|
+
from typing_extensions import Annotated
|
8
|
+
|
9
|
+
from pydantic import UUID4, BaseModel, Field, PrivateAttr, field_validator, model_validator, create_model
|
16
10
|
from pydantic_core import PydanticCustomError
|
17
11
|
|
18
12
|
from versionhq._utils.process_config import process_config
|
@@ -26,62 +20,73 @@ class ResponseField(BaseModel):
|
|
26
20
|
"""
|
27
21
|
|
28
22
|
title: str = Field(default=None)
|
29
|
-
type:
|
23
|
+
type: Type = Field(default=str)
|
30
24
|
required: bool = Field(default=True)
|
31
25
|
|
32
26
|
|
27
|
+
def _annotate(self, value: Any) -> Annotated:
|
28
|
+
"""
|
29
|
+
Address `create_model`
|
30
|
+
"""
|
31
|
+
return Annotated[self.type, value] if isinstance(value, self.type) else Annotated[str, str(value)]
|
32
|
+
|
33
|
+
|
34
|
+
def _convert(self, value: Any) -> Any:
|
35
|
+
try:
|
36
|
+
if self.type is Any:
|
37
|
+
pass
|
38
|
+
elif self.type is int:
|
39
|
+
return int(value)
|
40
|
+
elif self.type is float:
|
41
|
+
return float(value)
|
42
|
+
elif self.type is list or self.type is dict:
|
43
|
+
return json.loads(value)
|
44
|
+
else:
|
45
|
+
return value
|
46
|
+
except:
|
47
|
+
return value
|
48
|
+
|
49
|
+
|
50
|
+
def create_pydantic_model(self, result: Dict, base_model: Union[BaseModel | Any]) -> Any:
|
51
|
+
for k, v in result.items():
|
52
|
+
if k is not self.title:
|
53
|
+
pass
|
54
|
+
elif type(v) is not self.type:
|
55
|
+
v = self._convert(v)
|
56
|
+
setattr(base_model, k, v)
|
57
|
+
else:
|
58
|
+
setattr(base_model, k, v)
|
59
|
+
return base_model
|
60
|
+
|
61
|
+
|
62
|
+
class AgentOutput(BaseModel):
|
63
|
+
"""
|
64
|
+
Keep adding agents' learning and recommendation and store it in `pydantic` field of `TaskOutput` class.
|
65
|
+
Since the TaskOutput class has `agent` field, we don't add any info on the agent that handled the task.
|
66
|
+
"""
|
67
|
+
customer_id: str = Field(default=None, max_length=126, description="customer uuid")
|
68
|
+
customer_analysis: str = Field(default=None, max_length=256, description="analysis of the customer")
|
69
|
+
product_overview: str = Field(default=None, max_length=256, description="analysis of the client's business")
|
70
|
+
usp: str = Field()
|
71
|
+
cohort_timeframe: int = Field(default=None, max_length=256, description="suitable cohort timeframe in days")
|
72
|
+
kpi_metrics: List[str] = Field(default=list, description="Ideal KPIs to be tracked")
|
73
|
+
assumptions: List[Dict[str, Any]] = Field(default=list, description="assumptions to test")
|
74
|
+
|
75
|
+
|
76
|
+
|
33
77
|
class TaskOutput(BaseModel):
|
34
78
|
"""
|
35
79
|
Store the final output of the task in TaskOutput class.
|
36
80
|
Depending on the task output format, use `raw`, `pydantic`, `json_dict` accordingly.
|
37
81
|
"""
|
38
82
|
|
39
|
-
|
40
|
-
"""
|
41
|
-
Keep adding agents' learning and recommendation and store it in `pydantic` field of `TaskOutput` class.
|
42
|
-
Since the TaskOutput class has `agent` field, we don't add any info on the agent that handled the task.
|
43
|
-
"""
|
44
|
-
|
45
|
-
customer_id: str = Field(
|
46
|
-
default=None, max_length=126, description="customer uuid"
|
47
|
-
)
|
48
|
-
customer_analysis: str = Field(
|
49
|
-
default=None, max_length=256, description="analysis of the customer"
|
50
|
-
)
|
51
|
-
business_overview: str = Field(
|
52
|
-
default=None,
|
53
|
-
max_length=256,
|
54
|
-
description="analysis of the client's business",
|
55
|
-
)
|
56
|
-
cohort_timeframe: int = Field(
|
57
|
-
default=None,
|
58
|
-
max_length=256,
|
59
|
-
description="Suitable cohort timeframe in days",
|
60
|
-
)
|
61
|
-
kpi_metrics: List[str] = Field(
|
62
|
-
default=list, description="Ideal KPIs to be tracked"
|
63
|
-
)
|
64
|
-
assumptions: List[Dict[str, Any]] = Field(
|
65
|
-
default=list, description="assumptions to test"
|
66
|
-
)
|
67
|
-
|
68
|
-
task_id: UUID4 = Field(
|
69
|
-
default_factory=uuid.uuid4, frozen=True, description="store Task ID"
|
70
|
-
)
|
83
|
+
task_id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="store Task ID")
|
71
84
|
raw: str = Field(default="", description="Raw output of the task")
|
72
|
-
|
73
|
-
|
74
|
-
)
|
75
|
-
json_dict: Optional[Dict[str, Any]] = Field(
|
76
|
-
default=None, description="JSON dictionary of task"
|
77
|
-
)
|
85
|
+
json_dict: Union[Dict[str, Any]] = Field(default=None, description="`raw` converted to dictionary")
|
86
|
+
pydantic: Optional[Any] = Field(default=None, description="`raw` converted to the abs. pydantic model")
|
78
87
|
|
79
88
|
def __str__(self) -> str:
|
80
|
-
return (
|
81
|
-
str(self.pydantic)
|
82
|
-
if self.pydantic
|
83
|
-
else str(self.json_dict) if self.json_dict else self.raw
|
84
|
-
)
|
89
|
+
return str(self.pydantic) if self.pydantic else str(self.json_dict) if self.json_dict else self.raw
|
85
90
|
|
86
91
|
@property
|
87
92
|
def json(self) -> Optional[str]:
|
@@ -95,16 +100,31 @@ class TaskOutput(BaseModel):
|
|
95
100
|
)
|
96
101
|
return json.dumps(self.json_dict)
|
97
102
|
|
103
|
+
|
98
104
|
def to_dict(self) -> Dict[str, Any]:
|
99
|
-
"""
|
105
|
+
"""
|
106
|
+
Convert pydantic / raw output into dict and return the dict.
|
107
|
+
When we only have `raw` output, return `{ output: raw }` to avoid an error
|
108
|
+
"""
|
109
|
+
|
100
110
|
output_dict = {}
|
101
111
|
if self.json_dict:
|
102
112
|
output_dict.update(self.json_dict)
|
103
113
|
elif self.pydantic:
|
104
114
|
output_dict.update(self.pydantic.model_dump())
|
115
|
+
else:
|
116
|
+
output_dict.upate({ "output": self.raw })
|
105
117
|
return output_dict
|
106
118
|
|
107
119
|
|
120
|
+
def context_prompting(self) -> str:
|
121
|
+
"""
|
122
|
+
When the task is called as context, return its output in concise string to add it to the prompt
|
123
|
+
"""
|
124
|
+
return json.dumps(self.json_dict) if self.json_dict else self.raw[0: 127]
|
125
|
+
|
126
|
+
|
127
|
+
|
108
128
|
class Task(BaseModel):
|
109
129
|
"""
|
110
130
|
Task to be executed by the agent or the team.
|
@@ -114,51 +134,29 @@ class Task(BaseModel):
|
|
114
134
|
|
115
135
|
__hash__ = object.__hash__
|
116
136
|
|
117
|
-
id: UUID4 = Field(
|
118
|
-
default_factory=uuid.uuid4,
|
119
|
-
frozen=True,
|
120
|
-
description="unique identifier for the object, not set by user",
|
121
|
-
)
|
137
|
+
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="unique identifier for the object, not set by user")
|
122
138
|
name: Optional[str] = Field(default=None)
|
123
139
|
description: str = Field(description="Description of the actual task")
|
124
140
|
_original_description: str = PrivateAttr(default=None)
|
125
141
|
|
126
142
|
# output
|
127
|
-
expected_output_raw: bool = Field(default=False)
|
128
143
|
expected_output_json: bool = Field(default=True)
|
129
144
|
expected_output_pydantic: bool = Field(default=False)
|
130
|
-
output_field_list:
|
131
|
-
default=[
|
132
|
-
|
133
|
-
]
|
134
|
-
)
|
135
|
-
output: Optional[TaskOutput] = Field(
|
136
|
-
default=None, description="store the final task output in TaskOutput class"
|
145
|
+
output_field_list: List[ResponseField] = Field(
|
146
|
+
default=[ResponseField(title="output", type=str, required=False)],
|
147
|
+
description="provide output key and data type. this will be cascaded to the agent via task.prompt()"
|
137
148
|
)
|
149
|
+
output: Optional[TaskOutput] = Field(default=None, description="store the final task output in TaskOutput class")
|
138
150
|
|
139
151
|
# task setup
|
140
|
-
context: Optional[List["Task"]] = Field(
|
141
|
-
|
142
|
-
)
|
143
|
-
tools_called: Optional[List[ToolCalled]] = Field(
|
144
|
-
default_factory=list, description="tools that the agent can use for this task"
|
145
|
-
)
|
146
|
-
take_tool_res_as_final: bool = Field(
|
147
|
-
default=False,
|
148
|
-
description="when set True, tools res will be stored in the `TaskOutput`",
|
149
|
-
)
|
152
|
+
context: Optional[List["Task"]] = Field(default=None, description="other tasks whose outputs should be used as context")
|
153
|
+
tools_called: Optional[List[ToolCalled]] = Field(default_factory=list, description="tools that the agent can use for this task")
|
154
|
+
take_tool_res_as_final: bool = Field(default=False,description="when set True, tools res will be stored in the `TaskOutput`")
|
150
155
|
|
151
156
|
prompt_context: Optional[str] = None
|
152
|
-
async_execution: bool = Field(
|
153
|
-
|
154
|
-
|
155
|
-
)
|
156
|
-
config: Optional[Dict[str, Any]] = Field(
|
157
|
-
default=None, description="configuration for the agent"
|
158
|
-
)
|
159
|
-
callback: Optional[Any] = Field(
|
160
|
-
default=None, description="callback to be executed after the task is completed."
|
161
|
-
)
|
157
|
+
async_execution: bool = Field(default=False,description="whether the task should be executed asynchronously or not")
|
158
|
+
config: Optional[Dict[str, Any]] = Field(default=None, description="configuration for the agent")
|
159
|
+
callback: Optional[Any] = Field(default=None, description="callback to be executed after the task is completed.")
|
162
160
|
|
163
161
|
# recording
|
164
162
|
processed_by_agents: Set[str] = Field(default_factory=set)
|
@@ -166,33 +164,38 @@ class Task(BaseModel):
|
|
166
164
|
tools_errors: int = 0
|
167
165
|
delegations: int = 0
|
168
166
|
|
167
|
+
|
169
168
|
@property
|
170
|
-
def output_prompt(self):
|
169
|
+
def output_prompt(self) -> str:
|
171
170
|
"""
|
172
171
|
Draft prompts on the output format by converting `output_field_list` to dictionary.
|
173
172
|
"""
|
174
173
|
|
175
|
-
output_prompt,
|
174
|
+
output_prompt, output_formats_to_follow = "", dict()
|
176
175
|
for item in self.output_field_list:
|
177
|
-
|
176
|
+
output_formats_to_follow[item.title] = f"<Return your answer in {item.type.__name__}>"
|
178
177
|
|
179
178
|
output_prompt = f"""
|
180
|
-
|
181
|
-
|
179
|
+
Your outputs MUST adhere to the following format and should NOT include any irrelevant elements:
|
180
|
+
{output_formats_to_follow}
|
182
181
|
"""
|
183
182
|
return output_prompt
|
184
183
|
|
184
|
+
|
185
185
|
@property
|
186
186
|
def expected_output_formats(self) -> List[TaskOutputFormat]:
|
187
|
-
|
187
|
+
"""
|
188
|
+
Return output formats in list with the ENUM item.
|
189
|
+
`TaskOutputFormat.RAW` is set as default.
|
190
|
+
"""
|
191
|
+
outputs = [TaskOutputFormat.RAW,]
|
188
192
|
if self.expected_output_json:
|
189
193
|
outputs.append(TaskOutputFormat.JSON)
|
190
194
|
if self.expected_output_pydantic:
|
191
195
|
outputs.append(TaskOutputFormat.PYDANTIC)
|
192
|
-
if self.expected_output_raw:
|
193
|
-
outputs.append(TaskOutputFormat.RAW)
|
194
196
|
return outputs
|
195
197
|
|
198
|
+
|
196
199
|
@property
|
197
200
|
def key(self) -> str:
|
198
201
|
output_format = (
|
@@ -207,6 +210,7 @@ class Task(BaseModel):
|
|
207
210
|
source = [self.description, output_format]
|
208
211
|
return md5("|".join(source).encode(), usedforsecurity=False).hexdigest()
|
209
212
|
|
213
|
+
|
210
214
|
@property
|
211
215
|
def summary(self) -> str:
|
212
216
|
return f"""
|
@@ -216,32 +220,30 @@ class Task(BaseModel):
|
|
216
220
|
"task_tools": {", ".join([tool_called.tool.name for tool_called in self.tools_called])}
|
217
221
|
"""
|
218
222
|
|
223
|
+
|
219
224
|
# validators
|
220
225
|
@model_validator(mode="before")
|
221
226
|
@classmethod
|
222
227
|
def process_model_config(cls, values: Dict[str, Any]):
|
223
228
|
return process_config(values_to_update=values, model_class=cls)
|
224
229
|
|
230
|
+
|
225
231
|
@field_validator("id", mode="before")
|
226
232
|
@classmethod
|
227
233
|
def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
|
228
234
|
if v:
|
229
|
-
raise PydanticCustomError(
|
230
|
-
|
231
|
-
)
|
235
|
+
raise PydanticCustomError("may_not_set_field", "This field is not to be set by the user.", {})
|
236
|
+
|
232
237
|
|
233
238
|
@model_validator(mode="after")
|
234
239
|
def validate_required_fields(self):
|
235
|
-
required_fields = [
|
236
|
-
"description",
|
237
|
-
]
|
240
|
+
required_fields = ["description",]
|
238
241
|
for field in required_fields:
|
239
242
|
if getattr(self, field) is None:
|
240
|
-
raise ValueError(
|
241
|
-
f"{field} must be provided either directly or through config"
|
242
|
-
)
|
243
|
+
raise ValueError( f"{field} must be provided either directly or through config")
|
243
244
|
return self
|
244
245
|
|
246
|
+
|
245
247
|
@model_validator(mode="after")
|
246
248
|
def set_attributes_based_on_config(self) -> "Task":
|
247
249
|
"""
|
@@ -253,15 +255,14 @@ class Task(BaseModel):
|
|
253
255
|
setattr(self, key, value)
|
254
256
|
return self
|
255
257
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
return self
|
258
|
+
|
259
|
+
## comment out as we set raw as the default TaskOutputFormat
|
260
|
+
# @model_validator(mode="after")
|
261
|
+
# def validate_output_format(self):
|
262
|
+
# if self.expected_output_json == False and self.expected_output_pydantic == False:
|
263
|
+
# raise PydanticCustomError("Need to choose at least one output format.")
|
264
|
+
# return self
|
265
|
+
|
265
266
|
|
266
267
|
@model_validator(mode="after")
|
267
268
|
def backup_description(self):
|
@@ -269,57 +270,84 @@ class Task(BaseModel):
|
|
269
270
|
self._original_description = self.description
|
270
271
|
return self
|
271
272
|
|
272
|
-
|
273
|
+
|
274
|
+
def prompt(self, customer: str = None, product_overview: str = None) -> str:
|
273
275
|
"""
|
274
|
-
|
276
|
+
Format the task prompt and cascade it to the agent.
|
277
|
+
When the task has context, add context prompting of all the tasks in the context.
|
278
|
+
When we have cusotmer/product info, add them to the prompt.
|
275
279
|
"""
|
276
280
|
|
277
|
-
task_slices = [
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
f"
|
282
|
-
|
281
|
+
task_slices = [self.description, f"{self.output_prompt}"]
|
282
|
+
|
283
|
+
if self.context:
|
284
|
+
context_outputs = "\n".join([task.output.context_prompting() if hasattr(task, "output") else "" for task in self.context])
|
285
|
+
task_slices.insert(1, f"Take the following context into consideration: {context_outputs}")
|
286
|
+
|
287
|
+
if customer:
|
288
|
+
task_slices.insert(1, f"customer overview: {customer}")
|
289
|
+
|
290
|
+
if product_overview:
|
291
|
+
task_slices.insert(1, f"Product overview: {product_overview}")
|
292
|
+
|
283
293
|
return "\n".join(task_slices)
|
284
294
|
|
285
|
-
def _export_output(
|
286
|
-
self, result: Any
|
287
|
-
) -> Tuple[Optional[BaseModel], Optional[Dict[str, Any]]]:
|
288
|
-
output_pydantic: Optional[BaseModel] = None
|
289
|
-
output_json: Optional[Dict[str, Any]] = None
|
290
|
-
dict_output = None
|
291
295
|
|
292
|
-
|
296
|
+
def create_json_output(self, raw_result: Any) -> Optional[Dict[str, Any]]:
|
297
|
+
"""
|
298
|
+
Create json (dict) output from the raw result.
|
299
|
+
"""
|
300
|
+
|
301
|
+
output_json_dict: Optional[Dict[str, Any]] = None
|
302
|
+
|
303
|
+
if isinstance(raw_result, BaseModel):
|
304
|
+
output_json_dict = raw_result.model_dump()
|
305
|
+
|
306
|
+
elif isinstance(raw_result, dict):
|
307
|
+
output_json_dict = raw_result
|
308
|
+
|
309
|
+
elif isinstance(raw_result, str):
|
293
310
|
try:
|
294
|
-
|
311
|
+
output_json_dict = json.loads(raw_result)
|
295
312
|
except json.JSONDecodeError:
|
296
313
|
try:
|
297
|
-
|
314
|
+
output_json_dict = eval(raw_result)
|
298
315
|
except:
|
299
316
|
try:
|
300
317
|
import ast
|
301
|
-
|
302
|
-
dict_output = ast.literal_eval(result)
|
318
|
+
output_json_dict = ast.literal_eval(raw_result)
|
303
319
|
except:
|
304
|
-
|
320
|
+
output_json_dict = { "output": raw_result }
|
305
321
|
|
306
|
-
|
307
|
-
if isinstance(result, dict):
|
308
|
-
output_json = result
|
309
|
-
elif isinstance(result, BaseModel):
|
310
|
-
output_json = result.model_dump()
|
311
|
-
else:
|
312
|
-
output_json = dict_output
|
322
|
+
return output_json_dict
|
313
323
|
|
314
|
-
if self.expected_output_pydantic:
|
315
|
-
if isinstance(result, BaseModel):
|
316
|
-
output_pydantic = result
|
317
|
-
elif isinstance(result, dict):
|
318
|
-
output_json = result
|
319
|
-
else:
|
320
|
-
output_pydantic = None
|
321
324
|
|
322
|
-
|
325
|
+
|
326
|
+
def create_pydantic_output(self, output_json_dict: Dict[str, Any], raw_result: Any = None) -> Optional[Any]:
|
327
|
+
"""
|
328
|
+
Create pydantic output from the `raw` result.
|
329
|
+
"""
|
330
|
+
|
331
|
+
output_pydantic = None
|
332
|
+
if isinstance(raw_result, BaseModel):
|
333
|
+
output_pydantic = raw_result
|
334
|
+
|
335
|
+
elif hasattr(output_json_dict, "output"):
|
336
|
+
output_pydantic = create_model("PydanticTaskOutput", output=output_json_dict["output"], __base__=BaseModel)
|
337
|
+
|
338
|
+
else:
|
339
|
+
output_pydantic = create_model("PydanticTaskOutput", __base__=BaseModel)
|
340
|
+
try:
|
341
|
+
for item in self.output_field_list:
|
342
|
+
value = output_json_dict[item.title] if hasattr(output_json_dict, item.title) else None
|
343
|
+
if value and type(value) is not item.type:
|
344
|
+
value = item._convert(value)
|
345
|
+
setattr(output_pydantic, item.title, value)
|
346
|
+
except:
|
347
|
+
setattr(output_pydantic, "output", output_json_dict)
|
348
|
+
|
349
|
+
return output_pydantic
|
350
|
+
|
323
351
|
|
324
352
|
def _get_output_format(self) -> TaskOutputFormat:
|
325
353
|
if self.output_json == True:
|
@@ -328,6 +356,7 @@ class Task(BaseModel):
|
|
328
356
|
return TaskOutputFormat.PYDANTIC
|
329
357
|
return TaskOutputFormat.RAW
|
330
358
|
|
359
|
+
|
331
360
|
def interpolate_inputs(self, inputs: Dict[str, Any]) -> None:
|
332
361
|
"""
|
333
362
|
Interpolate inputs into the task description and expected output.
|
@@ -336,13 +365,22 @@ class Task(BaseModel):
|
|
336
365
|
self.description = self._original_description.format(**inputs)
|
337
366
|
# self.expected_output = self._original_expected_output.format(**inputs)
|
338
367
|
|
368
|
+
|
339
369
|
# task execution
|
340
370
|
def execute_sync(self, agent, context: Optional[str] = None) -> TaskOutput:
|
341
371
|
"""
|
342
372
|
Execute the task synchronously.
|
373
|
+
When the task has context, make sure we have executed all the tasks in the context first.
|
343
374
|
"""
|
375
|
+
|
376
|
+
if self.context:
|
377
|
+
for task in self.context:
|
378
|
+
if task.output is None:
|
379
|
+
task._execute_core(agent, context)
|
380
|
+
|
344
381
|
return self._execute_core(agent, context)
|
345
382
|
|
383
|
+
|
346
384
|
def execute_async(self, agent, context: Optional[str] = None) -> Future[TaskOutput]:
|
347
385
|
"""
|
348
386
|
Execute the task asynchronously.
|
@@ -356,26 +394,27 @@ class Task(BaseModel):
|
|
356
394
|
).start()
|
357
395
|
return future
|
358
396
|
|
359
|
-
|
360
|
-
|
361
|
-
) -> None:
|
397
|
+
|
398
|
+
def _execute_task_async(self, agent, context: Optional[str], future: Future[TaskOutput]) -> None:
|
362
399
|
"""Execute the task asynchronously with context handling."""
|
363
400
|
result = self._execute_core(agent, context)
|
364
401
|
future.set_result(result)
|
365
402
|
|
403
|
+
|
366
404
|
def _execute_core(self, agent, context: Optional[str]) -> TaskOutput:
|
367
405
|
"""
|
368
406
|
Run the core execution logic of the task.
|
369
407
|
"""
|
370
408
|
|
371
409
|
self.prompt_context = context
|
372
|
-
|
373
|
-
|
410
|
+
output_raw = agent.execute_task(task=self, context=context)
|
411
|
+
output_json_dict = self.create_json_output(raw_result=output_raw)
|
412
|
+
output_pydantic = self.create_pydantic_output(output_json_dict=output_json_dict)
|
374
413
|
task_output = TaskOutput(
|
375
414
|
task_id=self.id,
|
376
|
-
raw=
|
415
|
+
raw=output_raw,
|
377
416
|
pydantic=output_pydantic,
|
378
|
-
json_dict=
|
417
|
+
json_dict=output_json_dict
|
379
418
|
)
|
380
419
|
self.output = task_output
|
381
420
|
self.processed_by_agents.add(agent.role)
|
@@ -396,7 +435,6 @@ class Task(BaseModel):
|
|
396
435
|
# else pydantic_output.model_dump_json() if pydantic_output else result
|
397
436
|
# )
|
398
437
|
# self._save_file(content)
|
399
|
-
|
400
438
|
return task_output
|
401
439
|
|
402
440
|
|