versionhq 1.1.12.5__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 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, TeamMember, TaskHandlingProcess
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.12.5"
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
- "TeamMember",
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 metrics for the agent/team's execution.
6
+ Model to track usage
7
7
  """
8
8
 
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")
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, top_k=30, temperature=0.9),
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/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",
@@ -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
- responsible_agent: Any = Field(default=None, description="store agent instance that evaluates the outcome")
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 set_up_responsible_agent(self) -> Self:
80
+ def set_up_evaluator(self) -> Self:
82
81
  from versionhq.agent.inhouse_agents import vhq_task_evaluator
83
- self.responsible_agent = vhq_task_evaluator
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.responsible_agent.role if self.responsible_agent else None
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
@@ -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, TeamMember, Formation
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, agents (optional), context (optional), and expected outcome (optional).
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
- formation_ = Formation.SUPERVISING
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
- formation_ = Formation[formation_keys[0]]
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() in leader.lower())
72
- member = TeamMember(agent=created_agents[i], is_manager=is_manager)
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) - 1:len(created_tasks)])
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=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
- formation_keys = [k for k, v in Formation._member_map_.items() if k == res.json_dict["formation"].upper()]
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
- formation_ = Formation[formation_keys[0]]
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.json_dict["leader_agent"])
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() in leader.lower())
99
- member = TeamMember(agent=created_agents[i], is_manager=is_manager)
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
- if len(created_tasks) >= i:
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) - 1:len(created_tasks)])
139
+ team_tasks.extend(created_tasks[len(created_agents):len(created_tasks)])
107
140
 
108
- members.sort(key=lambda x: x.is_manager == True)
109
- team = Team(members=members, formation=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 an agent network - return None. You can try with solo agent. Error: {str(e)}", color="red")
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/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.responsible_agent)
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
- Task to be executed by agents or teams.
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__
versionhq/team/model.py CHANGED
@@ -39,12 +39,12 @@ load_dotenv(override=True)
39
39
 
40
40
 
41
41
  class Formation(str, Enum):
42
+ UNDEFINED = 0
42
43
  SOLO = 1
43
44
  SUPERVISING = 2
44
45
  NETWORK = 3
45
46
  RANDOM = 4
46
47
  HYBRID = 10
47
- UNDEFINED = 0
48
48
 
49
49
 
50
50
  class TaskHandlingProcess(str, Enum):
@@ -75,6 +75,7 @@ class TeamOutput(TaskOutput):
75
75
  def __str__(self):
76
76
  return (str(self.pydantic) if self.pydantic else str(self.json_dict) if self.json_dict else self.raw)
77
77
 
78
+
78
79
  def __getitem__(self, key):
79
80
  if self.pydantic and hasattr(self.pydantic, key):
80
81
  return getattr(self.pydantic, key)
@@ -85,25 +86,24 @@ class TeamOutput(TaskOutput):
85
86
 
86
87
 
87
88
 
88
- class TeamMember(BaseModel):
89
+ class Member(BaseModel):
89
90
  """
90
- A class to store a team member
91
+ A class to store a member in the network and connect the agent as a member with tasks and sharable settings.
91
92
  """
92
93
  agent: Agent | None = Field(default=None)
93
94
  is_manager: bool = Field(default=False)
94
95
  can_share_knowledge: bool = Field(default=True, description="whether to share the agent's knowledge in the team")
95
96
  can_share_memory: bool = Field(default=True, description="whether to share the agent's memory in the team")
96
- task: Optional[Task] = Field(default=None, description="task assigned to the agent")
97
+ tasks: Optional[List[Task]] = Field(default_factory=list, description="tasks explicitly assigned to the agent")
97
98
 
98
99
  @property
99
100
  def is_idling(self):
100
- return bool(self.task is None)
101
+ return bool(self.tasks)
101
102
 
102
103
 
103
104
  class Team(BaseModel):
104
105
  """
105
- A collaborative team of agents that handles complex, multiple tasks.
106
- We define strategies for task executions and overall workflow.
106
+ A class to store agent network that shares knowledge, memory and tools among the members.
107
107
  """
108
108
 
109
109
  __hash__ = object.__hash__
@@ -113,31 +113,31 @@ class Team(BaseModel):
113
113
 
114
114
  id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
115
115
  name: Optional[str] = Field(default=None)
116
- members: List[TeamMember] = Field(default_factory=list)
116
+ members: List[Member] = Field(default_factory=list)
117
117
  formation: Optional[Formation] = Field(default=None)
118
+ should_reform: bool = Field(default=False, description="True if task exe. failed or eval scores below threshold")
118
119
 
119
120
  # formation planning
120
- planning_llm: Optional[Any] = Field(default=None, description="llm to generate formation")
121
- team_tasks: Optional[List[Task]] = Field(default_factory=list, description="optional tasks for the team. can be assigned to team members later")
121
+ planner_llm: Optional[Any] = Field(default=None, description="llm to generate and evaluate formation")
122
+ team_tasks: Optional[List[Task]] = Field(default_factory=list, description="tasks without dedicated agents to handle")
122
123
 
123
124
  # task execution rules
124
- prompt_file: str = Field(default="", description="path to the prompt json file to be used by the team.")
125
+ prompt_file: str = Field(default="", description="absolute path to the prompt json file")
125
126
  process: TaskHandlingProcess = Field(default=TaskHandlingProcess.sequential)
126
127
 
127
128
  # callbacks
128
- before_kickoff_callbacks: List[Callable[[Optional[Dict[str, Any]]], Optional[Dict[str, Any]]]] = Field(
129
+ pre_launch_callbacks: List[Callable[[Optional[Dict[str, Any]]], Optional[Dict[str, Any]]]] = Field(
129
130
  default_factory=list,
130
- description="list of callback functions to be executed before the team kickoff. i.e., adjust inputs"
131
+ description="list of callback functions to be executed before the team launch. i.e., adjust inputs"
131
132
  )
132
- after_kickoff_callbacks: List[Callable[[TeamOutput], TeamOutput]] = Field(
133
+ post_launch_callbacks: List[Callable[[TeamOutput], TeamOutput]] = Field(
133
134
  default_factory=list,
134
- description="list of callback functions to be executed after the team kickoff. i.e., store the result in repo"
135
+ description="list of callback functions to be executed after the team launch. i.e., store the result in repo"
135
136
  )
136
137
  step_callback: Optional[Any] = Field(default=None, description="callback to be executed after each step for all agents execution")
137
138
 
138
139
  cache: bool = Field(default=True)
139
- memory: bool = Field(default=False, description="whether the team should use memory to store memories of its execution")
140
- execution_logs: List[Dict[str, Any]] = Field(default=[], description="list of execution logs for tasks")
140
+ execution_logs: List[Dict[str, Any]] = Field(default_factory=list, description="list of execution logs of the tasks handled by members")
141
141
  usage_metrics: Optional[UsageMetrics] = Field(default=None, description="usage metrics for all the llm executions")
142
142
 
143
143
 
@@ -163,40 +163,45 @@ class Team(BaseModel):
163
163
  if all(task in self.tasks for task in self.team_tasks) == False:
164
164
  raise PydanticCustomError("task_validation_error", "`team_tasks` needs to be recognized in the task.", {})
165
165
 
166
- if len(self.tasks) != len(self.team_tasks) + len([member for member in self.members if member.task is not None]):
167
- raise PydanticCustomError("task_validation_error", "Some tasks are missing.", {})
166
+
167
+ num_member_tasks = 0
168
+ for member in self.members:
169
+ num_member_tasks += len(member.tasks)
170
+
171
+ # if len(self.tasks) != len(self.team_tasks) + num_member_tasks:
172
+ # raise PydanticCustomError("task_validation_error", "Some tasks are missing.", {})
168
173
  return self
169
174
 
170
175
 
171
176
  @model_validator(mode="after")
172
177
  def check_manager_llm(self):
173
178
  """
