versionhq 1.1.11.8__py3-none-any.whl → 1.1.12.2__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/memory/model.py CHANGED
@@ -1,7 +1,53 @@
1
+ import datetime
1
2
  from typing import Any, Dict, List, Optional
2
3
 
3
4
  from versionhq.storage.rag_storage import RAGStorage
4
5
  from versionhq.storage.ltm_sqlite_storage import LTMSQLiteStorage
6
+ from versionhq._utils.logger import Logger
7
+
8
+
9
+ class MemoryData:
10
+ """
11
+ A class to store structured data to store in the memory.
12
+ """
13
+ def __init__(
14
+ self,
15
+ agent: Optional[str] = None, # task execution agent (core)
16
+ task_description: Optional[str] = None,
17
+ task_output: Optional[str] = None,
18
+ config: Optional[Dict[str, Any]] = None
19
+ ):
20
+ self.agent = agent
21
+ self.task_description = task_description
22
+ self.task_output = task_output
23
+
24
+ if config:
25
+ for k, v in config.items():
26
+ setattr(self, k, str(v))
27
+
28
+
29
+
30
+ class MemoryMetadata:
31
+ """
32
+ A class to store structured metadata to store in the memory.
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ eval_criteria: Optional[str] = None,
38
+ score: Optional[int | float] = None,
39
+ suggestion: Optional[str] = None,
40
+ eval_by: Optional[str] = None, # task evaluator agent
41
+ config: Optional[Dict[str, Any]] = None
42
+ ):
43
+ self.eval_criteria = eval_criteria
44
+ self.score = score
45
+ self.suggestion = suggestion
46
+ self.eval_by = eval_by
47
+
48
+ if config:
49
+ for k, v in config.items():
50
+ setattr(self, k, str(v))
5
51
 
6
52
 
7
53
  class Memory:
@@ -13,28 +59,46 @@ class Memory:
13
59
  self.storage = storage
14
60
 
15
61
 
16
- def save(self, value: Any, metadata: Optional[Dict[str, Any]] = None, agent: Optional[str] = None) -> None:
17
- metadata = metadata or {}
62
+ def save(
63
+ self,
64
+ data: MemoryData | Dict[str, Any],
65
+ metadata: Optional[MemoryMetadata | Dict[str, Any]] = None,
66
+ agent: Optional[str] = None
67
+ ) -> None:
68
+
69
+ """
70
+ Create a dict for data and metadata without empty values before storing them in the given storage.
71
+ """
72
+
73
+ if not data:
74
+ Logger(verbose=True).log(level="error", message="Missing data to store. Add either dict or MemoryData object", color="red")
75
+ return None
18
76
 
19
- if agent:
20
- metadata["agent"] = agent
21
- self.storage.save(value, metadata)
77
+ metadata_dict = metadata if isinstance(metadata, dict) else metadata.__dict__ if isinstance(metadata, MemoryMetadata) else dict()
78
+ metadata_dict = {k: v for k, v in metadata_dict.items() if v} # remove empty values
79
+ data_dict = data if isinstance(data, dict) else data.__dict__ if isinstance(data, MemoryData) else dict()
80
+ data_dict = {k: v for k, v in data_dict.items() if v}
81
+
82
+ if agent and data_dict["agent"] is None:
83
+ data_dict["agent"] = agent
84
+
85
+ if metadata_dict:
86
+ self.storage.save(data=data_dict, metadata=metadata_dict)
87
+ else:
88
+ self.storage.save(data=data_dict)
22
89
 
23
90
 
24
91
  def search(self, query: str, limit: int = 3, score_threshold: float = 0.35) -> List[Any]:
25
92
  return self.storage.search(query=query, limit=limit, score_threshold=score_threshold)
26
93
 
27
94
 
95
+ class MemoryItem:
96
+ """
97
+ A class to store item to be saved in either long term memory or short term memory.
98
+ """
28
99
 
29
- class ShortTermMemoryItem:
30
- def __init__(
31
- self,
32
- data: Any,
33
- agent: Optional[str] = None,
34
- metadata: Optional[Dict[str, Any]] = None,
35
- ):
100
+ def __init__(self, data: MemoryData = None, metadata: Optional[MemoryMetadata] = None):
36
101
  self.data = data
37
- self.agent = agent
38
102
  self.metadata = metadata if metadata is not None else {}
39
103
 
40
104
 
@@ -70,12 +134,22 @@ class ShortTermMemory(Memory):
70
134
  super().__init__(storage)
71
135
 
72
136
 
73
- def save(self, value: Any, metadata: Optional[Dict[str, Any]] = None, agent: Optional[str] = None) -> None:
74
- item = ShortTermMemoryItem(data=value, metadata=metadata, agent=agent)
137
+ def save(
138
+ self,
139
+ task_description: str = None,
140
+ task_output: str = None,
141
+ agent: Optional[str] = None,
142
+ data: Optional[MemoryData] = None,
143
+ metadata: Optional[MemoryMetadata] = None
144
+ ) -> None:
145
+
146
+ data = data if data else MemoryData(task_description=task_description, task_output=task_output, agent=agent)
147
+ item = MemoryItem(data=data, metadata=metadata)
148
+
75
149
  if self.memory_provider == "mem0":
76
- item.data = f"Remember the following insights from Agent run: {item.data}"
150
+ item.data.task_output = f"Remember the following insights from Agent run: {item.data.task_output}"
77
151
 
78
- super().save(value=item.data, metadata=item.metadata, agent=item.agent)
152
+ super().save(data=item.data.__dict__, metadata=item.metadata.__dict__ if item.metadata else {})
79
153
 
80
154
 
81
155
  def search(self, query: str, limit: int = 3, score_threshold: float = 0.35,):
@@ -89,29 +163,11 @@ class ShortTermMemory(Memory):
89
163
  raise Exception(f"An error occurred while resetting the short-term memory: {str(e)}")
90
164
 
91
165
 
92
-
93
- class LongTermMemoryItem:
94
- def __init__(
95
- self,
96
- agent: str,
97
- task: str,
98
- datetime: str,
99
- quality: Optional[int | float] = None,
100
- metadata: Optional[Dict[str, Any]] = None,
101
- ):
102
- self.task = task
103
- self.agent = agent
104
- self.quality = quality
105
- self.datetime = datetime
106
- self.metadata = metadata if metadata is not None else {}
107
-
108
-
109
-
110
166
  class LongTermMemory(Memory):
111
167
  """
