versionhq 1.2.1.15__py3-none-any.whl → 1.2.1.17__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 +2 -2
- versionhq/agent/inhouse_agents.py +2 -2
- versionhq/agent/model.py +120 -121
- versionhq/agent_network/formation.py +157 -0
- versionhq/agent_network/model.py +7 -8
- versionhq/task/model.py +34 -16
- versionhq/task_graph/draft.py +2 -2
- versionhq/tool/composio_tool.py +1 -2
- {versionhq-1.2.1.15.dist-info → versionhq-1.2.1.17.dist-info}/METADATA +1 -1
- {versionhq-1.2.1.15.dist-info → versionhq-1.2.1.17.dist-info}/RECORD +13 -13
- versionhq/task/formation.py +0 -159
- {versionhq-1.2.1.15.dist-info → versionhq-1.2.1.17.dist-info}/LICENSE +0 -0
- {versionhq-1.2.1.15.dist-info → versionhq-1.2.1.17.dist-info}/WHEEL +0 -0
- {versionhq-1.2.1.15.dist-info → versionhq-1.2.1.17.dist-info}/top_level.txt +0 -0
versionhq/__init__.py
CHANGED
@@ -27,11 +27,11 @@ from versionhq.tool.composio_tool import ComposioHandler
|
|
27
27
|
from versionhq.memory.contextual_memory import ContextualMemory
|
28
28
|
from versionhq.memory.model import ShortTermMemory,LongTermMemory, UserMemory, MemoryItem
|
29
29
|
|
30
|
-
from versionhq.
|
30
|
+
from versionhq.agent_network.formation import form_agent_network
|
31
31
|
from versionhq.task_graph.draft import workflow
|
32
32
|
|
33
33
|
|
34
|
-
__version__ = "1.2.1.
|
34
|
+
__version__ = "1.2.1.17"
|
35
35
|
__all__ = [
|
36
36
|
"Agent",
|
37
37
|
|
@@ -10,7 +10,7 @@ vhq_client_manager = Agent(
|
|
10
10
|
role="vhq-Client Manager",
|
11
11
|
goal="Efficiently communicate with the client on the task progress",
|
12
12
|
llm=DEFAULT_MODEL_NAME,
|
13
|
-
|
13
|
+
with_memory=True,
|
14
14
|
)
|
15
15
|
|
16
16
|
|
@@ -21,7 +21,7 @@ vhq_task_evaluator = Agent(
|
|
21
21
|
llm_config=dict(top_p=0.8, top_k=30, max_tokens=5000, temperature=0.9),
|
22
22
|
maxit=1,
|
23
23
|
max_retry_limit=1,
|
24
|
-
|
24
|
+
with_memory=True # refer past eval records of similar tasks
|
25
25
|
)
|
26
26
|
|
27
27
|
|
versionhq/agent/model.py
CHANGED
@@ -60,7 +60,6 @@ class Agent(BaseModel):
|
|
60
60
|
"""
|
61
61
|
|
62
62
|
__hash__ = object.__hash__
|
63
|
-
_logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=True))
|
64
63
|
_rpm_controller: Optional[RPMController] = PrivateAttr(default=None)
|
65
64
|
_request_within_rpm_limit: Any = PrivateAttr(default=None)
|
66
65
|
_token_process: TokenProcess = PrivateAttr(default_factory=TokenProcess)
|
@@ -76,39 +75,38 @@ class Agent(BaseModel):
|
|
76
75
|
|
77
76
|
# knowledge
|
78
77
|
knowledge_sources: Optional[List[BaseKnowledgeSource | Any]] = Field(default=None)
|
78
|
+
embedder_config: Optional[Dict[str, Any]] = Field(default=None, description="embedder configuration for knowledge sources")
|
79
79
|
_knowledge: Optional[Knowledge] = PrivateAttr(default=None)
|
80
80
|
|
81
81
|
# memory
|
82
|
-
|
83
|
-
memory_config: Optional[Dict[str, Any]] = Field(default=None, description="
|
82
|
+
with_memory: bool = Field(default=False, description="whether to use memories during the task execution")
|
83
|
+
memory_config: Optional[Dict[str, Any]] = Field(default=None, description="memory config. needs to store user_id for UserMemory to work")
|
84
84
|
short_term_memory: Optional[InstanceOf[ShortTermMemory]] = Field(default=None)
|
85
85
|
long_term_memory: Optional[InstanceOf[LongTermMemory]] = Field(default=None)
|
86
86
|
user_memory: Optional[InstanceOf[UserMemory]] = Field(default=None)
|
87
|
-
embedder_config: Optional[Dict[str, Any]] = Field(default=None, description="embedder configuration for the agent's knowledge")
|
88
87
|
|
89
88
|
# prompting
|
90
89
|
use_developer_prompt: Optional[bool] = Field(default=True, description="Use developer prompt when calling the llm")
|
91
|
-
developer_propmt_template: Optional[str] = Field(default=None, description="
|
92
|
-
user_prompt_template: Optional[str] = Field(default=None, description="user prompt template")
|
90
|
+
developer_propmt_template: Optional[str] = Field(default=None, description="abs. file path to developer prompt template")
|
91
|
+
user_prompt_template: Optional[str] = Field(default=None, description="abs. file path to user prompt template")
|
93
92
|
|
94
93
|
# task execution rules
|
95
|
-
|
96
|
-
allow_delegation: bool = Field(default=False,description="
|
97
|
-
max_retry_limit: int = Field(default=2
|
98
|
-
maxit: Optional[int] = Field(default=25,description="max. number of total optimization loops conducted when an error occurs")
|
94
|
+
networks: Optional[List[Any]] = Field(default_factory=list, description="store a list of agent networks that the agent belong as a member")
|
95
|
+
allow_delegation: bool = Field(default=False, description="whether to delegate the task to another agent")
|
96
|
+
max_retry_limit: int = Field(default=2, description="max. number of task retries when an error occurs")
|
97
|
+
maxit: Optional[int] = Field(default=25, description="max. number of total optimization loops conducted when an error occurs")
|
99
98
|
callbacks: Optional[List[Callable]] = Field(default_factory=list, description="callback functions to execute after any task execution")
|
100
99
|
|
101
100
|
# llm settings cascaded to the LLM model
|
102
101
|
llm: str | InstanceOf[LLM] | Dict[str, Any] = Field(default=None)
|
103
|
-
|
104
|
-
respect_context_window: bool = Field(default=True,description="
|
105
|
-
|
106
|
-
max_execution_time: Optional[int] = Field(default=None, description="max. execution time for an agent to execute a task")
|
102
|
+
func_calling_llm: str | InstanceOf[LLM] | Dict[str, Any] = Field(default=None)
|
103
|
+
respect_context_window: bool = Field(default=True,description="keep messages under the context window size")
|
104
|
+
max_execution_time: Optional[int] = Field(default=None, description="max. task execution time in seconds")
|
107
105
|
max_rpm: Optional[int] = Field(default=None, description="max. number of requests per minute")
|
108
106
|
llm_config: Optional[Dict[str, Any]] = Field(default=None, description="other llm config cascaded to the LLM model")
|
109
107
|
|
110
|
-
# cache, error, ops handling
|
111
|
-
formatting_errors: int = Field(default=0, description="number of formatting errors.")
|
108
|
+
# # cache, error, ops handling
|
109
|
+
# formatting_errors: int = Field(default=0, description="number of formatting errors.")
|
112
110
|
|
113
111
|
|
114
112
|
@field_validator("id", mode="before")
|
@@ -136,18 +134,18 @@ class Agent(BaseModel):
|
|
136
134
|
@model_validator(mode="after")
|
137
135
|
def set_up_llm(self) -> Self:
|
138
136
|
"""
|
139
|
-
Set up `llm` and `
|
137
|
+
Set up `llm` and `func_calling_llm` as valid LLM objects using the given kwargs.
|
140
138
|
"""
|
141
139
|
self.llm = self._convert_to_llm_object(llm=self.llm)
|
142
140
|
|
143
|
-
|
144
|
-
|
145
|
-
if
|
146
|
-
self.
|
141
|
+
func_calling_llm = self.func_calling_llm if self.func_calling_llm else self.llm if self.llm else None
|
142
|
+
func_calling_llm = self._convert_to_llm_object(llm=func_calling_llm)
|
143
|
+
if func_calling_llm._supports_function_calling():
|
144
|
+
self.func_calling_llm = func_calling_llm
|
147
145
|
elif self.llm._supports_function_calling():
|
148
|
-
self.
|
146
|
+
self.func_calling_llm = self.llm
|
149
147
|
else:
|
150
|
-
self.
|
148
|
+
self.func_calling_llm = self._convert_to_llm_object(llm=LLM(model=DEFAULT_MODEL_NAME))
|
151
149
|
return self
|
152
150
|
|
153
151
|
|
@@ -179,7 +177,7 @@ class Agent(BaseModel):
|
|
179
177
|
model_name = (getattr(self.llm, "model_name") or getattr(self.llm, "deployment_name") or str(self.llm))
|
180
178
|
llm_obj = LLM(model=model_name if model_name else DEFAULT_MODEL_NAME)
|
181
179
|
llm_params = {
|
182
|
-
"max_tokens": (getattr(llm, "max_tokens") or
|
180
|
+
"max_tokens": (getattr(llm, "max_tokens") or 3000),
|
183
181
|
"timeout": getattr(llm, "timeout", self.max_execution_time),
|
184
182
|
"callbacks": getattr(llm, "callbacks", None),
|
185
183
|
"temperature": getattr(llm, "temperature", None),
|
@@ -222,7 +220,7 @@ class Agent(BaseModel):
|
|
222
220
|
|
223
221
|
|
224
222
|
llm.timeout = self.max_execution_time if llm.timeout is None else llm.timeout
|
225
|
-
llm.max_tokens = self.max_tokens if self.max_tokens else llm.max_tokens
|
223
|
+
# llm.max_tokens = self.max_tokens if self.max_tokens else llm.max_tokens
|
226
224
|
|
227
225
|
if llm.provider is None:
|
228
226
|
provider_name = llm.model.split("/")[0]
|
@@ -262,7 +260,7 @@ class Agent(BaseModel):
|
|
262
260
|
tool_list.append(item)
|
263
261
|
|
264
262
|
else:
|
265
|
-
|
263
|
+
Logger().log(level="error", message=f"Tool {str(item)} is missing a function.", color="red")
|
266
264
|
raise PydanticCustomError("invalid_tool", f"The tool {str(item)} is missing a function.", {})
|
267
265
|
|
268
266
|
self.tools = tool_list
|
@@ -346,7 +344,7 @@ class Agent(BaseModel):
|
|
346
344
|
self._knowledge = Knowledge(sources=knowledge_sources, embedder_config=self.embedder_config, collection_name=collection_name)
|
347
345
|
|
348
346
|
except:
|
349
|
-
|
347
|
+
Logger().log(level="warning", message="We cannot find the format for the source. Add BaseKnowledgeSource objects instead.", color="yellow")
|
350
348
|
|
351
349
|
return self
|
352
350
|
|
@@ -357,7 +355,7 @@ class Agent(BaseModel):
|
|
357
355
|
Set up memories: stm, ltm, and um
|
358
356
|
"""
|
359
357
|
|
360
|
-
# if self.
|
358
|
+
# if self.with_memory == True:
|
361
359
|
self.long_term_memory = self.long_term_memory if self.long_term_memory else LongTermMemory()
|
362
360
|
self.short_term_memory = self.short_term_memory if self.short_term_memory else ShortTermMemory(agent=self, embedder_config=self.embedder_config)
|
363
361
|
|
@@ -371,21 +369,13 @@ class Agent(BaseModel):
|
|
371
369
|
return self
|
372
370
|
|
373
371
|
|
374
|
-
def
|
375
|
-
"""
|
376
|
-
Fine-tuned the base model using OpenAI train framework.
|
377
|
-
"""
|
378
|
-
if not isinstance(self.llm, LLM):
|
379
|
-
pass
|
380
|
-
|
381
|
-
|
382
|
-
def update_llm(self, llm: Any = None, llm_config: Optional[Dict[str, Any]] = None) -> Self:
|
372
|
+
def _update_llm(self, llm: Any = None, llm_config: Optional[Dict[str, Any]] = None) -> Self:
|
383
373
|
"""
|
384
374
|
Update llm and llm_config of the exsiting agent. (Other conditions will remain the same.)
|
385
375
|
"""
|
386
376
|
|
387
377
|
if not llm and not llm_config:
|
388
|
-
|
378
|
+
Logger().log(level="error", message="Missing llm or llm_config values to update", color="red")
|
389
379
|
pass
|
390
380
|
|
391
381
|
self.llm = llm
|
@@ -398,59 +388,15 @@ class Agent(BaseModel):
|
|
398
388
|
return self.set_up_llm()
|
399
389
|
|
400
390
|
|
401
|
-
def
|
391
|
+
def _train(self) -> Self:
|
402
392
|
"""
|
403
|
-
|
393
|
+
Fine-tuned the base model using OpenAI train framework.
|
404
394
|
"""
|
405
|
-
|
406
|
-
|
407
|
-
self._logger.log(level="error", message="Missing values to update", color="red")
|
408
|
-
return self
|
409
|
-
|
410
|
-
for k, v in kwargs.items():
|
411
|
-
match k:
|
412
|
-
case "tools":
|
413
|
-
self.tools = kwargs.get(k, self.tools)
|
414
|
-
self.set_up_tools()
|
415
|
-
|
416
|
-
case "role" | "goal":
|
417
|
-
self.role = kwargs.get("role", self.role)
|
418
|
-
self.goal = kwargs.get("goal", self.goal)
|
419
|
-
if not self.backstory:
|
420
|
-
self.set_up_backstory()
|
421
|
-
|
422
|
-
if self.backstory:
|
423
|
-
self.backstory += f"new role: {self.role}, new goal: {self.goal}"
|
424
|
-
|
425
|
-
case "max_rpm":
|
426
|
-
self.max_rpm = kwargs.get(k, self.max_rpm)
|
427
|
-
self.set_up_rpm()
|
428
|
-
|
429
|
-
case "knowledge_sources":
|
430
|
-
self.knowledge_sources = kwargs.get("knowledge_sources", self.knowledge_sources)
|
431
|
-
self.set_up_knowledge()
|
432
|
-
|
433
|
-
case "use_memory" | "memory_config":
|
434
|
-
self.use_memory = kwargs.get("use_memory", self.use_memory)
|
435
|
-
self.memory_config = kwargs.get("memory_config", self.memory_config)
|
436
|
-
self.set_up_memory()
|
437
|
-
|
438
|
-
case "llm" | "llm_config":
|
439
|
-
self.llm = kwargs.get("llm", self.llm)
|
440
|
-
self.llm_config = kwargs.get("llm_config", self.llm_config)
|
441
|
-
self.update_llm(llm=self.llm, llm_config=self.llm_config)
|
442
|
-
|
443
|
-
case _:
|
444
|
-
try:
|
445
|
-
setattr(self, k, v)
|
446
|
-
except Exception as e:
|
447
|
-
self._logger.log(level="error", message=f"Failed to update the key: {k} We'll skip. Error: {str(e)}", color="red")
|
448
|
-
pass
|
449
|
-
|
450
|
-
return self
|
395
|
+
if not isinstance(self.llm, LLM):
|
396
|
+
pass
|
451
397
|
|
452
398
|
|
453
|
-
def
|
399
|
+
def _invoke(
|
454
400
|
self,
|
455
401
|
prompts: str,
|
456
402
|
response_format: Optional[Dict[str, Any]] = None,
|
@@ -477,21 +423,21 @@ class Agent(BaseModel):
|
|
477
423
|
if self._rpm_controller and self.max_rpm:
|
478
424
|
self._rpm_controller.check_or_wait()
|
479
425
|
|
480
|
-
|
426
|
+
Logger().log(level="info", message=f"Messages sent to the model: {messages}", color="blue")
|
481
427
|
|
482
428
|
if tool_res_as_final:
|
483
|
-
raw_response = self.
|
484
|
-
task.tokens = self.
|
429
|
+
raw_response = self.func_calling_llm.call(messages=messages, tools=tools, tool_res_as_final=True)
|
430
|
+
task.tokens = self.func_calling_llm._tokens
|
485
431
|
else:
|
486
432
|
raw_response = self.llm.call(messages=messages, response_format=response_format, tools=tools)
|
487
433
|
task.tokens = self.llm._tokens
|
488
434
|
|
489
435
|
task_execution_counter += 1
|
490
|
-
|
436
|
+
Logger().log(level="info", message=f"Agent response: {raw_response}", color="green")
|
491
437
|
return raw_response
|
492
438
|
|
493
439
|
except Exception as e:
|
494
|
-
|
440
|
+
Logger().log(level="error", message=f"An error occured. The agent will retry: {str(e)}", color="red")
|
495
441
|
|
496
442
|
while not raw_response and task_execution_counter <= self.max_retry_limit:
|
497
443
|
while (not raw_response or raw_response == "" or raw_response is None) and iterations < self.maxit:
|
@@ -503,14 +449,89 @@ class Agent(BaseModel):
|
|
503
449
|
iterations += 1
|
504
450
|
|
505
451
|
task_execution_counter += 1
|
506
|
-
|
452
|
+
Logger().log(level="info", message=f"Agent #{task_execution_counter} response: {raw_response}", color="green")
|
507
453
|
return raw_response
|
508
454
|
|
509
455
|
if not raw_response:
|
510
|
-
|
456
|
+
Logger().log(level="error", message="Received None or empty response from the model", color="red")
|
511
457
|
raise ValueError("Invalid response from LLM call - None or empty.")
|
512
458
|
|
513
459
|
|
460
|
+
def update(self, **kwargs) -> Self:
|
461
|
+
"""
|
462
|
+
Update the existing agent. Address variables that require runnning set_up_x methods first, then update remaining variables.
|
463
|
+
"""
|
464
|
+
|
465
|
+
if not kwargs:
|
466
|
+
Logger().log(level="error", message="Missing values to update", color="red")
|
467
|
+
return self
|
468
|
+
|
469
|
+
for k, v in kwargs.items():
|
470
|
+
match k:
|
471
|
+
case "tools":
|
472
|
+
self.tools = kwargs.get(k, self.tools)
|
473
|
+
self.set_up_tools()
|
474
|
+
|
475
|
+
case "role" | "goal":
|
476
|
+
self.role = kwargs.get("role", self.role)
|
477
|
+
self.goal = kwargs.get("goal", self.goal)
|
478
|
+
if not self.backstory:
|
479
|
+
self.set_up_backstory()
|
480
|
+
|
481
|
+
if self.backstory:
|
482
|
+
self.backstory += f"new role: {self.role}, new goal: {self.goal}"
|
483
|
+
|
484
|
+
case "max_rpm":
|
485
|
+
self.max_rpm = kwargs.get(k, self.max_rpm)
|
486
|
+
self.set_up_rpm()
|
487
|
+
|
488
|
+
case "knowledge_sources":
|
489
|
+
self.knowledge_sources = kwargs.get("knowledge_sources", self.knowledge_sources)
|
490
|
+
self.set_up_knowledge()
|
491
|
+
|
492
|
+
case "with_memory" | "memory_config":
|
493
|
+
self.with_memory = kwargs.get("with_memory", self.with_memory)
|
494
|
+
self.memory_config = kwargs.get("memory_config", self.memory_config)
|
495
|
+
self.set_up_memory()
|
496
|
+
|
497
|
+
case "llm" | "llm_config":
|
498
|
+
self.llm = kwargs.get("llm", self.llm)
|
499
|
+
self.llm_config = kwargs.get("llm_config", self.llm_config)
|
500
|
+
self._update_llm(llm=self.llm, llm_config=self.llm_config)
|
501
|
+
|
502
|
+
case _:
|
503
|
+
try:
|
504
|
+
setattr(self, k, v)
|
505
|
+
except Exception as e:
|
506
|
+
Logger().log(level="error", message=f"Failed to update the field: {k} We'll skip it. Error: {str(e)}", color="red")
|
507
|
+
pass
|
508
|
+
|
509
|
+
return self
|
510
|
+
|
511
|
+
|
512
|
+
def start(self, context: Any = None, tool_res_as_final: bool = False) -> Any | None:
|
513
|
+
"""
|
514
|
+
Defines and executes a task when it is not given and returns TaskOutput object.
|
515
|
+
"""
|
516
|
+
|
517
|
+
if not self.goal or not self.role:
|
518
|
+
return None
|
519
|
+
|
520
|
+
from versionhq.task.model import Task
|
521
|
+
|
522
|
+
class Output(BaseModel):
|
523
|
+
result: str
|
524
|
+
steps: list[str]
|
525
|
+
|
526
|
+
task = Task(
|
527
|
+
description=f"Generate a simple result in a sentence to achieve the goal: {self.goal}. If needed, list up necessary steps in concise manner.",
|
528
|
+
pydantic_output=Output,
|
529
|
+
tool_res_as_final=tool_res_as_final,
|
530
|
+
)
|
531
|
+
res = task.execute(agent=self, context=context)
|
532
|
+
return res
|
533
|
+
|
534
|
+
|
514
535
|
def execute_task(self, task, context: Optional[Any] = None, task_tools: Optional[List[Tool | ToolSet]] = list()) -> str:
|
515
536
|
"""
|
516
537
|
Format a task prompt, adding context from knowledge and memory (if given), and invoke LLM.
|
@@ -534,7 +555,7 @@ class Agent(BaseModel):
|
|
534
555
|
if agent_knowledge_context:
|
535
556
|
task_prompt += agent_knowledge_context
|
536
557
|
|
537
|
-
if self.
|
558
|
+
if self.with_memory == True:
|
538
559
|
contextual_memory = ContextualMemory(
|
539
560
|
memory_config=self.memory_config, stm=self.short_term_memory, ltm=self.long_term_memory, um=self.user_memory
|
540
561
|
)
|
@@ -546,14 +567,14 @@ class Agent(BaseModel):
|
|
546
567
|
|
547
568
|
|
548
569
|
## comment out for now
|
549
|
-
# if self.
|
570
|
+
# if self.networks and self.networks._train:
|
550
571
|
# task_prompt = self._training_handler(task_prompt=task_prompt)
|
551
572
|
# else:
|
552
573
|
# task_prompt = self._use_trained_data(task_prompt=task_prompt)
|
553
574
|
|
554
575
|
try:
|
555
576
|
self._times_executed += 1
|
556
|
-
raw_response = self.
|
577
|
+
raw_response = self._invoke(
|
557
578
|
prompts=task_prompt,
|
558
579
|
response_format=task._structure_response_format(model_provider=self.llm.provider),
|
559
580
|
tools=tools,
|
@@ -563,11 +584,11 @@ class Agent(BaseModel):
|
|
563
584
|
|
564
585
|
except Exception as e:
|
565
586
|
self._times_executed += 1
|
566
|
-
|
587
|
+
Logger().log(level="error", message=f"The agent failed to execute the task. Error: {str(e)}", color="red")
|
567
588
|
raw_response = self.execute_task(task, context, task_tools)
|
568
589
|
|
569
590
|
if self._times_executed > self.max_retry_limit:
|
570
|
-
|
591
|
+
Logger().log(level="error", message=f"Max retry limit has exceeded.", color="red")
|
571
592
|
raise e
|
572
593
|
|
573
594
|
if self.max_rpm and self._rpm_controller:
|
@@ -576,27 +597,5 @@ class Agent(BaseModel):
|
|
576
597
|
return raw_response
|
577
598
|
|
578
599
|
|
579
|
-
def start(self, context: Any = None) -> Any | None:
|
580
|
-
"""
|
581
|
-
Defines and executes a task when it is not given and returns TaskOutput object.
|
582
|
-
"""
|
583
|
-
|
584
|
-
if not self.goal or not self.role:
|
585
|
-
return None
|
586
|
-
|
587
|
-
from versionhq.task.model import Task, ResponseField
|
588
|
-
|
589
|
-
class Output(BaseModel):
|
590
|
-
result: str
|
591
|
-
steps: list[str]
|
592
|
-
|
593
|
-
task = Task(
|
594
|
-
description=f"Generate a simple result in a sentence to achieve the goal: {self.goal}. If needed, list up necessary steps in concise manner.",
|
595
|
-
pydantic_output=Output
|
596
|
-
)
|
597
|
-
res = task.execute(agent=self, context=context)
|
598
|
-
return res
|
599
|
-
|
600
|
-
|
601
600
|
def __repr__(self):
|
602
601
|
return f"Agent(role={self.role}, goal={self.goal}"
|
@@ -0,0 +1,157 @@
|
|
1
|
+
from typing import List, Type
|
2
|
+
from enum import Enum
|
3
|
+
|
4
|
+
from pydantic import BaseModel, create_model, Field
|
5
|
+
|
6
|
+
from versionhq.task.model import Task
|
7
|
+
from versionhq.agent.model import Agent
|
8
|
+
from versionhq.agent_network.model import AgentNetwork, Member, Formation
|
9
|
+
from versionhq.agent.inhouse_agents import vhq_formation_planner
|
10
|
+
from versionhq._utils import Logger
|
11
|
+
|
12
|
+
|
13
|
+
def form_agent_network(
|
14
|
+
task: str,
|
15
|
+
expected_outcome: str | Type[BaseModel],
|
16
|
+
agents: List[Agent] = None,
|
17
|
+
context: str = None,
|
18
|
+
formation: Type[Formation] = None
|
19
|
+
) -> AgentNetwork | None:
|
20
|
+
"""
|
21
|
+
Make a formation of agents from the given task description, expected outcome, agents (optional), and context (optional).
|
22
|
+
"""
|
23
|
+
|
24
|
+
if not task:
|
25
|
+
Logger(verbose=True).log(level="error", message="Missing task description.", color="red")
|
26
|
+
return None
|
27
|
+
|
28
|
+
if not expected_outcome:
|
29
|
+
Logger(verbose=True).log(level="error", message="Missing expected outcome.", color="red")
|
30
|
+
return None
|
31
|
+
|
32
|
+
if formation:
|
33
|
+
try:
|
34
|
+
match formation:
|
35
|
+
case Formation():
|
36
|
+
pass
|
37
|
+
|
38
|
+
case str():
|
39
|
+
matched = [item for item in Formation.s_ if item == formation.upper()]
|
40
|
+
if matched:
|
41
|
+
formation = getattr(Formation, matched[0])
|
42
|
+
else:
|
43
|
+
# Formation._generate_next_value_(name=f"CUSTOM_{formation.upper()}", start=100, count=6, last_values=Formation.HYBRID.name)
|
44
|
+
Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid. We'll recreate a valid formation.", color="yellow")
|
45
|
+
formation = None
|
46
|
+
|
47
|
+
case int() | float():
|
48
|
+
formation = Formation(int(formation))
|
49
|
+
|
50
|
+
case _:
|
51
|
+
Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid. We'll recreate a valid formation.", color="yellow")
|
52
|
+
formation = None
|
53
|
+
|
54
|
+
except Exception as e:
|
55
|
+
Logger(verbose=True).log(level="warning", message=f"The formation {formation} is invalid: {str(e)}. We'll recreate a formation.", color="yellow")
|
56
|
+
formation = None
|
57
|
+
|
58
|
+
# try:
|
59
|
+
prompt_formation = formation.name if formation and isinstance(formation, Formation) else f"Select the best formation to effectively execute the tasks from the given Enum sets: {str(Formation.__dict__)}."
|
60
|
+
|
61
|
+
prompt_expected_outcome = expected_outcome if isinstance(expected_outcome, str) else expected_outcome.model_dump_json()
|
62
|
+
|
63
|
+
class Outcome(BaseModel):
|
64
|
+
formation: Enum
|
65
|
+
agent_roles: list[str]
|
66
|
+
task_descriptions: list[str]
|
67
|
+
task_outcomes: list[list[str]]
|
68
|
+
leader_agent: str
|
69
|
+
|
70
|
+
vhq_task = Task(
|
71
|
+
description=f"Design a team of specialized agents to fully automate the following task and achieve the expected outcome. For each agent, define its role, task description, and expected outputs via the task with items in a list. Then specify the team formation if the formation is not given. If you think SUPERVISING or HYBRID is the best formation, include a leader_agent role, else leave the leader_agent role blank.\nTask: {str(task)}\nExpected outcome: {prompt_expected_outcome}\nFormation: {prompt_formation}",
|
72
|
+
pydantic_output=Outcome
|
73
|
+
)
|
74
|
+
|
75
|
+
if agents:
|
76
|
+
vhq_task.description += "Consider adding following agents in the formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
|
77
|
+
|
78
|
+
res = vhq_task.execute(agent=vhq_formation_planner, context=context)
|
79
|
+
|
80
|
+
formation_keys = ([k for k in Formation._member_map_.keys() if k == res.pydantic.formation.upper()]
|
81
|
+
if res.pydantic else [k for k in Formation._member_map_.keys() if k == res.json_dict["formation"].upper()])
|
82
|
+
_formation = Formation[formation_keys[0]] if formation_keys else Formation.SUPERVISING
|
83
|
+
|
84
|
+
network_tasks = []
|
85
|
+
members = []
|
86
|
+
leader = str(res.pydantic.leader_agent) if res.pydantic else str(res.json_dict["leader_agent"])
|
87
|
+
|
88
|
+
created_agents = [Agent(role=item, goal=item) for item in res.pydantic.agent_roles]
|
89
|
+
created_tasks = []
|
90
|
+
|
91
|
+
if res.pydantic:
|
92
|
+
for i, item in enumerate(res.pydantic.task_outcomes):
|
93
|
+
if len(res.pydantic.task_descriptions) > i and res.pydantic.task_descriptions[i]:
|
94
|
+
fields = {}
|
95
|
+
for ob in item:
|
96
|
+
try:
|
97
|
+
field_name = str(ob).lower().replace(" ", "_").replace(":", "_")[0: 10]
|
98
|
+
fields[field_name] = (str, Field(default=None))
|
99
|
+
except:
|
100
|
+
pass
|
101
|
+
output = create_model("Output", **fields) if fields else None
|
102
|
+
_task = Task(description=res.pydantic.task_descriptions[i], pydantic_output=output)
|
103
|
+
created_tasks.append(_task)
|
104
|
+
|
105
|
+
elif res.json_dict:
|
106
|
+
for i, item in enumerate(res["task_outcomes"]):
|
107
|
+
if len(res["task_descriptions"]) > i and res["task_descriptions"][i]:
|
108
|
+
fields = {}
|
109
|
+
for ob in item:
|
110
|
+
try:
|
111
|
+
field_name = str(ob).lower().replace(" ", "_").replace(":", "_")[0: 10]
|
112
|
+
fields[field_name] = (str, Field(default=None))
|
113
|
+
except:
|
114
|
+
pass
|
115
|
+
output = create_model("Output", **fields) if fields else None
|
116
|
+
_task = Task(description=res["task_descriptions"][i], pydantic_output=output)
|
117
|
+
created_tasks.append(_task)
|
118
|
+
|
119
|
+
|
120
|
+
if len(created_tasks) <= len(created_agents):
|
121
|
+
for i in range(len(created_tasks)):
|
122
|
+
is_manager = bool(created_agents[i].role.lower() == leader.lower())
|
123
|
+
member = Member(agent=created_agents[i], is_manager=is_manager, tasks=[created_tasks[i]])
|
124
|
+
members.append(member)
|
125
|
+
|
126
|
+
for i in range(len(created_tasks), len(created_agents)):
|
127
|
+
try:
|
128
|
+
is_manager = bool(created_agents[i].role.lower() == leader.lower())
|
129
|
+
member_w_o_task = Member(agent=created_agents[i], is_manager=is_manager)
|
130
|
+
members.append(member_w_o_task)
|
131
|
+
except:
|
132
|
+
pass
|
133
|
+
|
134
|
+
elif len(created_tasks) > len(created_agents):
|
135
|
+
for i in range(len(created_agents)):
|
136
|
+
is_manager = bool(created_agents[i].role.lower() == leader.lower())
|
137
|
+
member = Member(agent=created_agents[i], is_manager=is_manager, tasks=[created_tasks[i]])
|
138
|
+
members.append(member)
|
139
|
+
|
140
|
+
network_tasks.append(created_tasks[len(created_agents):len(created_tasks)])
|
141
|
+
|
142
|
+
|
143
|
+
if _formation == Formation.SUPERVISING and not [member for member in members if member.is_manager]:
|
144
|
+
manager = Member(agent=Agent(role=leader, goal=leader), is_manager=True)
|
145
|
+
members.append(manager)
|
146
|
+
|
147
|
+
members.sort(key=lambda x: x.is_manager == False)
|
148
|
+
network = AgentNetwork(members=members, formation=_formation, network_tasks=network_tasks)
|
149
|
+
|
150
|
+
Logger().log(level="info", message=f"Successfully created a agent network: {str(network.id)} with {len(network.members)} agents.", color="blue")
|
151
|
+
|
152
|
+
return network
|
153
|
+
|
154
|
+
|
155
|
+
# except Exception as e:
|
156
|
+
# Logger().log(level="error", message=f"Failed to create a agent network - return None. You can try with solo agent. Error: {str(e)}", color="red")
|
157
|
+
# return None
|
versionhq/agent_network/model.py
CHANGED
@@ -28,7 +28,6 @@ warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")
|
|
28
28
|
|
29
29
|
|
30
30
|
class Formation(str, Enum):
|
31
|
-
UNDEFINED = 0
|
32
31
|
SOLO = 1
|
33
32
|
SUPERVISING = 2
|
34
33
|
SQUAD = 3
|
@@ -96,7 +95,6 @@ class AgentNetwork(BaseModel):
|
|
96
95
|
|
97
96
|
__hash__ = object.__hash__
|
98
97
|
_execution_span: Any = PrivateAttr()
|
99
|
-
_logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=True))
|
100
98
|
_inputs: Optional[Dict[str, Any]] = PrivateAttr(default=None)
|
101
99
|
|
102
100
|
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
|
@@ -167,12 +165,12 @@ class AgentNetwork(BaseModel):
|
|
167
165
|
"""
|
168
166
|
if self.process == TaskHandlingProcess.HIERARCHY or self.formation == Formation.SUPERVISING:
|
169
167
|
if not self.managers:
|
170
|
-
|
168
|
+
Logger().log(level="error", message="The process or formation created needs at least 1 manager agent.", color="red")
|
171
169
|
raise PydanticCustomError("missing_manager", "`manager` is required when using hierarchical process.", {})
|
172
170
|
|
173
171
|
## comment out for the formation flexibilities
|
174
172
|
# if self.managers and (self.manager_tasks is None or self.network_tasks is None):
|
175
|
-
#
|
173
|
+
# Logger().log(level="error", message="The manager is idling. At least 1 task needs to be assigned to the manager.", color="red")
|
176
174
|
# raise PydanticCustomError("missing_manager_task", "manager needs to have at least one manager task or network task.", {})
|
177
175
|
|
178
176
|
return self
|
@@ -186,10 +184,11 @@ class AgentNetwork(BaseModel):
|
|
186
184
|
if self.process == TaskHandlingProcess.SEQUENT and self.network_tasks is None:
|
187
185
|
for task in self.tasks:
|
188
186
|
if not [member.task == task for member in self.members]:
|
189
|
-
|
187
|
+
Logger().log(level="error", message=f"The following task needs a dedicated agent to be assinged: {task.description}", color="red")
|
190
188
|
raise PydanticCustomError("missing_agent_in_task", "Sequential process error: Agent is missing the task", {})
|
191
189
|
return self
|
192
190
|
|
191
|
+
|
193
192
|
@model_validator(mode="after")
|
194
193
|
def validate_end_with_at_most_one_async_task(self):
|
195
194
|
"""
|
@@ -371,7 +370,7 @@ class AgentNetwork(BaseModel):
|
|
371
370
|
task_outputs = self._process_async_tasks(futures, was_replayed)
|
372
371
|
|
373
372
|
if not task_outputs:
|
374
|
-
|
373
|
+
Logger().log(level="error", message="Missing task outputs.", color="red")
|
375
374
|
raise ValueError("Missing task outputs")
|
376
375
|
|
377
376
|
final_task_output = lead_task_output if lead_task_output is not None else task_outputs[0] #! REFINEME
|
@@ -399,12 +398,12 @@ class AgentNetwork(BaseModel):
|
|
399
398
|
self._assign_tasks()
|
400
399
|
|
401
400
|
if kwargs_pre is not None:
|
402
|
-
for func in self.pre_launch_callbacks:
|
401
|
+
for func in self.pre_launch_callbacks: # signature check
|
403
402
|
func(**kwargs_pre)
|
404
403
|
|
405
404
|
for member in self.members:
|
406
405
|
agent = member.agent
|
407
|
-
agent.
|
406
|
+
agent.networks.append(self)
|
408
407
|
|
409
408
|
if self.step_callback:
|
410
409
|
agent.callbacks.append(self.step_callback)
|
versionhq/task/model.py
CHANGED
@@ -281,7 +281,7 @@ class Task(BaseModel):
|
|
281
281
|
|
282
282
|
# executing
|
283
283
|
execution_type: TaskExecutionType = Field(default=TaskExecutionType.SYNC)
|
284
|
-
allow_delegation: bool = Field(default=False, description="
|
284
|
+
allow_delegation: bool = Field(default=False, description="whether to delegate the task to another agent")
|
285
285
|
callback: Optional[Callable] = Field(default=None, description="callback to be executed after the task is completed.")
|
286
286
|
callback_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict, description="kwargs for the callback when the callback is callable")
|
287
287
|
|
@@ -530,9 +530,9 @@ Ref. Output image: {output_formats_to_follow}
|
|
530
530
|
from versionhq.agent.model import Agent
|
531
531
|
from versionhq.memory.model import ShortTermMemory, MemoryMetadata, LongTermMemory
|
532
532
|
|
533
|
-
agent = agent if isinstance(agent, Agent) else Agent(role=str(agent), goal=str(agent),
|
533
|
+
agent = agent if isinstance(agent, Agent) else Agent(role=str(agent), goal=str(agent), with_memory=True)
|
534
534
|
|
535
|
-
if agent.
|
535
|
+
if agent.with_memory == False:
|
536
536
|
return None
|
537
537
|
|
538
538
|
try:
|
@@ -574,6 +574,36 @@ Ref. Output image: {output_formats_to_follow}
|
|
574
574
|
return agent
|
575
575
|
|
576
576
|
|
577
|
+
def _select_agent_to_delegate(self, agent: Any = None) -> Any | None: # return agent object or None
|
578
|
+
"""
|
579
|
+
Creates or selects an agent to delegate the given task and returns Agent object else None.
|
580
|
+
"""
|
581
|
+
|
582
|
+
from versionhq.agent.model import Agent
|
583
|
+
|
584
|
+
if not self.allow_delegation:
|
585
|
+
return None
|
586
|
+
|
587
|
+
agent_to_delegate: InstanceOf[Agent] = None
|
588
|
+
|
589
|
+
if not agent:
|
590
|
+
agent_to_delegate = self._build_agent_from_task()
|
591
|
+
|
592
|
+
elif agent and not agent.networks:
|
593
|
+
agent_to_delegate = Agent(role="vhq-Delegated-Agent", goal=agent.goal, llm=agent.llm)
|
594
|
+
|
595
|
+
else:
|
596
|
+
_managers = []
|
597
|
+
_members = []
|
598
|
+
for network in agent.networks:
|
599
|
+
_managers.extend(member.agent for member in network.members if member.is_manager)
|
600
|
+
_members.extend(member.agent for member in network.members if not member.is_manager)
|
601
|
+
|
602
|
+
agent_to_delegate = _managers[0] if _managers else _members[0] if _members else Agent(role="vhq-Delegated-Agent", goal=agent.goal, llm=agent.llm)
|
603
|
+
|
604
|
+
return agent_to_delegate
|
605
|
+
|
606
|
+
|
577
607
|
# task execution
|
578
608
|
def execute(
|
579
609
|
self, type: TaskExecutionType = None, agent: Optional["vhq.Agent"] = None, context: Optional[Any] = None
|
@@ -635,19 +665,7 @@ Ref. Output image: {output_formats_to_follow}
|
|
635
665
|
task_tools.append(item)
|
636
666
|
|
637
667
|
if self.allow_delegation == True:
|
638
|
-
agent_to_delegate =
|
639
|
-
|
640
|
-
if hasattr(agent, "network") and isinstance(agent.network, AgentNetwork):
|
641
|
-
if agent.network.managers:
|
642
|
-
idling_manager_agents = [manager.agent for manager in agent.network.managers if manager.is_idling]
|
643
|
-
agent_to_delegate = idling_manager_agents[0] if idling_manager_agents else agent.network.managers[0]
|
644
|
-
else:
|
645
|
-
peers = [member.agent for member in agent.network.members if member.is_manager == False and member.agent.id is not agent.id]
|
646
|
-
if len(peers) > 0:
|
647
|
-
agent_to_delegate = peers[0]
|
648
|
-
else:
|
649
|
-
agent_to_delegate = Agent(role="vhq-Delegated-Agent", goal=agent.goal, llm=agent.llm)
|
650
|
-
|
668
|
+
agent_to_delegate = self._select_agent_to_delegate(agent=agent)
|
651
669
|
agent = agent_to_delegate
|
652
670
|
self.delegations += 1
|
653
671
|
|
versionhq/task_graph/draft.py
CHANGED
@@ -14,7 +14,7 @@ from versionhq.task_graph.model import TaskGraph, Task, DependencyType, Node
|
|
14
14
|
from versionhq._utils.logger import Logger
|
15
15
|
|
16
16
|
|
17
|
-
def workflow(final_output: Type[BaseModel], context: Any = None, human: bool = False,
|
17
|
+
def workflow(final_output: Type[BaseModel], context: Any = None, human: bool = False, with_memory: bool = False) -> TaskGraph | None:
|
18
18
|
"""
|
19
19
|
Generate a TaskGraph object to generate the givne final_output most resource-efficiently.
|
20
20
|
"""
|
@@ -43,7 +43,7 @@ def workflow(final_output: Type[BaseModel], context: Any = None, human: bool = F
|
|
43
43
|
", ".join([k for k in DependencyType._member_map_.keys()]),
|
44
44
|
],
|
45
45
|
llm="gemini-2.0",
|
46
|
-
|
46
|
+
with_memory=with_memory,
|
47
47
|
maxit=1,
|
48
48
|
max_retry_limit=1,
|
49
49
|
)
|
versionhq/tool/composio_tool.py
CHANGED
@@ -2,14 +2,13 @@ import os
|
|
2
2
|
import uuid
|
3
3
|
from abc import ABC
|
4
4
|
from dotenv import load_dotenv
|
5
|
-
from typing import Any,
|
5
|
+
from typing import Any, Optional, Tuple, Dict
|
6
6
|
from typing_extensions import Self
|
7
7
|
|
8
8
|
from pydantic import BaseModel, Field, model_validator, field_validator, UUID4, PrivateAttr
|
9
9
|
from pydantic_core import PydanticCustomError
|
10
10
|
|
11
11
|
from composio import ComposioToolSet
|
12
|
-
from composio_langchain import action
|
13
12
|
|
14
13
|
from versionhq.tool.composio_tool_vars import ComposioAppName, ComposioAuthScheme, composio_app_set, ComposioStatus, ComposioAction
|
15
14
|
from versionhq.tool.cache_handler import CacheHandler
|
@@ -1,4 +1,4 @@
|
|
1
|
-
versionhq/__init__.py,sha256=
|
1
|
+
versionhq/__init__.py,sha256=nNrMhlfHaW9xgRJhnU9sHM4p9c5dL1n0UeAlVkTxwY4,2892
|
2
2
|
versionhq/_utils/__init__.py,sha256=dzoZr4cBlh-2QZuPzTdehPUCe9lP1dmRtauD7qTjUaA,158
|
3
3
|
versionhq/_utils/i18n.py,sha256=TwA_PnYfDLA6VqlUDPuybdV9lgi3Frh_ASsb_X8jJo8,1483
|
4
4
|
versionhq/_utils/logger.py,sha256=zgogTwAY-ujDLrdryAKhdtoaNe1nOFajmEN0V8aMR34,3155
|
@@ -6,14 +6,15 @@ versionhq/_utils/process_config.py,sha256=jbPGXK2Kb4iyCugJ3FwRJuU0wL5Trq2x4xFQz2
|
|
6
6
|
versionhq/_utils/usage_metrics.py,sha256=NXF18dn5NNvGK7EsQ4AAghpR8ppYOjMx6ABenLLHnmM,1066
|
7
7
|
versionhq/_utils/vars.py,sha256=bZ5Dx_bFKlt3hi4-NNGXqdk7B23If_WaTIju2fiTyPQ,57
|
8
8
|
versionhq/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
versionhq/agent/inhouse_agents.py,sha256=
|
10
|
-
versionhq/agent/model.py,sha256=
|
9
|
+
versionhq/agent/inhouse_agents.py,sha256=vupO1viYqVb7sKohIE1zThu6JArhh5JLo5LBeSnh0kM,2534
|
10
|
+
versionhq/agent/model.py,sha256=ixfYjUY8u6CAFGuaExV8cU1WFlBgNQPxr1UQuxPBSew,25604
|
11
11
|
versionhq/agent/parser.py,sha256=riG0dkdQCxH7uJ0AbdVdg7WvL0BXhUgJht0VtQvxJBc,4082
|
12
12
|
versionhq/agent/rpm_controller.py,sha256=grezIxyBci_lDlwAlgWFRyR5KOocXeOhYkgN02dNFNE,2360
|
13
13
|
versionhq/agent/TEMPLATES/Backstory.py,sha256=IAhGnnt6VUMe3wO6IzeyZPDNu7XE7Uiu3VEXUreOcKs,532
|
14
14
|
versionhq/agent/TEMPLATES/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
versionhq/agent_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
versionhq/agent_network/
|
16
|
+
versionhq/agent_network/formation.py,sha256=nGUVX8Ljiq2mQ5BNXm17SP-kuCRCA87CucRy-QB-Zv0,7426
|
17
|
+
versionhq/agent_network/model.py,sha256=hjtYIopAN52nStcM6TlV0b6ulRMrmzKH7jIkzNmZHDE,19265
|
17
18
|
versionhq/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
19
|
versionhq/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
20
|
versionhq/clients/customer/__init__.py,sha256=-YXh1FQfvpfLacK8SUC7bD7Wx_eIEi4yrkCC_cUasFg,217
|
@@ -44,25 +45,24 @@ versionhq/storage/task_output_storage.py,sha256=E1t_Fkt78dPYIOl3MP7LfQ8oGtjlzxBu
|
|
44
45
|
versionhq/storage/utils.py,sha256=ByYXPoEIGJYLUqz-DWjbCAnneNrH1otiYbp12SCILpM,747
|
45
46
|
versionhq/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
47
|
versionhq/task/evaluate.py,sha256=WdUgjbZL62XrxyWe5MTz29scfzwmuAHGxJ7GvAB8Fmk,3954
|
47
|
-
versionhq/task/formation.py,sha256=WH604q9bRmWH7KQCrk2qKJwisCopYX5CjJvsj4TgFjI,6894
|
48
48
|
versionhq/task/formatter.py,sha256=N8Kmk9vtrMtBdgJ8J7RmlKNMdZWSmV8O1bDexmCWgU0,643
|
49
49
|
versionhq/task/log_handler.py,sha256=LT7YnO7gcPR9IZS7eRvMjnHh8crMBFtqduxd8dxIbkk,1680
|
50
|
-
versionhq/task/model.py,sha256=
|
50
|
+
versionhq/task/model.py,sha256=KshCysteol3ggfotZMfFn192dMYALg8lvjiGpyLUVQA,28948
|
51
51
|
versionhq/task/structured_response.py,sha256=4q-hQPu7oMMHHXEzh9YW4SJ7N5eCZ7OfZ65juyl_jCI,5000
|
52
52
|
versionhq/task/TEMPLATES/Description.py,sha256=V-4kh8xpQTKOcDMi2xnuP-fcNk6kuoz1_5tYBlDLQWQ,420
|
53
53
|
versionhq/task_graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
54
|
versionhq/task_graph/colors.py,sha256=naJCx4Vho4iuJtbW8USUXb-M5uYvd5ds2p8qbjUfRus,669
|
55
|
-
versionhq/task_graph/draft.py,sha256=
|
55
|
+
versionhq/task_graph/draft.py,sha256=AuQ2X-T5xuQ2ipMiAqeh9Pjm6I2fIf952pBQRYqdaog,5131
|
56
56
|
versionhq/task_graph/model.py,sha256=njyHQyHrVTZP46iVkC6YvuMnGcS40vOy1wszRtf7DHY,23971
|
57
57
|
versionhq/tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
58
58
|
versionhq/tool/cache_handler.py,sha256=iL8FH7X0G-cdT0uhJwzuhLDaadTXOdfybZcDy151-es,1085
|
59
|
-
versionhq/tool/composio_tool.py,sha256=
|
59
|
+
versionhq/tool/composio_tool.py,sha256=IATfsEnF_1RPJyGtPBmAtEJh5XPcgDHpyG3SUR461Og,8572
|
60
60
|
versionhq/tool/composio_tool_vars.py,sha256=FvBuEXsOQUYnN7RTFxT20kAkiEYkxWKkiVtgpqOzKZQ,1843
|
61
61
|
versionhq/tool/decorator.py,sha256=C4ZM7Xi2gwtEMaSeRo-geo_g_MAkY77WkSLkAuY0AyI,1205
|
62
62
|
versionhq/tool/model.py,sha256=PO4zNWBZcJhYVur381YL1dy6zqurio2jWjtbxOxZMGI,12194
|
63
63
|
versionhq/tool/tool_handler.py,sha256=2m41K8qo5bGCCbwMFferEjT-XZ-mE9F0mDUOBkgivOI,1416
|
64
|
-
versionhq-1.2.1.
|
65
|
-
versionhq-1.2.1.
|
66
|
-
versionhq-1.2.1.
|
67
|
-
versionhq-1.2.1.
|
68
|
-
versionhq-1.2.1.
|
64
|
+
versionhq-1.2.1.17.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
|
65
|
+
versionhq-1.2.1.17.dist-info/METADATA,sha256=WenkpMZmDc-5XW_iq-8ksRqp3YTvXzAbr5sjEfw09BQ,22033
|
66
|
+
versionhq-1.2.1.17.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
67
|
+
versionhq-1.2.1.17.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
|
68
|
+
versionhq-1.2.1.17.dist-info/RECORD,,
|
versionhq/task/formation.py
DELETED
@@ -1,159 +0,0 @@
|
|
1
|
-
from typing import List, Type
|
2
|
-
from enum import Enum
|
3
|
-
|
4
|
-
from pydantic import BaseModel
|
5
|
-
|
6
|
-
from versionhq.task.model import Task
|
7
|
-
from versionhq.agent.model import Agent
|
8
|
-
from versionhq.agent_network.model import AgentNetwork, Member, Formation
|
9
|
-
from versionhq.agent.inhouse_agents import vhq_formation_planner
|
10
|
-
from versionhq._utils import Logger
|
11
|
-
|
12
|
-
|
13
|
-
def form_agent_network(
|
14
|
-
task: str,
|
15
|
-
expected_outcome: str,
|
16
|
-
agents: List[Agent] = None,
|
17
|
-
context: str = None,
|
18
|
-
formation: Type[Formation] = None
|
19
|
-
) -> AgentNetwork | None:
|
20
|
-
"""
|
21
|
-
Make a formation of agents from the given task description, expected outcome, agents (optional), and context (optional).
|
22
|
-
"""
|
23
|
-
|
24
|
-
if not task:
|
25
|
-
Logger(verbose=True).log(level="error", message="Missing task description.", color="red")
|
26
|
-
return None
|
27
|
-
|
28
|
-
if not expected_outcome:
|
29
|
-
Logger(verbose=True).log(level="error", message="Missing expected outcome.", color="red")
|
30
|
-
return None
|
31
|
-
|
32
|
-
if formation:
|
33
|
-
try:
|
34
|
-
match formation:
|
35
|
-
case Formation():
|
36
|
-
if formation == Formation.UNDEFINED:
|
37
|
-
formation = None
|
38
|
-
else:
|
39
|
-
pass
|
40
|
-
|
41
|
-
case str():
|
42
|
-
matched = [item for item in Formation.s_ 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
|
60
|
-
|
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__)}."
|
63
|
-
class Outcome(BaseModel):
|
64
|
-
formation: Enum
|
65
|
-
agent_roles: list[str]
|
66
|
-
task_descriptions: list[str]
|
67
|
-
leader_agent: str
|
68
|
-
|
69
|
-
vhq_task = Task(
|
70
|
-
description=f"""
|
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. When you have a leader_agent, the formation must be SUPERVISING or HYBRID.
|
72
|
-
Task: {str(task)}
|
73
|
-
Expected outcome: {str(expected_outcome)}
|
74
|
-
Formation: {prompt_formation}
|
75
|
-
""",
|
76
|
-
pydantic_output=Outcome
|
77
|
-
)
|
78
|
-
|
79
|
-
if agents:
|
80
|
-
vhq_task.description += "Consider adding following agents in the formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
|
81
|
-
|
82
|
-
res = vhq_task.execute(agent=vhq_formation_planner, context=context)
|
83
|
-
_formation = Formation.SUPERVISING
|
84
|
-
|
85
|
-
|
86
|
-
if res.pydantic:
|
87
|
-
formation_keys = [k for k, v in Formation._member_map_.items() if k == res.pydantic.formation.upper()]
|
88
|
-
|
89
|
-
if formation_keys:
|
90
|
-
_formation = Formation[formation_keys[0]]
|
91
|
-
|
92
|
-
|
93
|
-
network_tasks = []
|
94
|
-
members = []
|
95
|
-
leader = str(res.pydantic.leader_agent)
|
96
|
-
|
97
|
-
created_agents = [Agent(role=item, goal=item) for item in res.pydantic.agent_roles]
|
98
|
-
created_tasks = [Task(description=item) for item in res.pydantic.task_descriptions]
|
99
|
-
|
100
|
-
|
101
|
-
for i in range(len(created_agents)):
|
102
|
-
is_manager = bool(created_agents[i].role.lower() == leader.lower())
|
103
|
-
member = Member(agent=created_agents[i], is_manager=is_manager)
|
104
|
-
|
105
|
-
if len(created_tasks) >= i and created_tasks[i]:
|
106
|
-
member.tasks.append(created_tasks[i])
|
107
|
-
members.append(member)
|
108
|
-
|
109
|
-
|
110
|
-
if len(created_agents) < len(created_tasks):
|
111
|
-
network_tasks.extend(created_tasks[len(created_agents):len(created_tasks)])
|
112
|
-
|
113
|
-
if _formation == Formation.SUPERVISING and not [member for member in members if member.is_manager]:
|
114
|
-
manager = Member(agent=Agent(role=leader, goal=leader), is_manager=True)
|
115
|
-
members.append(manager)
|
116
|
-
|
117
|
-
members.sort(key=lambda x: x.is_manager == False)
|
118
|
-
network = AgentNetwork(members=members, formation=_formation, network_tasks=network_tasks)
|
119
|
-
return network
|
120
|
-
|
121
|
-
else:
|
122
|
-
res = res.json_dict
|
123
|
-
formation_keys = [k for k, v in Formation._member_map_.items() if k == res["formation"].upper()]
|
124
|
-
|
125
|
-
if formation_keys:
|
126
|
-
_formation = Formation[formation_keys[0]]
|
127
|
-
|
128
|
-
created_agents = [Agent(role=item, goal=item) for item in res["agent_roles"]]
|
129
|
-
created_tasks = [Task(description=item) for item in res["task_descriptions"]]
|
130
|
-
|
131
|
-
network_tasks = []
|
132
|
-
members = []
|
133
|
-
leader = str(res["leader_agent"])
|
134
|
-
|
135
|
-
for i in range(len(created_agents)):
|
136
|
-
is_manager = bool(created_agents[i].role.lower() == leader.lower())
|
137
|
-
member = Member(agent=created_agents[i], is_manager=is_manager)
|
138
|
-
|
139
|
-
if len(created_tasks) >= i and created_tasks[i]:
|
140
|
-
member.tasks.append(created_tasks[i])
|
141
|
-
|
142
|
-
members.append(member)
|
143
|
-
|
144
|
-
if len(created_agents) < len(created_tasks):
|
145
|
-
network_tasks.extend(created_tasks[len(created_agents):len(created_tasks)])
|
146
|
-
|
147
|
-
if _formation == Formation.SUPERVISING and not [member for member in members if member.is_manager]:
|
148
|
-
member = Member(agent=Agent(role=leader, goal=leader), is_manager=True)
|
149
|
-
members.append(member)
|
150
|
-
|
151
|
-
members.sort(key=lambda x: x.is_manager == False)
|
152
|
-
network = AgentNetwork(members=members, formation=_formation, network_tasks=network_tasks)
|
153
|
-
|
154
|
-
return network
|
155
|
-
|
156
|
-
|
157
|
-
except Exception as e:
|
158
|
-
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")
|
159
|
-
return None
|
File without changes
|
File without changes
|
File without changes
|