174
- Validates that the language model is set when using hierarchical process.
179
+ Check if the team has a manager
175
180
  """
176
181
 
177
- if self.process == TaskHandlingProcess.hierarchical:
178
- if self.managers is None:
182
+ if self.process == TaskHandlingProcess.hierarchical or self.formation == Formation.SUPERVISING:
183
+ if not self.managers:
184
+ self._logger.log(level="error", message="The process or formation created needs at least 1 manager agent.", color="red")
179
185
  raise PydanticCustomError(
180
- "missing_manager_llm_or_manager",
181
- "Attribute `manager_llm` or `manager` is required when using hierarchical process.",
182
- {},
183
- )
186
+ "missing_manager_llm_or_manager","Attribute `manager_llm` or `manager` is required when using hierarchical process.", {})
184
187
 
185
- if self.managers and (self.manager_tasks is None or self.team_tasks is None):
186
- raise PydanticCustomError("missing_manager_task", "manager needs to have at least one manager task or team task.", {})
188
+ ## comment out for the formation flexibilities
189
+ # if self.managers and (self.manager_tasks is None or self.team_tasks is None):
190
+ # self._logger.log(level="error", message="The manager is idling. At least 1 task needs to be assigned to the manager.", color="red")
191
+ # raise PydanticCustomError("missing_manager_task", "manager needs to have at least one manager task or team task.", {})
187
192
 
188
193
  return self
189
194
 
190
195
 
191
196
  @model_validator(mode="after")
192
- def validate_tasks(self):
197
+ def validate_task_member_paring(self):
193
198
  """
194
199
  Sequential task processing without any team tasks require a task-agent pairing.
195
200
  """
196
-
197
201
  if self.process == TaskHandlingProcess.sequential and self.team_tasks is None:
198
- for member in self.members:
199
- if member.task is None:
202
+ for task in self.tasks:
203
+ if not [member.task == task for member in self.members]:
204
+ self._logger.log(level="error", message=f"The following task needs a dedicated agent to be assinged: {task.description}", color="red")
200
205
  raise PydanticCustomError("missing_agent_in_task", "Sequential process error: Agent is missing the task", {})
201
206
  return self
202
207
 
@@ -224,11 +229,14 @@ class Team(BaseModel):
224
229
  if task is None:
225
230
  return None
226
231
  else:
227
- res = [member.agent for member in self.members if member.task and member.task.id == task.id]
228
- return None if len(res) == 0 else res[0]
232
+ for member in self.members:
233
+ if member.tasks and [item for item in member.tasks if item.id == task.id]:
234
+ return member.agent
235
+
236
+ return None
229
237
 
230
238
 
231
- def _handle_agent_formation(self) -> None:
239
+ def _assign_tasks(self) -> None:
232
240
  """
233
241
  Form a team considering agents and tasks given, and update `self.members` field:
234
242
  1. Idling managers to take the team tasks.
@@ -236,42 +244,26 @@ class Team(BaseModel):
236
244
  3. Create agents to handle the rest tasks.
237
245
  """
238
246
 
239
- team_planner = TeamPlanner(tasks=self.tasks, planner_llm=self.planning_llm)
240
- idling_managers: List[TeamMember] = [member for member in self.members if member.is_idling and member.is_manager is True]
241
- idling_members: List[TeamMember] = [member for member in self.members if member.is_idling and member.is_manager is False]
242
- unassigned_tasks: List[Task] = self.member_tasks_without_agent
243
- new_team_members: List[TeamMember] = []
244
-
245
- if self.team_tasks:
246
- candidates = idling_managers + idling_members
247
- if candidates:
248
- i = 0
249
- while i < len(candidates):
250
- if self.team_tasks[i]:
251
- candidates[i].task = self.team_tasks[i]
252
- i += 1
253
-
254
- if len(self.team_tasks) > i:
255
- for item in self.team_tasks[i:]:
256
- if item not in unassigned_tasks:
257
- unassigned_tasks = [item, ] + unassigned_tasks
247
+ team_planner = TeamPlanner(tasks=self.tasks, planner_llm=self.planner_llm)
248
+ idling_managers: List[Member] = [member for member in self.members if member.is_idling and member.is_manager == True]
249
+ idling_members: List[Member] = [member for member in self.members if member.is_idling and member.is_manager == False]
250
+ unassigned_tasks: List[Task] = self.team_tasks + self.member_tasks_without_agent if self.team_tasks else self.member_tasks_without_agent
251
+ new_team_members: List[Member] = []
258
252
 
259
- else:
260
- for item in self.team_tasks:
261
- if item not in unassigned_tasks:
262
- unassigned_tasks = [item, ] + unassigned_tasks
253
+ if idling_managers:
254
+ idling_managers[0].tasks.extend(unassigned_tasks)
263
255
 
264
- if unassigned_tasks:
265
- new_team_members = team_planner._handle_assign_agents(unassigned_tasks=unassigned_tasks)
256
+ elif idling_members:
257
+ idling_members[0].tasks.extend(unassigned_tasks)
266
258
 
267
- if new_team_members:
268
- self.members += new_team_members
259
+ else:
260
+ new_team_members = team_planner._handle_assign_agents(unassigned_tasks=unassigned_tasks)
261
+ if new_team_members:
262
+ self.members += new_team_members
269
263
 
270
264
 
271
265
  # task execution
272
- def _process_async_tasks(
273
- self, futures: List[Tuple[Task, Future[TaskOutput], int]], was_replayed: bool = False
274
- ) -> List[TaskOutput]:
266
+ def _process_async_tasks(self, futures: List[Tuple[Task, Future[TaskOutput], int]], was_replayed: bool = False) -> List[TaskOutput]:
275
267
  """