112
168
  A class for managing cross runs data related to overall task executions.
113
169
  - Type: ltm
114
- - Storage: LTMSQLiteStorage
170
+ - Storage: LTMSQLiteStorage | RAGStorage
115
171
  """
116
172
 
117
173
  def __init__(self, storage=None, path=None):
@@ -121,19 +177,25 @@ class LongTermMemory(Memory):
121
177
  super().__init__(storage)
122
178
 
123
179
 
124
- def save(self, item: LongTermMemoryItem) -> None:
125
- metadata = item.metadata
126
- metadata.update({ "agent": item.agent })
127
- self.storage.save(
128
- task_description=item.task,
129
- score=metadata["quality"],
130
- metadata=metadata,
131
- datetime=item.datetime,
132
- )
180
+ def save(
181
+ self,
182
+ task_description: str = None,
183
+ task_output: str = None,
184
+ agent: Optional[str] = None,
185
+ data: Optional[MemoryData] = None,
186
+ metadata: Optional[MemoryMetadata] = None
187
+ ) -> None:
188
+
189
+ data = data if data else MemoryData(task_description=task_description, task_output=task_output, agent=agent)
190
+ item = MemoryItem(data=data, metadata=metadata)
191
+ super().save(data=item.data, metadata=item.metadata)
133
192
 
134
193
 
135
- def search(self, task: str, latest_n: int = 3) -> List[Dict[str, Any]]:
136
- return self.storage.load(task, latest_n)
194
+ def search(self, query: str, latest_n: int = 3) -> List[Dict[str, Any]]:
195
+ """
196
+ Query the storage and return the results up to latest_n.
197
+ """
198
+ return self.storage.load(query=query, latest_n=latest_n)
137
199
 
138
200
 
139
201
  def reset(self) -> None:
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import datetime
2
3
  import sqlite3
3
4
  from pathlib import Path
4
5
  from typing import Any, Dict, List, Optional
@@ -27,88 +28,77 @@ class LTMSQLiteStorage:
27
28
  """
28
29
  Initializes the SQLite database and creates LTM table
29
30
  """
31
+
30
32
  try:
31
33
  with sqlite3.connect(self.db_path) as conn:
32
34
  cursor = conn.cursor()
33
35
  cursor.execute(
34
- """
36
+ """
35
37
  CREATE TABLE IF NOT EXISTS long_term_memories (
36
38
  id INTEGER PRIMARY KEY AUTOINCREMENT,
37
- task_description TEXT,
38
- metadata TEXT,
39
- datetime TEXT,
40
- score REAL
39
+ datetime REAL,
40
+ data TEXT,
41
+ metadata TEXT
41
42
  )
42
43
  """
