versionhq 1.1.12.4__py3-none-any.whl → 1.1.13.0__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 +6 -3
- versionhq/_utils/usage_metrics.py +6 -6
- versionhq/agent/inhouse_agents.py +1 -1
- versionhq/agent/model.py +1 -1
- versionhq/llm/llm_vars.py +5 -0
- versionhq/llm/model.py +66 -40
- versionhq/task/evaluate.py +5 -6
- versionhq/task/formation.py +64 -30
- versionhq/task/log_handler.py +1 -1
- versionhq/task/model.py +4 -7
- versionhq/team/model.py +95 -92
- versionhq/team/team_planner.py +5 -6
- versionhq/tool/model.py +1 -4
- {versionhq-1.1.12.4.dist-info → versionhq-1.1.13.0.dist-info}/METADATA +116 -141
- {versionhq-1.1.12.4.dist-info → versionhq-1.1.13.0.dist-info}/RECORD +18 -18
- {versionhq-1.1.12.4.dist-info → versionhq-1.1.13.0.dist-info}/LICENSE +0 -0
- {versionhq-1.1.12.4.dist-info → versionhq-1.1.13.0.dist-info}/WHEEL +0 -0
- {versionhq-1.1.12.4.dist-info → versionhq-1.1.13.0.dist-info}/top_level.txt +0 -0
versionhq/__init__.py
CHANGED
@@ -4,6 +4,9 @@ warnings.filterwarnings(action="ignore", message="Pydantic serializer warnings:"
|
|
4
4
|
warnings.filterwarnings(action="ignore", category=UserWarning, module="pydantic._internal")
|
5
5
|
warnings.filterwarnings(action="ignore", module="LiteLLM:utils")
|
6
6
|
|
7
|
+
from dotenv import load_dotenv
|
8
|
+
load_dotenv(override=True)
|
9
|
+
|
7
10
|
from versionhq.agent.model import Agent
|
8
11
|
from versionhq.clients.customer.model import Customer
|
9
12
|
from versionhq.clients.product.model import Product, ProductProvider
|
@@ -13,7 +16,7 @@ from versionhq.knowledge.source import PDFKnowledgeSource, CSVKnowledgeSource, J
|
|
13
16
|
from versionhq.knowledge.source_docling import DoclingSource
|
14
17
|
from versionhq.task.model import Task, TaskOutput, ConditionalTask, ResponseField
|
15
18
|
from versionhq.task.evaluate import Evaluation, EvaluationItem
|
16
|
-
from versionhq.team.model import Team, TeamOutput, Formation,
|
19
|
+
from versionhq.team.model import Team, TeamOutput, Formation, Member, TaskHandlingProcess
|
17
20
|
from versionhq.tool.model import Tool, ToolSet
|
18
21
|
from versionhq.tool.cache_handler import CacheHandler
|
19
22
|
from versionhq.tool.tool_handler import ToolHandler
|
@@ -24,7 +27,7 @@ from versionhq.memory.model import ShortTermMemory,LongTermMemory, UserMemory, M
|
|
24
27
|
from versionhq.task.formation import form_agent_network
|
25
28
|
|
26
29
|
|
27
|
-
__version__ = "1.1.
|
30
|
+
__version__ = "1.1.13.0"
|
28
31
|
__all__ = [
|
29
32
|
"Agent",
|
30
33
|
|
@@ -55,7 +58,7 @@ __all__ = [
|
|
55
58
|
"Team",
|
56
59
|
"TeamOutput",
|
57
60
|
"Formation",
|
58
|
-
"
|
61
|
+
"Member",
|
59
62
|
"TaskHandlingProcess",
|
60
63
|
|
61
64
|
"Tool",
|
@@ -3,14 +3,14 @@ from pydantic import BaseModel, Field
|
|
3
3
|
|
4
4
|
class UsageMetrics(BaseModel):
|
5
5
|
"""
|
6
|
-
Model to track usage
|
6
|
+
Model to track usage
|
7
7
|
"""
|
8
8
|
|
9
|
-
total_tokens: int = Field(default=0, description="
|
10
|
-
prompt_tokens: int = Field(default=0, description="
|
11
|
-
cached_prompt_tokens: int = Field(default=0, description="
|
12
|
-
completion_tokens: int = Field(default=0, description="
|
13
|
-
successful_requests: int = Field(default=0, description="
|
9
|
+
total_tokens: int = Field(default=0, description="total number of tokens used")
|
10
|
+
prompt_tokens: int = Field(default=0, description="number of tokens used in prompts")
|
11
|
+
cached_prompt_tokens: int = Field(default=0, description="number of cached prompt tokens used")
|
12
|
+
completion_tokens: int = Field(default=0, description="number of tokens used in completions")
|
13
|
+
successful_requests: int = Field(default=0, description="number of successful requests made")
|
14
14
|
|
15
15
|
def add_usage_metrics(self, usage_metrics: "UsageMetrics") -> None:
|
16
16
|
"""
|
@@ -29,7 +29,7 @@ vhq_formation_planner = Agent(
|
|
29
29
|
role="vhq-Formation Planner",
|
30
30
|
goal="Plan a formation of agents based on the given task descirption.",
|
31
31
|
llm="gemini/gemini-2.0-flash-exp",
|
32
|
-
llm_config=dict(top_p=0.8,
|
32
|
+
llm_config=dict(top_p=0.8, topK=40, temperature=0.9),
|
33
33
|
maxit=1,
|
34
34
|
max_retry_limit=1,
|
35
35
|
knowledge_sources=[
|
versionhq/agent/model.py
CHANGED
versionhq/llm/llm_vars.py
CHANGED
@@ -55,6 +55,11 @@ MODELS = {
|
|
55
55
|
"openrouter": [
|
56
56
|
"openrouter/deepseek/deepseek-r1",
|
57
57
|
"openrouter/qwen/qwen-2.5-72b-instruct",
|
58
|
+
"openrouter/google/gemini-2.0-flash-thinking-exp:free",
|
59
|
+
"openrouter/google/gemini-2.0-flash-thinking-exp-1219:free",
|
60
|
+
"openrouter/google/gemini-2.0-flash-001",
|
61
|
+
"openrouter/meta-llama/llama-3.3-70b-instruct",
|
62
|
+
"openrouter/mistralai/mistral-large-2411",
|
58
63
|
],
|
59
64
|
"huggingface": [
|
60
65
|
"huggingface/qwen/qwen2.5-VL-72B-Instruct",
|
versionhq/llm/model.py
CHANGED
@@ -100,7 +100,7 @@ class LLM(BaseModel):
|
|
100
100
|
# LiteLLM specific fields
|
101
101
|
api_base: Optional[str] = Field(default=None, description="litellm specific field - api base of the model provider")
|
102
102
|
api_version: Optional[str] = Field(default=None)
|
103
|
-
num_retries: Optional[int] = Field(default=
|
103
|
+
num_retries: Optional[int] = Field(default=1)
|
104
104
|
context_window_fallback_dict: Optional[Dict[str, Any]] = Field(default=None, description="A mapping of model to use if call fails due to context window error")
|
105
105
|
fallbacks: Optional[List[Any]]= Field(default=None, description="A list of model names + params to be used, in case the initial call fails")
|
106
106
|
metadata: Optional[Dict[str, Any]] = Field(default=None)
|
@@ -256,60 +256,86 @@ class LLM(BaseModel):
|
|
256
256
|
self._set_callbacks(self.callbacks) # passed by agent
|
257
257
|
|
258
258
|
try:
|
259
|
-
|
259
|
+
res, tool_res = None, ""
|
260
260
|
|
261
261
|
if not tools:
|
262
|
+
self.response_format = response_format
|
262
263
|
params = self._create_valid_params(config=config)
|
263
264
|
res = litellm.completion(model=self.model, messages=messages, stream=False, **params)
|
264
265
|
self._tokens += int(res["usage"]["total_tokens"])
|
265
266
|
return res["choices"][0]["message"]["content"]
|
266
267
|
|
267
268
|
else:
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
269
|
+
try:
|
270
|
+
self.response_format = { "type": "json_object" } if tool_res_as_final and self.provider != "gemini" else response_format
|
271
|
+
self.tools = [item.tool.properties if isinstance(item, ToolSet) else item.properties for item in tools]
|
272
|
+
params = self._create_valid_params(config=config)
|
273
|
+
res = litellm.completion(model=self.model, messages=messages, **params)
|
274
|
+
tool_calls = res.choices[0].message.tool_calls
|
275
|
+
|
276
|
+
if tool_calls:
|
277
|
+
for item in tool_calls:
|
278
|
+
func_name = item.function.name
|
279
|
+
func_args = item.function.arguments
|
280
|
+
if not isinstance(func_args, dict):
|
281
|
+
try:
|
282
|
+
func_args = json.loads(json.dumps(eval(str(func_args))))
|
283
|
+
except:
|
284
|
+
pass
|
285
|
+
|
286
|
+
# find a tool whose name is matched with the retrieved func_name
|
287
|
+
matches = []
|
288
|
+
for tool in tools:
|
289
|
+
tool_name = tool.tool.name if isinstance(tool, ToolSet) else tool.name
|
290
|
+
tool_func_name = tool.tool.func.__name__ if isinstance(tool, ToolSet) else tool.func.__name__
|
291
|
+
if tool_name.replace(" ", "_") == func_name or tool_func_name == func_name or tool_name == "random_func":
|
292
|
+
matches.append(tool)
|
293
|
+
else:
|
294
|
+
pass
|
295
|
+
|
296
|
+
if matches:
|
297
|
+
tool_to_execute = matches[0]
|
298
|
+
tool_instance = tool_to_execute.tool if isinstance(tool_to_execute, ToolSet) else tool_to_execute
|
299
|
+
params = tool_to_execute.kwargs if isinstance(tool_to_execute, ToolSet) else func_args
|
300
|
+
tool_res_to_add = tool_instance.run(params=params) if params else tool_instance.run()
|
295
301
|
|
296
|
-
else:
|
297
|
-
try:
|
298
|
-
tool_res_to_add = tool.run(params=func_args)
|
299
302
|
if tool_res_as_final:
|
300
|
-
|
303
|
+
if tool_res_to_add not in tool_res:
|
304
|
+
tool_res += str(tool_res_to_add)
|
301
305
|
else:
|
302
306
|
messages.append(res.choices[0].message)
|
303
307
|
messages.append({ "role": "tool", "tool_call_id": item.id, "content": str(tool_res_to_add) })
|
304
|
-
|
308
|
+
|
309
|
+
else:
|
310
|
+
if tool_res_as_final and tools and not tool_res:
|
311
|
+
for item in tools:
|
312
|
+
tool_res_to_add = item.tool.run(params=item.kwargs) if isinstance(item, ToolSet) else item.run()
|
313
|
+
if tool_res_to_add not in tool_res:
|
314
|
+
tool_res += str(tool_res_to_add)
|
315
|
+
else:
|
316
|
+
pass
|
317
|
+
|
318
|
+
except:
|
319
|
+
if tool_res_as_final and tools and not tool_res:
|
320
|
+
for item in tools:
|
321
|
+
tool_res_to_add = item.tool.run(params=item.kwargs) if isinstance(item, ToolSet) else item.run()
|
322
|
+
if tool_res_to_add not in tool_res:
|
323
|
+
tool_res += str(tool_res_to_add)
|
324
|
+
else:
|
305
325
|
pass
|
326
|
+
elif tools and not tool_res:
|
327
|
+
tool_res = res["choices"][0]["message"]["content"]
|
328
|
+
else:
|
329
|
+
pass
|
330
|
+
|
331
|
+
|
332
|
+
if tool_res_as_final:
|
333
|
+
return tool_res
|
334
|
+
else:
|
335
|
+
res = litellm.completion(model=self.model, messages=messages, **params)
|
336
|
+
self._tokens += int(res["usage"]["total_tokens"])
|
337
|
+
return res.choices[0].message.content
|
306
338
|
|
307
|
-
if tool_res_as_final:
|
308
|
-
return tool_res
|
309
|
-
else:
|
310
|
-
res = litellm.completion(model=self.model, messages=messages, **params)
|
311
|
-
self._tokens += int(res["usage"]["total_tokens"])
|
312
|
-
return res.choices[0].message.content
|
313
339
|
|
314
340
|
except JSONSchemaValidationError as e:
|
315
341
|
self._logger.log(level="error", message="Raw Response: {}".format(e.raw_response), color="red")
|
versionhq/task/evaluate.py
CHANGED
@@ -70,17 +70,16 @@ class EvaluationItem(BaseModel):
|
|
70
70
|
else: return None
|
71
71
|
|
72
72
|
|
73
|
-
|
74
73
|
class Evaluation(BaseModel):
|
75
74
|
items: List[EvaluationItem] = []
|
76
|
-
latency: int = Field(default=None, description="seconds")
|
75
|
+
latency: int = Field(default=None, description="job execution latency in seconds")
|
77
76
|
tokens: int = Field(default=None, description="tokens consumed")
|
78
|
-
|
77
|
+
eval_by: Any = Field(default=None, description="stores agent object that evaluates the outcome")
|
79
78
|
|
80
79
|
@model_validator(mode="after")
|
81
|
-
def
|
80
|
+
def set_up_evaluator(self) -> Self:
|
82
81
|
from versionhq.agent.inhouse_agents import vhq_task_evaluator
|
83
|
-
self.
|
82
|
+
self.eval_by = vhq_task_evaluator
|
84
83
|
return self
|
85
84
|
|
86
85
|
|
@@ -88,7 +87,7 @@ class Evaluation(BaseModel):
|
|
88
87
|
"""
|
89
88
|
Create and store evaluation results in the memory metadata
|
90
89
|
"""
|
91
|
-
eval_by = self.
|
90
|
+
eval_by = self.eval_by.role if self.eval_by else None
|
92
91
|
score = self.aggregate_score
|
93
92
|
eval_criteria = ", ".join([item.criteria for item in self.items]) if self.items else None
|
94
93
|
suggestion = self.suggestion_summary
|
versionhq/task/formation.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
from typing import List
|
1
|
+
from typing import List, Type
|
2
2
|
from enum import Enum
|
3
3
|
|
4
4
|
from pydantic import BaseModel
|
5
5
|
|
6
6
|
from versionhq.task.model import Task
|
7
7
|
from versionhq.agent.model import Agent
|
8
|
-
from versionhq.team.model import Team,
|
8
|
+
from versionhq.team.model import Team, Member, Formation
|
9
9
|
from versionhq.agent.inhouse_agents import vhq_formation_planner
|
10
10
|
from versionhq._utils import Logger
|
11
11
|
|
@@ -15,10 +15,10 @@ def form_agent_network(
|
|
15
15
|
expected_outcome: str,
|
16
16
|
agents: List[Agent] = None,
|
17
17
|
context: str = None,
|
18
|
-
formation: Formation = None
|
18
|
+
formation: Type[Formation] = None
|
19
19
|
) -> Team | None:
|
20
20
|
"""
|
21
|
-
Make a formation of agents from the given task description,
|
21
|
+
Make a formation of agents from the given task description, expected outcome, agents (optional), and context (optional).
|
22
22
|
"""
|
23
23
|
|
24
24
|
if not task:
|
@@ -29,8 +29,37 @@ def form_agent_network(
|
|
29
29
|
Logger(verbose=True).log(level="error", message="Missing expected outcome.", color="red")
|
30
30
|
return None
|
31
31
|
|
32
|
+
if formation:
|
33
|
+
try:
|
34
|
+
match formation:
|
35
|
+
case Formation():
|
36
|
+
if formation == Formation.UNDEFINED:
|
37
|
+
formation = None
|
38
|
+
else:
|
39
|
+
pass
|
40
|
+
|
41
|
+
case str():
|
42
|
+
matched = [item for item in Formation._member_names_ if item == formation.upper()]
|
43
|
+
if matched:
|
44
|
+
formation = getattr(Formation, matched[0])
|
45
|
+
else:
|
46
|
+
# Formation._generate_next_value_(name=f"CUSTOM_{formation.upper()}", start=100, count=6, last_values=Formation.HYBRID.name)
|
47
|
+
Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid. We'll recreate a valid formation.", color="yellow")
|
48
|
+
formation = None
|
49
|
+
|
50
|
+
case int() | float():
|
51
|
+
formation = Formation(int(formation))
|
52
|
+
|
53
|
+
case _:
|
54
|
+
Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid. We'll recreate a valid formation.", color="yellow")
|
55
|
+
formation = None
|
56
|
+
|
57
|
+
except Exception as e:
|
58
|
+
Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid: {str(e)}. We'll recreate a formation.", color="yellow")
|
59
|
+
formation = None
|
32
60
|
|
33
61
|
try:
|
62
|
+
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__)}."
|
34
63
|
class Outcome(BaseModel):
|
35
64
|
formation: Enum
|
36
65
|
agent_roles: list[str]
|
@@ -42,73 +71,78 @@ def form_agent_network(
|
|
42
71
|
Create a team of specialized agents designed to automate the following task and deliver the expected outcome. Consider the necessary roles for each agent with a clear task description. If you think we neeed a leader to handle the automation, return a leader_agent role as well, but if not, leave the a leader_agent role blank.
|
43
72
|
Task: {str(task)}
|
44
73
|
Expected outcome: {str(expected_outcome)}
|
74
|
+
Formation: {prompt_formation}
|
45
75
|
""",
|
46
76
|
pydantic_output=Outcome
|
47
77
|
)
|
48
78
|
|
49
|
-
if formation:
|
50
|
-
vhq_task.description += f"Select 1 formation you think the best from the given Enum sets: {str(Formation.__dict__)}"
|
51
|
-
|
52
79
|
if agents:
|
53
80
|
vhq_task.description += "Consider adding following agents in the formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
|
54
81
|
|
55
82
|
res = vhq_task.execute_sync(agent=vhq_formation_planner, context=context)
|
56
|
-
|
83
|
+
_formation = Formation.SUPERVISING
|
57
84
|
|
58
85
|
if res.pydantic:
|
59
86
|
formation_keys = [k for k, v in Formation._member_map_.items() if k == res.pydantic.formation.upper()]
|
60
87
|
|
61
88
|
if formation_keys:
|
62
|
-
|
89
|
+
_formation = Formation[formation_keys[0]]
|
63
90
|
|
64
91
|
created_agents = [Agent(role=item, goal=item) for item in res.pydantic.agent_roles]
|
65
92
|
created_tasks = [Task(description=item) for item in res.pydantic.task_descriptions]
|
93
|
+
|
66
94
|
team_tasks = []
|
67
95
|
members = []
|
68
96
|
leader = str(res.pydantic.leader_agent)
|
69
97
|
|
70
98
|
for i in range(len(created_agents)):
|
71
|
-
is_manager = bool(created_agents[i].role.lower()
|
72
|
-
member =
|
99
|
+
is_manager = bool(created_agents[i].role.lower() == leader.lower())
|
100
|
+
member = Member(agent=created_agents[i], is_manager=is_manager)
|
101
|
+
|
102
|
+
if len(created_tasks) >= i and created_tasks[i]:
|
103
|
+
member.tasks.append(created_tasks[i])
|
104
|
+
|
105
|
+
members.append(member)
|
73
106
|
|
74
|
-
if len(created_tasks) >= i:
|
75
|
-
member.task = created_tasks[i]
|
76
|
-
members.append(member)
|
77
107
|
|
78
108
|
if len(created_agents) < len(created_tasks):
|
79
|
-
team_tasks.extend(created_tasks[len(created_agents)
|
109
|
+
team_tasks.extend(created_tasks[len(created_agents):len(created_tasks)])
|
80
110
|
|
81
111
|
members.sort(key=lambda x: x.is_manager == False)
|
82
|
-
team = Team(members=members, formation=
|
112
|
+
team = Team( members=members, formation=_formation, team_tasks=team_tasks, planner_llm=vhq_formation_planner.llm)
|
83
113
|
return team
|
84
114
|
|
85
115
|
else:
|
86
|
-
|
116
|
+
res = res.json_dict
|
117
|
+
formation_keys = [k for k, v in Formation._member_map_.items() if k == res["formation"].upper()]
|
87
118
|
|
88
119
|
if formation_keys:
|
89
|
-
|
120
|
+
_formation = Formation[formation_keys[0]]
|
121
|
+
|
122
|
+
created_agents = [Agent(role=item, goal=item) for item in res["agent_roles"]]
|
123
|
+
created_tasks = [Task(description=item) for item in res["task_descriptions"]]
|
90
124
|
|
91
|
-
created_agents = [Agent(role=item, goal=item) for item in res.json_dict["agent_roles"]]
|
92
|
-
created_tasks = [Task(description=item) for item in res.json_dict["task_descriptions"]]
|
93
125
|
team_tasks = []
|
94
126
|
members = []
|
95
|
-
leader = str(res
|
127
|
+
leader = str(res["leader_agent"])
|
96
128
|
|
97
129
|
for i in range(len(created_agents)):
|
98
|
-
is_manager = bool(created_agents[i].role.lower()
|
99
|
-
member =
|
130
|
+
is_manager = bool(created_agents[i].role.lower() == leader.lower())
|
131
|
+
member = Member(agent=created_agents[i], is_manager=is_manager)
|
132
|
+
|
133
|
+
if len(created_tasks) >= i and created_tasks[i]:
|
134
|
+
member.tasks.append(created_tasks[i])
|
100
135
|
|
101
|
-
|
102
|
-
member.task = created_tasks[i]
|
103
|
-
members.append(member)
|
136
|
+
members.append(member)
|
104
137
|
|
105
138
|
if len(created_agents) < len(created_tasks):
|
106
|
-
team_tasks.extend(created_tasks[len(created_agents)
|
139
|
+
team_tasks.extend(created_tasks[len(created_agents):len(created_tasks)])
|
107
140
|
|
108
|
-
members.sort(key=lambda x: x.is_manager ==
|
109
|
-
team = Team(members=members, formation=
|
141
|
+
members.sort(key=lambda x: x.is_manager == False)
|
142
|
+
team = Team( members=members, formation=_formation, team_tasks=team_tasks, planner_llm=vhq_formation_planner.llm)
|
110
143
|
return team
|
111
144
|
|
145
|
+
|
112
146
|
except Exception as e:
|
113
|
-
Logger(verbose=True).log(level="error", message=f"Failed to create
|
147
|
+
Logger(verbose=True).log(level="error", message=f"Failed to create a agent network - return None. You can try with solo agent. Error: {str(e)}", color="red")
|
114
148
|
return None
|
versionhq/task/log_handler.py
CHANGED
@@ -45,7 +45,7 @@ class TaskOutputStorageHandler:
|
|
45
45
|
output_to_store = dict(
|
46
46
|
description=str(task.description),
|
47
47
|
raw=str(task.output.raw),
|
48
|
-
responsible_agent=str(task.
|
48
|
+
responsible_agent=str(task.processed_agents),
|
49
49
|
)
|
50
50
|
|
51
51
|
self.storage.add(task=task, output=output_to_store, task_index=task_index, was_replayed=was_replayed, inputs=inputs)
|
versionhq/task/model.py
CHANGED
@@ -203,7 +203,7 @@ class TaskOutput(BaseModel):
|
|
203
203
|
description=EVALUATE.format(task_description=task.description, task_output=self.raw, eval_criteria=str(item)),
|
204
204
|
pydantic_output=EvaluationItem
|
205
205
|
)
|
206
|
-
res = task_eval.execute_sync(agent=self.evaluation.
|
206
|
+
res = task_eval.execute_sync(agent=self.evaluation.eval_by)
|
207
207
|
|
208
208
|
if res.pydantic:
|
209
209
|
item = EvaluationItem(score=res.pydantic.score, suggestion=res.pydantic.suggestion, criteria=res.pydantic.criteria)
|
@@ -241,10 +241,7 @@ class TaskOutput(BaseModel):
|
|
241
241
|
|
242
242
|
class Task(BaseModel):
|
243
243
|
"""
|
244
|
-
|
245
|
-
Each task must have a description.
|
246
|
-
Default response is JSON string that strictly follows `response_fields` - and will be stored in TaskOuput.raw / json_dict.
|
247
|
-
When `pydantic_output` is provided, we prioritize them and store raw (json string), json_dict, pydantic in the TaskOutput class.
|
244
|
+
A class that stores independent task information.
|
248
245
|
"""
|
249
246
|
|
250
247
|
__hash__ = object.__hash__
|
@@ -282,7 +279,7 @@ class Task(BaseModel):
|
|
282
279
|
eval_criteria: Optional[List[str]] = Field(default_factory=list, description="criteria to evaluate the outcome. i.e., fit to the brand tone")
|
283
280
|
|
284
281
|
# recording
|
285
|
-
|
282
|
+
processed_agents: Set[str] = Field(default_factory=set, description="store roles of the agents that executed the task")
|
286
283
|
tools_errors: int = 0
|
287
284
|
delegations: int = 0
|
288
285
|
latency: int | float = 0 # job latency in sec
|
@@ -640,7 +637,7 @@ Ref. Output image: {output_formats_to_follow}
|
|
640
637
|
|
641
638
|
self.latency = (ended_at - started_at).total_seconds()
|
642
639
|
self.output = task_output
|
643
|
-
self.
|
640
|
+
self.processed_agents.add(agent.role)
|
644
641
|
|
645
642
|
if self.should_evaluate:
|
646
643
|
task_output.evaluate(task=self, latency=self.latency, tokens=self.tokens)
|