276
268
  When we have `Future` tasks, updated task outputs and task execution logs accordingly.
277
269
  """
@@ -292,10 +284,11 @@ class Team(BaseModel):
292
284
  Note that `tasks` are already sorted by the importance.
293
285
  """
294
286
 
295
- if len(task_outputs) < 1:
296
- raise ValueError("Something went wrong. Kickoff should return only one task output.")
287
+ if not task_outputs:
288
+ self._logger.log(level="error", message="Missing task outcomes. Failed to launch the task.", color="red")
289
+ raise ValueError("Failed to launch tasks")
297
290
 
298
- final_task_output = lead_task_output if lead_task_output is not None else task_outputs[0]
291
+ final_task_output = lead_task_output if lead_task_output is not None else task_outputs[0] #! REFINEME
299
292
  # final_string_output = final_task_output.raw
300
293
  # self._finish_execution(final_string_output)
301
294
  token_usage = self._calculate_usage_metrics()
@@ -357,7 +350,7 @@ class Team(BaseModel):
357
350
 
358
351
  responsible_agent = self._get_responsible_agent(task)
359
352
  if responsible_agent is None:
360
- self._handle_agent_formation()
353
+ self._assign_tasks()
361
354
 
362
355
  if isinstance(task, ConditionalTask):
363
356
  skipped_task_output = task._handle_conditional_task(task_outputs, futures, task_index, was_replayed)
@@ -387,29 +380,27 @@ class Team(BaseModel):
387
380
  return self._create_team_output(task_outputs, lead_task_output)
388
381
 
389
382
 
390
- def launch(self, kwargs_before: Optional[Dict[str, str]] = None, kwargs_after: Optional[Dict[str, Any]] = None) -> TeamOutput:
383
+ def launch(self, kwargs_pre: Optional[Dict[str, str]] = None, kwargs_post: Optional[Dict[str, Any]] = None) -> TeamOutput:
391
384
  """
392
385
  Confirm and launch the formation - execute tasks and record outputs.
393
- 0. Assign an agent to a task - using conditions (manager prioritizes team_tasks) and planning_llm.
394
- 1. Address `before_kickoff_callbacks` if any.
386
+ 0. Assign an agent to a task - using conditions (manager prioritizes team_tasks) and planner_llm.
387
+ 1. Address `pre_launch_callbacks` if any.
395
388
  2. Handle team members' tasks in accordance with the process.
396
- 3. Address `after_kickoff_callbacks` if any.
389
+ 3. Address `post_launch_callbacks` if any.
397
390
  """
398
391
 
399
392
  metrics: List[UsageMetrics] = []
400
393
 
401
394
  if self.team_tasks or self.member_tasks_without_agent:
402
- self._handle_agent_formation()
395
+ self._assign_tasks()
403
396
 
404
- if kwargs_before is not None:
405
- for before_callback in self.before_kickoff_callbacks:
406
- before_callback(**kwargs_before)
397
+ if kwargs_pre is not None:
398
+ for func in self.pre_launch_callbacks:
399
+ func(**kwargs_pre)
407
400
 
408
401
  # self._execution_span = self._telemetry.team_execution_span(self, inputs)
409
402
  # self._task_output_handler.reset()
410
403
  # self._logging_color = "bold_purple"
411
-
412
-
413
404
  # i18n = I18N(prompt_file=self.prompt_file)
414
405
 
415
406
  for member in self.members:
@@ -424,8 +415,8 @@ class Team(BaseModel):
424
415
 
425
416
  result = self._execute_tasks(self.tasks)
426
417
 
427
- for after_callback in self.after_kickoff_callbacks:
428
- result = after_callback(result, **kwargs_after)
418
+ for func in self.post_launch_callbacks:
419
+ result = func(result, **kwargs_post)
429
420
 
430
421
  metrics += [member.agent._token_process.get_summary() for member in self.members]
431
422
 
@@ -443,25 +434,28 @@ class Team(BaseModel):
443
434
 
444
435
 
445
436
  @property
446
- def managers(self) -> List[TeamMember] | None:
437
+ def managers(self) -> List[Member] | None:
447
438
  managers = [member for member in self.members if member.is_manager == True]
448
439
  return managers if len(managers) > 0 else None
449
440
 
450
441
 
451
442
  @property
452
- def manager_tasks(self) -> List[Task] | None:
443
+ def manager_tasks(self) -> List[Task]:
453
444
  """
454
445
  Tasks (incl. team tasks) handled by managers in the team.
455
446
  """
447
+ res = list()
448
+
456
449
  if self.managers:
457
- tasks = [manager.task for manager in self.managers if manager.task is not None]
458
- return tasks if len(tasks) > 0 else None
450
+ for manager in self.managers:
451
+ if manager.tasks:
452
+ res.extend(manager.tasks)
459
453
 
460
- return None
454
+ return res
461
455
 
462
456
 
463
457
  @property