43
44
  )
44
-
45
45
  conn.commit()
46
46
 
47
47
  except sqlite3.Error as e:
48
- self._logger.log(
49
- level="error",
50
- message=f"MEMORY ERROR: An error occurred during database initialization: {str(e)}",
51
- color="red",
52
- )
48
+ self._logger.log(level="error", message=f"MEMORY ERROR: An error occurred during database initialization: {str(e)}", color="red")
49
+
53
50
 
54
- def save(self, task_description: str, metadata: Dict[str, Any], datetime: str, score: int | float) -> None:
51
+ def save(self, data: Dict[str, Any] | str, metadata: Optional[Dict[str, Any]] = {}) -> None:
55
52
  """
56
53
  Saves data to the LTM table with error handling.
57
54
  """
55
+ data = data if isinstance(data, dict) else dict(data=data)
56
+
58
57
  try:
59
58
  with sqlite3.connect(self.db_path) as conn:
60
59
  cursor = conn.cursor()
61
60
  cursor.execute(
62
- """
63
- INSERT INTO long_term_memories (task_description, metadata, datetime, score)
64
- VALUES (?, ?, ?, ?)
61
+ """
62
+ INSERT INTO long_term_memories (datetime, data, metadata)
63
+ VALUES (?, ?, ?)
65
64
  """,
66
- (task_description, json.dumps(metadata), datetime, score),
65
+ (datetime.datetime.now().timestamp(), json.dumps(data), json.dumps(metadata)),
67
66
  )
68
67
  conn.commit()
69
68
  except sqlite3.Error as e:
70
- self._logger.log(
71
- level="error",
72
- message=f"MEMORY ERROR: An error occurred while saving to LTM: {str(e)}",
73
- color="red",
74
- )
69
+ self._logger.log(level="error", message=f"MEMORY ERROR: An error occurred while saving to LTM: {str(e)}", color="red")
75
70
 
76
71
 
77
- def load(self, task_description: str, latest_n: int) -> Optional[List[Dict[str, Any]]]:
72
+ def load(self, query: str, latest_n: int) -> Optional[List[Dict[str, Any]]]:
78
73
  """
79
- Queries the LTM table by task description with error handling.
74
+ Queries the data row in the storage with error handling.
80
75
  """
81
76
  try:
82
77
  with sqlite3.connect(self.db_path) as conn:
83
78
  cursor = conn.cursor()
84
79
  cursor.execute(
85
- f"""
86
- SELECT metadata, datetime, score
80
+ f"""
81
+ SELECT datetime, data, metadata
87
82
  FROM long_term_memories
88
- WHERE task_description = ?
89
- ORDER BY datetime DESC, score ASC
83
+ WHERE data LIKE '%{query}%'
84
+ ORDER BY datetime
90
85
  LIMIT {latest_n}
91
- """,
92
- (task_description,),
86
+ """
93
87
  )
94
88
  rows = cursor.fetchall()
95
89
  if rows:
96
90
  return [
97
91
  {
98
- "metadata": json.loads(row[0]),
99
- "datetime": row[1],
100
- "score": row[2],
92
+ "datetime": row[0],
93
+ "data": json.loads(row[1]),
94
+ "metadata": json.loads(row[2]),
101
95
  }
102
96
  for row in rows
103
97
  ]
104
98
 
105
99
  except sqlite3.Error as e:
106
- self._logger.log(
107
- level="error",
108
- message=f"MEMORY ERROR: An error occurred while querying LTM: {e}",
109
- color="red",
110
- )
111
- return None
100
+ self._logger.log(level="error", message=f"MEMORY ERROR: An error occurred while querying LTM: {e}",color="red")
101
+ return None
112
102
 
113
103
 
114
104
  def reset(self) -> None:
@@ -123,9 +113,5 @@ class LTMSQLiteStorage:
123
113
  conn.commit()
124
114
 
125
115
  except sqlite3.Error as e:
126
- self._logger.log(
127
- level="error",
128
- message=f"MEMORY ERROR: An error occurred while deleting all rows in LTM: {str(e)}",
129
- color="red",
130
- )
116
+ self._logger.log(level="error", message=f"MEMORY ERROR: An error occurred while deleting all rows in LTM: {str(e)}", color="red")
131
117
  return None
@@ -47,7 +47,7 @@ class Mem0Storage(Storage):
47
47
  return role.replace("\n", "").replace(" ", "_").replace("/", "_")
48
48
 
49
49
 
50
- def save(self, value: Any, metadata: Dict[str, Any]) -> None:
50
+ def save(self, value: Dict[str, Any] | str, metadata: Dict[str, Any]) -> None:
51
51
  user_id = self._get_user_id()
52
52
  agent_name = self._get_agent_name()
53
53
 
@@ -163,11 +163,32 @@ class RAGStorage(BaseRAGStorage):
163
163
  return f"{base_path}/{file_name}"
164
164
 
165
165
 
166
- def save(self, value: Any, metadata: Dict[str, Any]) -> None:
166
+ def _create_default_embedding_function(self):
167
+ from chromadb.utils.embedding_functions.openai_embedding_function import (
168
+ OpenAIEmbeddingFunction,
169
+ )
170
+
171
+ return OpenAIEmbeddingFunction(
172
+ api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
173
+ )
174
+
175
+
176
+ def _generate_embedding(self, text: str, metadata: Optional[Dict[str, Any]]) -> None:
177
+ if not hasattr(self, "app") or not hasattr(self, "collection"):
178
+ self._initialize_app()
179
+
180
+ if metadata:
181
+ self.collection.add(documents=[text], metadatas=[metadata, ], ids=[str(uuid.uuid4())])
182
+
183
+ else:
184
+ self.collection.add(documents=[text], ids=[str(uuid.uuid4())])
185
+
186
+
187
+ def save(self, data: Dict[str, Any] | str, metadata: Optional[Dict[str, Any]] = dict()) -> None:
167
188
  if not hasattr(self, "app") or not hasattr(self, "collection"):
168
189
  self._initialize_app()
169
190
  try:
170
- self._generate_embedding(value, metadata)
191
+ self._generate_embedding(text=str(data), metadata=metadata)
171
192
  except Exception as e:
172
193
  logging.error(f"Error during {self.type} save: {str(e)}")
173
194
 
@@ -197,17 +218,6 @@ class RAGStorage(BaseRAGStorage):
197
218
  return []
198
219
 
199
220
 
200
- def _generate_embedding(self, text: str, metadata: Dict[str, Any]) -> None:
201
- if not hasattr(self, "app") or not hasattr(self, "collection"):
202
- self._initialize_app()
203
-
204
- self.collection.add(
205
- documents=[text],
206
- metadatas=[metadata or {}],
207
- ids=[str(uuid.uuid4())],
208
- )
209
-
210
-
211
221
  def reset(self) -> None:
212
222
  try:
213
223
  if self.app:
@@ -220,12 +230,3 @@ class RAGStorage(BaseRAGStorage):
220
230
  pass
221
231
  else:
222
232
  raise Exception(f"An error occurred while resetting the {self.type} memory: {e}")
223
-
224
- def _create_default_embedding_function(self):
225
- from chromadb.utils.embedding_functions.openai_embedding_function import (
226
- OpenAIEmbeddingFunction,
227
- )
228
-
229
- return OpenAIEmbeddingFunction(
230
- api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
231
- )
@@ -7,7 +7,7 @@ from versionhq._utils.logger import Logger
7
7
  from versionhq.storage.utils import fetch_db_storage_path
8
8
 
9
9
  storage_path = fetch_db_storage_path()
10
- default_db_name = "task_outputs"
10
+ default_db_name = "task_output"
11
11
 
12
12
 
13
13
  class TaskOutputSQLiteStorage:
@@ -31,7 +31,7 @@ class TaskOutputSQLiteStorage:
31
31
  cursor = conn.cursor()
32
32
  cursor.execute(
33
33
  """
34
- CREATE TABLE IF NOT EXISTS task_outputs (
34
+ CREATE TABLE IF NOT EXISTS task_output (
35
35
  task_id TEXT PRIMARY KEY,
36
36
  output JSON,
37
37
  task_index INTEGER,
@@ -52,7 +52,7 @@ class TaskOutputSQLiteStorage:
52
52
  with sqlite3.connect(self.db_path) as conn:
53
53
  cursor = conn.cursor()
54
54
  cursor.execute(
55
- """INSERT OR REPLACE INTO task_outputs
55
+ """INSERT OR REPLACE INTO task_output
56
56
  (task_id, output, task_index, inputs, was_replayed, timestamp)
57
57
  VALUES (?, ?, ?, ?, ?, ?)
58
58
  """,
@@ -73,7 +73,7 @@ class TaskOutputSQLiteStorage:
73
73
  fields.append(f"{k} = ?")
74
74
  values.append(json.dumps(v) if isinstance(v, dict) else v)
75
75
 
76
- query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?"
76
+ query = f"UPDATE latest_kickoff_task_output SET {', '.join(fields)} WHERE task_index = ?"
77
77
  values.append(task_index)
78
78
  cursor.execute(query, tuple(values))
79
79
  conn.commit()
@@ -93,7 +93,7 @@ class TaskOutputSQLiteStorage:
93
93
  cursor = conn.cursor()
94
94
  cursor.execute("""
95
95
  SELECT *
96
- FROM task_outputs
96
+ FROM task_output
97
97
  ORDER BY task_index
98
98
  """)
99
99
 
@@ -120,7 +120,7 @@ class TaskOutputSQLiteStorage:
120
120
  try:
121
121
  with sqlite3.connect(self.db_path) as conn:
122
122
  cursor = conn.cursor()
123
- cursor.execute("DELETE FROM task_outputs")
123
+ cursor.execute("DELETE FROM task_output")
124
124
  conn.commit()
125
125
 
126
126
  except sqlite3.Error as e:
@@ -1,5 +1,5 @@
1
- EVALUATE="""Assess the accuracy and quality of the following task output to the task described below. Score based on the criterion (0-1, 0=worst, 1=best) and suggest improvements. Vary scores; don't assign identical values. Store criteria in the "criteria" field.
2
- Task: {task_description}
3
- Task Output: {task_output}
1
+ EVALUATE="""Evaluate the provided task output against the given task description, assigning a score between 0 (worst) and 1 (best) based on the specified criteria. Scores should be numerical (integers or decimals). Provide specific suggestions for improvement. Do not assign identical scores to different criteria:
2
+ Task output: {task_output}
3
+ Task description: {task_description}
4
4
  Evaluation criteria: {eval_criteria}
5
5
  """
@@ -1,9 +0,0 @@
1
- from enum import Enum
2
-
3
-
4
- class TaskOutputFormat(str, Enum):
5
- """Enum that represents the output format of a task."""
6
-
7
- JSON = "json"
8
- PYDANTIC = "pydantic model"
9
- RAW = "raw"
@@ -1,15 +1,15 @@
1
1
  from typing import List, Optional, Dict, Any
2
2
  from typing_extensions import Self
3
3
 
4
- from pydantic import BaseModel, Field, InstanceOf, model_validator
4
+ from pydantic import BaseModel, Field, model_validator
5
+
6
+ from versionhq.memory.model import MemoryMetadata
5
7
 
6
8
  """
7
9
  Evaluate task output from accuracy, token consumption, latency perspectives, and mark the score from 0 to 1.
8
10
  """
9
11
 
10
12
 
11
-
12
-
13
13
  class ScoreFormat:
14
14
  def __init__(self, rate: float | int = 0, weight: int = 1):
15
15
  self.rate = rate
@@ -61,10 +61,10 @@ class EvaluationItem(BaseModel):
61
61
  """
62
62
  criteria: str
63
63
  suggestion: str
64
- score: int | float
64
+ score: float
65
65
 
66
66
  def _convert_score_to_score_format(self, weight: int = 1) -> ScoreFormat | None:
67
- if self.score and isinstance(self.score, (int, float)):
67
+ if self.score and isinstance(self.score, float):
68
68
  return ScoreFormat(rate=self.score, weight=weight)
69
69
 
70
70
  else: return None
@@ -72,7 +72,6 @@ class EvaluationItem(BaseModel):
72
72
 
73
73
 
74
74
  class Evaluation(BaseModel):
75
- # expected_outcome: Optional[str] = Field(default=None, description="human input on expected outcome")
76
75
  items: List[EvaluationItem] = []
77
76
  latency: int = Field(default=None, description="seconds")
78
77
  tokens: int = Field(default=None, description="tokens consumed")
@@ -80,11 +79,23 @@ class Evaluation(BaseModel):
80
79
 
81
80
  @model_validator(mode="after")
82
81
  def set_up_responsible_agent(self) -> Self:
83
- from versionhq.agent.default_agents import task_evaluator
84
- self.responsible_agent = task_evaluator
82
+ from versionhq.agent.inhouse_agents import vhq_task_evaluator
83
+ self.responsible_agent = vhq_task_evaluator
85
84
  return self
86
85
 
87
86
 
87
+ def _create_memory_metadata(self) -> MemoryMetadata:
88
+ """
89
+ Create and store evaluation results in the memory metadata
90
+ """
91
+ eval_by = self.responsible_agent.role if self.responsible_agent else None
92
+ score = self.aggregate_score
93
+ eval_criteria = ", ".join([item.criteria for item in self.items]) if self.items else None
94
+ suggestion = self.suggestion_summary
95
+ memory_metadata = MemoryMetadata(eval_by=eval_by, score=score, eval_criteria=eval_criteria, suggestion=suggestion)
96
+ return memory_metadata
97
+
98
+
88
99
  @property
89
100
  def aggregate_score(self) -> float:
90
101
  """
@@ -0,0 +1,123 @@
1
+ from typing import List
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.team.model import Team, TeamMember, 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_overview: str,
15
+ expected_outcome: str,
16
+ agents: List[Agent] = None,
17
+ context: str = None,
18
+ formation: Formation = None
19
+ ) -> Team | None:
20
+ """
21
+ Make a formation of agents from the given task description, agents (optional), context (optional), and expected outcome (optional).
22
+ """
23
+
24
+ if not task_overview:
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
+
33
+ try:
34
+ class Outcome(BaseModel):
35
+ formation: Enum
36
+ agent_roles: list[str]
37
+ task_descriptions: list[str]
38
+ leader_agent: str
39
+
40
+ vhq_task = Task(
41
+ description=f"""
42
+ 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
+ Task: {str(task_overview)}
44
+ Expected outcome: {str(expected_outcome)}
45
+ """,
46
+ pydantic_output=Outcome
47
+ )
48
+
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
+ if agents:
53
+ vhq_task.description += "Consider adding following agents in the formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
54
+
55
+ res = vhq_task.execute_sync(agent=vhq_formation_planner, context=context)
56
+ formation_ = Formation.SUPERVISING
57
+
58
+ if res.pydantic:
59
+ formation_keys = [k for k, v in Formation._member_map_.items() if k == res.pydantic.formation.upper()]
60
+
61
+ if formation_keys:
62
+ formation_ = Formation[formation_keys[0]]
63
+
64
+ created_agents = [Agent(role=item, goal=item) for item in res.pydantic.agent_roles]
65
+ created_tasks = [Task(description=item) for item in res.pydantic.task_descriptions]
66
+ team_tasks = []
67
+ members = []
68
+ leader = str(res.pydantic.leader_agent)
69
+
70
+ for i in range(len(created_agents)):
71
+ is_manager = bool(created_agents[i].role.lower() in leader.lower())
72
+ member = TeamMember(agent=created_agents[i], is_manager=is_manager)
73
+
74
+ if len(created_tasks) >= i:
75
+ member.task = created_tasks[i]
76
+ members.append(member)
77
+
78
+ if len(created_agents) < len(created_tasks):
79
+ team_tasks.extend(created_tasks[len(created_agents) - 1:len(created_tasks)])
80
+
81
+ members.sort(key=lambda x: x.is_manager == False)
82
+ team = Team(members=members, formation=formation_)
83
+ return team
84
+
85
+ else:
86
+ formation_keys = [k for k, v in Formation._member_map_.items() if k == res.json_dict["formation"].upper()]
87
+
88
+ if formation_keys:
89
+ formation_ = Formation[formation_keys[0]]
90
+
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
+ team_tasks = []
94
+ members = []
95
+ leader = str(res.json_dict["leader_agent"])
96
+
97
+ for i in range(len(created_agents)):
98
+ is_manager = bool(created_agents[i].role.lower() in leader.lower())
99
+ member = TeamMember(agent=created_agents[i], is_manager=is_manager)
100
+
101
+ if len(created_tasks) >= i:
102
+ member.task = created_tasks[i]
103
+ members.append(member)
104
+
105
+ if len(created_agents) < len(created_tasks):
106
+ team_tasks.extend(created_tasks[len(created_agents) - 1:len(created_tasks)])
107
+
108
+ members.sort(key=lambda x: x.is_manager == True)
109
+ team = Team(members=members, formation=formation_)
110
+ return team
111
+
112
+ except Exception as e:
113
+ Logger(verbose=True).log(level="error", message=f"Failed to create an agent network - return None. You can try with solo agent. Error: {str(e)}", color="red")
114
+ return None
115
+
116
+
117
+
118
+ if __name__ == "__main__":
119
+ res = form_agent_network(
120
+ task_overview="Launch an outbound campaign to attract young audience.",
121
+ expected_outcome="Best media mix of the campaign.",
122
+ context="We are selling sports wear.",
123
+ )