464
- def tasks(self):
458
+ def tasks(self) -> List[Task]:
465
459
  """
466
460
  Return all the tasks that the team needs to handle in order of priority:
467
461
  1. team tasks, -> assigned to the member
@@ -470,15 +464,24 @@ class Team(BaseModel):
470
464
  """
471
465
 
472
466
  team_tasks = self.team_tasks
473
- manager_tasks = [member.task for member in self.members if member.is_manager == True and member.task is not None and member.task not in team_tasks]
474
- member_tasks = [member.task for member in self.members if member.is_manager == False and member.task is not None and member.task not in team_tasks]
467
+ manager_tasks = self.manager_tasks
468
+ member_tasks = []
469
+
470
+ for member in self.members:
471
+ if member.is_manager == False and member.tasks:
472
+ a = [item for item in member.tasks if item not in team_tasks and item not in manager_tasks]
473
+ member_tasks += a
475
474
 
476
475
  return team_tasks + manager_tasks + member_tasks
477
476
 
478
477
 
479
478
  @property
480
- def member_tasks_without_agent(self) -> List[Task] | None:
479
+ def member_tasks_without_agent(self) -> List[Task]:
480
+ res = list()
481
+
481
482
  if self.members:
482
- return [member.task for member in self.members if member.agent is None]
483
+ for member in self.members:
484
+ if member.agent is None and member.tasks:
485
+ res.extend(member.tasks)
483
486
 
484
- return None
487
+ return res
@@ -1,9 +1,8 @@
1
1
  import os
2
- from dotenv import load_dotenv
3
2
  from typing import Any, List, Optional, Dict
4
3
  from pydantic import BaseModel, Field
5
4
 
6
- load_dotenv(override=True)
5
+
7
6
 
8
7
 
9
8
  class TeamPlanner:
@@ -26,14 +25,14 @@ class TeamPlanner:
26
25
 
27
26
  def _handle_assign_agents(self, unassigned_tasks: List[Task]) -> List[Any]:
28
27
  """
29
- Build an agent and assign it a task, then return a list of TeamMember connecting the agent created and the task given.
28
+ Build an agent and assign it a task, then return a list of Member connecting the agent created and the task given.
30
29
  """
31
30
 
32
31
  from versionhq.agent.model import Agent
33
32
  from versionhq.task.model import Task, ResponseField
34
- from versionhq.team.model import TeamMember
33
+ from versionhq.team.model import Member
35
34
 
36
- new_member_list: List[TeamMember] = []
35
+ new_member_list: List[Member] = []
37
36
  agent_creator = Agent(
38
37
  role="agent_creator",
39
38
  goal="build an ai agent that can competitively handle the task given",
@@ -57,7 +56,7 @@ class TeamPlanner:
57
56
  goal=res.json_dict["goal"] if "goal" in res.json_dict else task.description
58
57
  )
59
58
  if agent.id:
60
- team_member = TeamMember(agent=agent, task=unassgined_task, is_manager=False)
59
+ team_member = Member(agent=agent, tasks=[unassgined_task], is_manager=False)
61
60
  new_member_list.append(team_member)
62
61
 
63
62
  return new_member_list
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: versionhq
3
- Version: 1.1.12.5
4
- Summary: An agentic orchestration framework for building agent networks that handle task automation without human interaction.
3
+ Version: 1.1.13.0
4
+ Summary: An agentic orchestration framework for building agent networks that handle task automation.
5
5
  Author-email: Kuriko Iwai <kuriko@versi0n.io>
6
6
  License: MIT License
7
7
 
@@ -40,7 +40,7 @@ Classifier: Development Status :: 3 - Alpha
40
40
  Classifier: Intended Audience :: Developers
41
41
  Classifier: Intended Audience :: Information Technology
42
42
  Classifier: Topic :: Software Development :: Build Tools
43
- Requires-Python: >=3.11
43
+ Requires-Python: <3.13,>=3.11
44
44
  Description-Content-Type: text/markdown
45
45
  License-File: LICENSE
46
46
  Requires-Dist: regex==2024.11.6
@@ -77,7 +77,7 @@ Requires-Dist: numpy>=1.26.4; extra == "numpy"
77
77
 
78
78
  # Overview
79
79
 
80
- ![DL](https://img.shields.io/badge/Download-15K+-red)
80
+ [![DL](https://img.shields.io/badge/Download-15K+-red)](https://clickpy.clickhouse.com/dashboard/versionhq)
81
81
  ![MIT license](https://img.shields.io/badge/License-MIT-green)
82
82
  [![Publisher](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml/badge.svg)](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
83
83
  ![PyPI](https://img.shields.io/badge/PyPI-v1.1.12+-blue)
@@ -85,7 +85,7 @@ Requires-Dist: numpy>=1.26.4; extra == "numpy"
85
85
  ![pyenv ver](https://img.shields.io/badge/pyenv-2.5.0-orange)
86
86
 
87
87
 
88
- Agentic orchestration framework to deploy agent network and handle complex task automation.
88
+ A Python framework for agentic orchestration that handles complex task automation without human interaction.
89
89
 
90
90
  **Visit:**
91
91
 
@@ -104,19 +104,17 @@ Agentic orchestration framework to deploy agent network and handle complex task
104
104
  - [Key Features](#key-features)
105
105
  - [Agent formation](#agent-formation)
106
106
  - [Quick Start](#quick-start)
107
- - [Generate agent networks and launch task execution:](#generate-agent-networks-and-launch-task-execution)
108
- - [Solo Agent:](#solo-agent)
109
- - [Solo Agent:](#solo-agent-1)
110
- - [Supervising:](#supervising)
107
+ - [Package installation](#package-installation)
108
+ - [Forming a agent network](#forming-a-agent-network)
109
+ - [Customizing AI agents](#customizing-ai-agents)
110
+ - [Supervising](#supervising)
111
111
  - [Technologies Used](#technologies-used)
112
112
  - [Project Structure](#project-structure)
113
- - [Setup](#setup)
113
+ - [Setting Up](#setting-up)
114
114
  - [Contributing](#contributing)
115
- - [Documentation](#documentation)
116
- - [Customizing AI Agent](#customizing-ai-agent)
117
- - [Modifying RAG Functionality](#modifying-rag-functionality)
118
115
  - [Package Management with uv](#package-management-with-uv)
119
116
  - [Pre-Commit Hooks](#pre-commit-hooks)
117
+ - [Documentation](#documentation)
120
118
  - [Trouble Shooting](#trouble-shooting)
121
119
  - [Frequently Asked Questions (FAQ)](#frequently-asked-questions-faq)
122
120
 
@@ -148,20 +146,20 @@ You can specify a desired formation or allow the agents to determine it autonomo
148
146
 
149
147
  ## Quick Start
150
148
 
151
- **Install `versionhq` package:**
149
+ ### Package installation
152
150
 
153
151
  ```
154
152
  pip install versionhq
155
153
  ```
156
154
 
157
- (Python 3.11 or higher)
155
+ (Python 3.11 / 3.12)
158
156
 
159
- ### Generate agent networks and launch task execution:
157
+ ### Forming a agent network
160
158
 
161
159
  ```python
162
- from versionhq import form_agent_network
160
+ import versionhq as vhq
163
161
 
164
- network = form_agent_network(
162
+ network = vhq.form_agent_network(
165
163
  task="YOUR AMAZING TASK OVERVIEW",
166
164
  expected_outcome="YOUR OUTCOME EXPECTATION",
167
165
  )
@@ -171,31 +169,28 @@ You can specify a desired formation or allow the agents to determine it autonomo
171
169
  This will form a network with multiple agents on `Formation` and return `TaskOutput` object with output in JSON, plane text, Pydantic model format with evaluation.
172
170
 
173
171
 
174
- ### Solo Agent:
175
-
176
- ### Solo Agent:
172
+ ### Customizing AI agents
177
173
 
178
174
  You can simply build an agent using `Agent` model.
179
175
 
180
- By default, the agent prioritize JSON serializable output.
176
+ By default, the agent prioritize JSON serializable outputs over plane texts.
181
177
 
182
- But you can add a plane text summary of the structured output by using callbacks.
183
178
 
184
179
  ```python
180
+ import versionhq as vhq
185
181
  from pydantic import BaseModel
186
- from versionhq import Agent, Task
187
182
 
188
183
  class CustomOutput(BaseModel):
189
184
  test1: str
190
185
  test2: list[str]
191
186
 
192
187
  def dummy_func(message: str, test1: str, test2: list[str]) -> str:
193
- return f"{message}: {test1}, {", ".join(test2)}"
188
+ return f"""{message}: {test1}, {", ".join(test2)}"""
194
189
 
195
190
 
196
- agent = Agent(role="demo", goal="amazing project goal")
191
+ agent = vhq.Agent(role="demo", goal="amazing project goal")
197
192
 
198
- task = Task(
193
+ task = vhq.Task(
199
194
  description="Amazing task",
200
195
  pydantic_output=CustomOutput,
201
196
  callback=dummy_func,
@@ -221,33 +216,33 @@ This will return a `TaskOutput` object that stores response in plane text, JSON,
221
216
  )
222
217
  ```
223
218
 
224
- ### Supervising:
219
+ ### Supervising
225
220
 
226
221
  ```python
227
- from versionhq import Agent, Task, ResponseField, Team, TeamMember
222
+ import versionhq as vhq
228
223
 
229
- agent_a = Agent(role="agent a", goal="My amazing goals", llm="llm-of-your-choice")
230
- agent_b = Agent(role="agent b", goal="My amazing goals", llm="llm-of-your-choice")
224
+ agent_a = vhq.Agent(role="agent a", goal="My amazing goals", llm="llm-of-your-choice")
225
+ agent_b = vhq.Agent(role="agent b", goal="My amazing goals", llm="llm-of-your-choice")
231
226
 
232
- task_1 = Task(
227
+ task_1 = vhq.Task(
233
228
  description="Analyze the client's business model.",
234
- response_fields=[ResponseField(title="test1", data_type=str, required=True),],
229
+ response_fields=[vhq.ResponseField(title="test1", data_type=str, required=True),],
235
230
  allow_delegation=True
236
231
  )
237
232
 
238
- task_2 = Task(
233
+ task_2 = vhq.Task(
239
234
  description="Define the cohort.",
240
235
  response_fields=[ResponseField(title="test1", data_type=int, required=True),],
241
236
  allow_delegation=False
242
237
  )
243
238
 
244
- team = Team(
239
+ team = vhq.Team(
245
240
  members=[
246
- TeamMember(agent=agent_a, is_manager=False, task=task_1),
247
- TeamMember(agent=agent_b, is_manager=True, task=task_2),
241
+ vhq.Member(agent=agent_a, is_manager=False, task=task_1),
242
+ vhq.Member(agent=agent_b, is_manager=True, task=task_2),
248
243
  ],
249
244
  )
250
- res = team.kickoff()
245
+ res = team.launch()
251
246
  ```
252
247
 
253
248
  This will return a list with dictionaries with keys defined in the `ResponseField` of each task.
@@ -257,27 +252,33 @@ Tasks can be delegated to a team manager, peers in the team, or completely new a
257
252
  <hr />
258
253
 
259
254
  ## Technologies Used
255
+
260
256
  **Schema, Data Validation**
261
- - [Pydantic](https://docs.pydantic.dev/latest/): Data validation and serialization library for Python.
262
- - [Upstage](https://console.upstage.ai/docs/getting-started/overview): Document processer for ML tasks. (Use `Document Parser API` to extract data from documents)
263
- - [Docling](https://ds4sd.github.io/docling/): Document parsing
257
+
258
+ * [Pydantic](https://docs.pydantic.dev/latest/): Data validation and serialization library for Python.
259
+ * [Upstage](https://console.upstage.ai/docs/getting-started/overview): Document processer for ML tasks. (Use `Document Parser API` to extract data from documents)
260
+ * [Docling](https://ds4sd.github.io/docling/): Document parsing
264
261
 
265
262
  **Storage**
266
- - [mem0ai](https://docs.mem0.ai/quickstart#install-package): Agents' memory storage and management.
267
- - [Chroma DB](https://docs.trychroma.com/): Vector database for storing and querying usage data.
268
- - [SQLite](https://www.sqlite.org/docs.html): C-language library to implements a small SQL database engine.
263
+
264
+ * [mem0ai](https://docs.mem0.ai/quickstart#install-package): Agents' memory storage and management.
265
+ * [Chroma DB](https://docs.trychroma.com/): Vector database for storing and querying usage data.
266
+ * [SQLite](https://www.sqlite.org/docs.html): C-language library to implements a small SQL database engine.
269
267
 
270
268
  **LLM-curation**
271
- - [LiteLLM](https://docs.litellm.ai/docs/providers): Curation platform to access LLMs
269
+
270
+ * [LiteLLM](https://docs.litellm.ai/docs/providers): Curation platform to access LLMs
272
271
 
273
272
  **Tools**
274
- - [Composio](https://composio.dev/): Conect RAG agents with external tools, Apps, and APIs to perform actions and receive triggers. We use [tools](https://composio.dev/tools) and [RAG tools](https://app.composio.dev/app/ragtool) from Composio toolset.
273
+
274
+ * [Composio](https://composio.dev/): Conect RAG agents with external tools, Apps, and APIs to perform actions and receive triggers. We use [tools](https://composio.dev/tools) and [RAG tools](https://app.composio.dev/app/ragtool) from Composio toolset.
275
275
 
276
276
  **Deployment**
277
- - Python: Primary programming language. v3.13 is recommended.
278
- - [uv](https://docs.astral.sh/uv/): Python package installer and resolver
279
- - [pre-commit](https://pre-commit.com/): Manage and maintain pre-commit hooks
280
- - [setuptools](https://pypi.org/project/setuptools/): Build python modules
277
+
278
+ * **Python**: Primary programming language. v3.12.x is recommended
279
+ * [uv](https://docs.astral.sh/uv/): Python package installer and resolver
280
+ * [pre-commit](https://pre-commit.com/): Manage and maintain pre-commit hooks
281
+ * [setuptools](https://pypi.org/project/setuptools/): Build python modules
281
282
 
282
283
  <hr />
283
284
 
@@ -309,7 +310,7 @@ src/
309
310
 
310
311
  <hr />
311
312
 
312
- ## Setup
313
+ ## Setting Up
313
314
 
314
315
  1. Install `uv` package manager:
315
316
 
@@ -339,12 +340,12 @@ src/
339
340
  pyenv install 3.12.8
340
341
  pyenv global 3.12.8 (optional: `pyenv global system` to get back to the system default ver.)
341
342
  uv python pin 3.12.8
342
- echo 3.12.8 > .python-version
343
+ echo 3.12.8 >> .python-version
343
344
  ```
344
345
 
345
346
 
346
- 3. Set up environment variables:
347
- Create a `.env` file in the project root and add the following:
347
+ 3. Add secrets to `.env` file in the project root:
348
+
348
349
  ```
349
350
  LITELLM_API_KEY=your-litellm-api-key
350
351
  OPENAI_API_KEY=your-openai-api-key
@@ -361,68 +362,37 @@ src/
361
362
 
362
363
  2. Create amazing features
363
364
 
364
- 3. Test the features using the `tests` directory.
365
+ 3. Add a test funcition to the `tests` directory and run **pytest**.
365
366
 
366
- - Add a test function to respective components in the `tests` directory.
367
- - Add your `LITELLM_API_KEY`, `OPENAI_API_KEY`, `COMPOSIO_API_KEY`, `DEFAULT_USER_ID` to the Github `repository secrets` located at settings > secrets & variables > Actions.
368
- - Run a test.
367
+ - Add secret values defined in `.github/workflows/run_test.yml` to your Github `repository secrets` located at settings > secrets & variables > Actions.
368
+ - Run a following command:
369
369
  ```
370
370
  uv run pytest tests -vv --cache-clear
371
371
  ```
372
372
 
373
- **pytest**
374
-
375
- * When adding a new file to `tests`, name the file ended with `_test.py`.
376
- * When adding a new feature to the file, name the feature started with `test_`.
377
-
378
- 4. Pull the latest version of source code from the main branch (`git pull origin main`) *Address conflicts if any.
379
- 5. Commit your changes (`git add .` / `git commit -m 'Add your-amazing-feature'`)
380
- 6. Push to the branch (`git push origin feature/your-amazing-feature`)
381
- 7. Open a pull request
382
-
383
-
384
- **Optional**
385
- * Flag with `#! REFINEME` for any improvements needed and `#! FIXME` for any errors.
386
-
387
- <!-- * Run a React demo app: [React demo app](https://github.com/versionHQ/test-client-app) to check it on the client endpoint.
388
- ```
389
- npm i
390
- npm start
391
- ```
392
- The frontend will be available at `http://localhost:3000`. -->
393
-
394
- * `production` use case is available at `https://versi0n.io`. Currently, we are running alpha test.
395
-
396
-
397
- ### Documentation
398
-
399
- * To edit the documentation, see `docs` repository and edit the respective component.
373
+ **Building a new pytest function**
400
374
 
401
- * We use `mkdocs` to update the docs. You can run the doc locally at http://127.0.0.1:8000/:
375
+ * Files added to the `tests` directory must end in `_test.py`.
402
376
 
403
- ```
404
- uv run python3 -m mkdocs serve --clean
405
- ```
377
+ * Test functions within the files must begin with `test_`.
406
378
 
407
- * To add a new page, update `mkdocs.yml` in the root. Refer to [MkDocs official docs](https://squidfunk.github.io/mkdocs-material/getting-started/) for more details.
408
379
 
380
+ 4. Update `docs` accordingly.
409
381
 
410
- ### Customizing AI Agent
382
+ 5. Pull the latest version of source code from the main branch (`git pull origin main`) *Address conflicts if any.
411
383
 
412
- To add an agent, use `sample` directory to add new `project`. You can define an agent with a specific role, goal, and set of tools.
384
+ 6. Commit your changes (`git add .` / `git commit -m 'Add your-amazing-feature'`)
413
385
 
414
- Your new agent needs to follow the `Agent` model defined in the `verionhq.agent.model.py`.
386
+ 7. Push to the branch (`git push origin feature/your-amazing-feature`)
415
387
 
416
- You can also add any fields and functions to the `Agent` model **universally** by modifying `verionhq.agent.model.py`.
388
+ 8. Open a pull request
417
389
 
418
390
 
419
- ### Modifying RAG Functionality
391
+ **Optional**
420
392
 
421
- The RAG system uses Chroma DB to store and query past campaign dataset. To update the knowledge base:
393
+ * Flag with `#! REFINEME` for any improvements needed and `#! FIXME` for any errors.
422
394
 
423
- 1. Add new files to the `uploads/` directory. (This will not be pushed to Github.)
424
- 2. Modify the `tools.py` file to update the ingestion process if necessary.
425
- 3. Run the ingestion process to update the Chroma DB.
395
+ * `Playground` is available at `https://versi0n.io`.
426
396
 
427
397
 
428
398
  ### Package Management with uv
@@ -453,19 +423,39 @@ Pre-commit hooks help maintain code quality by running checks for formatting, li
453
423
  git commit --no-verify -m "your-commit-message"
454
424
  ```
455
425
 
426
+ ### Documentation
427
+
428
+ * To edit the documentation, see `docs` repository and edit the respective component.
429
+
430
+ * We use `mkdocs` to update the docs. You can run the doc locally at http://127.0.0.1:8000/:
431
+
432
+ ```
433
+ uv run python3 -m mkdocs serve --clean
434
+ ```
435
+
436
+ * To add a new page, update `mkdocs.yml` in the root. Refer to [MkDocs documentation](https://squidfunk.github.io/mkdocs-material/getting-started/) for more details.
437
+
456
438
  <hr />
457
439
 
458
440
  ## Trouble Shooting
459
441
 
460
442
  Common issues and solutions:
461
- - API key errors: Ensure all API keys in the `.env` file are correct and up to date. Make sure to add `load_dotenv()` on the top of the python file to apply the latest environment values.
462
- - Database connection issues: Check if the Chroma DB is properly initialized and accessible.
463
- - Memory errors: If processing large contracts, you may need to increase the available memory for the Python process.
464
- - Issues related to the Python version: Docling/Pytorch is not ready for Python 3.13 as of Jan 2025. Use Python 3.12.x as default by running `uv venv --python 3.12.8` and `uv python pin 3.12.8`.
465
- - Issues related to dependencies: `rm -rf uv.lock`, `uv cache clean`, `uv venv`, and run `uv pip install -r requirements.txt -v`.
466
- - Issues related to the AI agents or RAG system: Check the `output.log` file for detailed error messages and stack traces.
467
- - Issues related to `Python quit unexpectedly`: Check [this stackoverflow article](https://stackoverflow.com/questions/59888499/macos-catalina-python-quit-unexpectedly-error).
468
- - `reportMissingImports` error from pyright after installing the package: This might occur when installing new libraries while VSCode is running. Open the command pallete (ctrl + shift + p) and run the Python: Restart language server task.
443
+
444
+ * API key errors: Ensure all API keys in the `.env` file are correct and up to date. Make sure to add `load_dotenv()` on the top of the python file to apply the latest environment values.
445
+
446
+ * Database connection issues: Check if the Chroma DB is properly initialized and accessible.
447
+
448
+ * Memory errors: If processing large contracts, you may need to increase the available memory for the Python process.
449
+
450
+ * Issues related to the Python version: Docling/Pytorch is not ready for Python 3.13 as of Jan 2025. Use Python 3.12.x as default by running `uv venv --python 3.12.8` and `uv python pin 3.12.8`.
451
+
452
+ * Issues related to dependencies: `rm -rf uv.lock`, `uv cache clean`, `uv venv`, and run `uv pip install -r requirements.txt -v`.
453
+
454
+ * Issues related to the AI agents or RAG system: Check the `output.log` file for detailed error messages and stack traces.
455
+
456
+ * Issues related to `Python quit unexpectedly`: Check [this stackoverflow article](https://stackoverflow.com/questions/59888499/macos-catalina-python-quit-unexpectedly-error).
457
+
458
+ * `reportMissingImports` error from pyright after installing the package: This might occur when installing new libraries while VSCode is running. Open the command pallete (ctrl + shift + p) and run the Python: Restart language server task.
469
459
 
470
460
  <hr />
471
461
 
@@ -477,5 +467,5 @@ A. Visit [playground](https://versi0n.io).
477
467
 
478
468
  **Q. How do you analyze the customer?**
479
469
 
480
- > A. We employ soft clustering for each customer.
481
- > <img width="200" src="https://res.cloudinary.com/dfeirxlea/image/upload/v1732732628/pj_m_agents/ito937s5d5x0so8isvw6.png">
470
+ A. We employ soft clustering for each customer.
471
+ <img width="200" src="https://res.cloudinary.com/dfeirxlea/image/upload/v1732732628/pj_m_agents/ito937s5d5x0so8isvw6.png">
@@ -1,12 +1,12 @@
1
- versionhq/__init__.py,sha256=X5s6M9BrlutQtocMHgdTXkmStyazfeE5WYjjUfqcVcg,2349
1
+ versionhq/__init__.py,sha256=lfts4IiFxMVN-gRb7eoit56jqfMgOSMG1C8803J_-UQ,2400
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=j9SlQPIefdVUlwpGfJY83E2BUt1ejWgZ2M2I8aMyQ3c,1579
5
5
  versionhq/_utils/process_config.py,sha256=jbPGXK2Kb4iyCugJ3FwRJuU0wL5Trq2x4xFQz2uOyFY,746
6
- versionhq/_utils/usage_metrics.py,sha256=hhq1OCW8Z4V93vwW2O2j528EyjOlF8wlTsX5IL-7asA,1106
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=v8frZjmiqYR8zuuh4CjYJheaHfHT2n_utT8pWCLJFes,2375
9
+ versionhq/agent/inhouse_agents.py,sha256=vSobrH1gXDWlaNsiges3sqETeUrEssRzQvCZCY2hQZA,2374
10
10
  versionhq/agent/model.py,sha256=8aJ4rdgGEph10DuB8zhJkiRWzQTZ2LGKNq1MTeQ9hM8,23342
11
11
  versionhq/agent/parser.py,sha256=riG0dkdQCxH7uJ0AbdVdg7WvL0BXhUgJht0VtQvxJBc,4082
12
12
  versionhq/agent/rpm_controller.py,sha256=grezIxyBci_lDlwAlgWFRyR5KOocXeOhYkgN02dNFNE,2360
@@ -28,7 +28,7 @@ versionhq/knowledge/source.py,sha256=30VXsl3uHdM0wK0Dik3XfFxpNpEiy539PBNBvg0Y4-g
28
28
  versionhq/knowledge/source_docling.py,sha256=hhHn3rS4KVsFKEPWcfllM8VxSL86PckZdAHDZNQNOq8,5411
29
29
  versionhq/knowledge/storage.py,sha256=7oxCg3W9mFjYH1YmuH9kFtTbNxquzYFjuUjd_TlsB9E,8170
30
30
  versionhq/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- versionhq/llm/llm_vars.py,sha256=p4MbhH0eaQ1qWkHfA3QBP1KteJFkatEecvSCwqJ4m-M,6773
31
+ versionhq/llm/llm_vars.py,sha256=3fax7EXNwCw1yapIoqRMmwgGmK3O37Wm1e8uvq8ObL4,7063
32
32
  versionhq/llm/model.py,sha256=QacjThF43Vfel6LIvSt5KkOZAbzo1jYjwFgFfhrv7ms,17174
33
33
  versionhq/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  versionhq/memory/contextual_memory.py,sha256=tCsOOAUnfrOL7YiakqGoi3uShzzS870TmGnlGd3z_A4,3556
@@ -41,16 +41,16 @@ versionhq/storage/rag_storage.py,sha256=ScWC0vH327vnGw8UGscAOoIfqrq3mhvXT3vEKzHZ
41
41
  versionhq/storage/task_output_storage.py,sha256=E1t_Fkt78dPYIOl3MP7LfQ8oGtjlzxBuSNq_8ZXKho8,4573
42
42
  versionhq/storage/utils.py,sha256=ByYXPoEIGJYLUqz-DWjbCAnneNrH1otiYbp12SCILpM,747
43
43
  versionhq/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- versionhq/task/evaluate.py,sha256=f8S-nuEl2xJ2LnLv7iQixH53-gp0pKx1hFp_sUlN464,3977
45
- versionhq/task/formation.py,sha256=bZytru6I5a_CFq2rtmsBb0hVWIqExpzUxXPXYpd6qnI,4706
44
+ versionhq/task/evaluate.py,sha256=fBLL6sm763e3Ev6uU0tMqFRUVwAcBj6dLM5N062qHuc,3952
45
+ versionhq/task/formation.py,sha256=QXFZfY604cpS79X7HEBmwFKn91H8jS3Ak6EhJGgaRIg,6355
46
46
  versionhq/task/formatter.py,sha256=N8Kmk9vtrMtBdgJ8J7RmlKNMdZWSmV8O1bDexmCWgU0,643
47
47
  versionhq/task/log_handler.py,sha256=LT7YnO7gcPR9IZS7eRvMjnHh8crMBFtqduxd8dxIbkk,1680
48
- versionhq/task/model.py,sha256=kTYWv50iSOQdANsJbP4ehlSmixhp2d15MfKX2aZX0nk,29459
48
+ versionhq/task/model.py,sha256=qbH_fMnkDqZUp-Sgd7LB7LMsXSaytMFMVffS5q3En1c,29160
49
49
  versionhq/task/structured_response.py,sha256=uVqgeUxNOACPe2hdc0RELSbtKd1vrwonfjXMOGTT0TI,4818
50
50
  versionhq/task/TEMPLATES/Description.py,sha256=V-4kh8xpQTKOcDMi2xnuP-fcNk6kuoz1_5tYBlDLQWQ,420
51
51
  versionhq/team/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- versionhq/team/model.py,sha256=WDnaJuvnVMmreztR0CMypnKpqFtGzAb7rrZ-HKa010Y,18886
53
- versionhq/team/team_planner.py,sha256=l1PwyBXK1F3uOcbF1IvJBWKApJhghZnBF_ErkNcE04s,3745
52
+ versionhq/team/model.py,sha256=qPsV3O3O2TIOT-3h142KVVFdO587LhoOeVkK2niZHEc,19062
53
+ versionhq/team/team_planner.py,sha256=x1eLkdfQwW6w3Kyi_wVaVlA41TaNJDIYWhtEqp_OcaI,3675
54
54
  versionhq/tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  versionhq/tool/cache_handler.py,sha256=iL8FH7X0G-cdT0uhJwzuhLDaadTXOdfybZcDy151-es,1085
56
56
  versionhq/tool/composio_tool.py,sha256=38mEiVvTkuw1BLD233Bl1Gwxbpss1yfQiZLTWwX6BdA,8648
@@ -58,8 +58,8 @@ versionhq/tool/composio_tool_vars.py,sha256=FvBuEXsOQUYnN7RTFxT20kAkiEYkxWKkiVtg
58
58
  versionhq/tool/decorator.py,sha256=C4ZM7Xi2gwtEMaSeRo-geo_g_MAkY77WkSLkAuY0AyI,1205
59
59
  versionhq/tool/model.py,sha256=PO4zNWBZcJhYVur381YL1dy6zqurio2jWjtbxOxZMGI,12194
60
60
  versionhq/tool/tool_handler.py,sha256=2m41K8qo5bGCCbwMFferEjT-XZ-mE9F0mDUOBkgivOI,1416
61
- versionhq-1.1.12.5.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
62
- versionhq-1.1.12.5.dist-info/METADATA,sha256=OpjOZZN6bHmf-KKiR9gqTJokG-Nd805C-wCbgvUSlhU,18676
63
- versionhq-1.1.12.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
64
- versionhq-1.1.12.5.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
65
- versionhq-1.1.12.5.dist-info/RECORD,,
61
+ versionhq-1.1.13.0.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
62
+ versionhq-1.1.13.0.dist-info/METADATA,sha256=zNnwZLvuWsZPjXoCrsa7PqD8iR1sJ2zUjjj8wV0QkfA,17365
63
+ versionhq-1.1.13.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
64
+ versionhq-1.1.13.0.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
65
+ versionhq-1.1.13.0.dist-info/RECORD,,