versionhq 1.1.13.1__py3-none-any.whl → 1.2.0.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/__init__.py CHANGED
@@ -17,7 +17,7 @@ from versionhq.knowledge.model import Knowledge, KnowledgeStorage
17
17
  from versionhq.knowledge.source import PDFKnowledgeSource, CSVKnowledgeSource, JSONKnowledgeSource, TextFileKnowledgeSource, ExcelKnowledgeSource, StringKnowledgeSource
18
18
  from versionhq.knowledge.source_docling import DoclingSource
19
19
  from versionhq.network.model import TaskStatus, TaskGraph, Node, Edge, DependencyType
20
- from versionhq.task.model import Task, TaskOutput, ConditionalTask, ResponseField
20
+ from versionhq.task.model import Task, TaskOutput, ResponseField, TaskExecutionType
21
21
  from versionhq.task.evaluate import Evaluation, EvaluationItem
22
22
  from versionhq.team.model import Team, TeamOutput, Formation, Member, TaskHandlingProcess
23
23
  from versionhq.tool.model import Tool, ToolSet
@@ -30,7 +30,7 @@ from versionhq.memory.model import ShortTermMemory,LongTermMemory, UserMemory, M
30
30
  from versionhq.task.formation import form_agent_network
31
31
 
32
32
 
33
- __version__ = "1.1.13.1"
33
+ __version__ = "1.2.0.2"
34
34
  __all__ = [
35
35
  "Agent",
36
36
 
@@ -64,8 +64,8 @@ __all__ = [
64
64
 
65
65
  "Task",
66
66
  "TaskOutput",
67
- "ConditionalTask",
68
67
  "ResponseField",
68
+ "TaskExecutionType",
69
69
 
70
70
  "Evaluation",
71
71
  "EvaluationItem",
versionhq/agent/model.py CHANGED
@@ -584,4 +584,4 @@ class Agent(BaseModel):
584
584
 
585
585
 
586
586
  def __repr__(self):
587
- return f"Agent(role={self.role}, goal={self.goal}, backstory={self.backstory})"
587
+ return f"Agent(role={self.role}, goal={self.goal}"
@@ -1,23 +1,22 @@
1
1
  import enum
2
2
  import uuid
3
3
  from abc import ABC
4
- from typing import List, Any, Optional, Callable, Dict, Type
4
+ from typing import List, Any, Optional, Callable, Dict, Type, Tuple
5
5
 
6
- from pydantic import BaseModel, InstanceOf, Field, UUID4, PrivateAttr, field_validator
6
+ from pydantic import BaseModel, InstanceOf, Field, UUID4, PrivateAttr, field_validator, model_validator
7
7
  from pydantic_core import PydanticCustomError
8
8
 
9
- from versionhq.task.model import Task
9
+ from versionhq.task.model import Task, TaskOutput
10
10
  from versionhq.agent.model import Agent
11
11
  from versionhq._utils.logger import Logger
12
12
 
13
-
14
13
  try:
15
- import networkx as ntx
14
+ import networkx as nx
16
15
  except ImportError:
17
16
  try:
18
17
  import os
19
18
  os.system("uv add networkx --optional networkx")
20
- import networkx as ntx
19
+ import networkx as nx
21
20
  except:
22
21
  raise ImportError("networkx is not installed. Please install it with: uv add networkx --optional networkx")
23
22
 
@@ -43,18 +42,17 @@ class TaskStatus(enum.Enum):
43
42
  """
44
43
  NOT_STARTED = 1
45
44
  IN_PROGRESS = 2
46
- BLOCKED = 3 # task is waiting for its dependant tasks to complete. resumption set as AUTO.
45
+ WAITING = 3 # waiting for its dependant tasks to complete. resumption set as AUTO.
47
46
  COMPLETED = 4
48
- DELAYED = 5 # task has begun - but is taking longer than expected duration and behind schedule.
49
- ON_HOLD = 6 # task is temporarily & intentionally paused due to external factors and/or decisions. resumption set as DECISION.
50
-
47
+ DELAYED = 5 # task in progress - but taking longer than expected duration
48
+ ON_HOLD = 6 # intentionally paused due to external factors and decisions. resumption set as DECISION.
49
+ ERROR = 7 # tried task execute but returned error. resupmtion follows edge weights and agent settings
51
50
 
52
51
 
53
52
  class DependencyType(enum.Enum):
54
53
  """
55
54
  Concise enumeration of the edge type.
56
55
  """
57
-
58
56
  FINISH_TO_START = "FS" # Task B starts after Task A finishes
59
57
  START_TO_START = "SS" # Task B starts when Task A starts
60
58
  FINISH_TO_FINISH = "FF" # Task B finishes when Task A finishes
@@ -86,7 +84,7 @@ class Node(BaseModel):
86
84
  """
87
85
  id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
88
86
  task: InstanceOf[Task] = Field(default=None)
89
- trigger_event: TriggerEvent = Field(default=TriggerEvent.IMMEDIATE, description="store trigger event to execute the task")
87
+ # trigger_event: TriggerEvent = Field(default=TriggerEvent.IMMEDIATE, description="store trigger event for starting the task execution")
90
88
  in_degree_nodes: List[Any] = Field(default=None, description="list of Node objects")
91
89
  out_degree_nodes: List[Any] = Field(default=None, description="list of Node objects")
92
90
  assigned_to: InstanceOf[Agent] = Field(default=None)
@@ -119,49 +117,110 @@ class Node(BaseModel):
119
117
  """Unique identifier for the node"""
120
118
  return f"{str(self.id)}"
121
119
 
120
+ def handle_task_execution(self, agent: Agent = None, context: str = None) -> TaskOutput | None:
121
+ """
122
+ Start task execution and update status accordingly.
123
+ """
124
+
125
+ self.status = TaskStatus.IN_PROGRESS
126
+
127
+ if not self.task:
128
+ Logger(verbose=True).log(level="error", message="Missing a task to execute. We'll return None.", color="red")
129
+ self.status = TaskStatus.ERROR
130
+ return None
131
+
132
+ res = self.task.execute(agent=agent, context=context)
133
+ self.status = TaskStatus.COMPLETED if res else TaskStatus.ERROR
134
+ return res
135
+
122
136
  def __str__(self):
123
137
  return self.identifier
124
138
 
125
139
 
140
+
126
141
  class Edge(BaseModel):
127
142
  """
128
- A class to store an edge object that connects multiple nodes as dependencies.
143
+ A class to store an edge object that connects source and target nodes.
129
144
  """
145
+ source: Node = Field(default=None)
146
+ target: Node = Field(default=None)
147
+
130
148
  description: Optional[str] = Field(default=None)
149
+ weight: Optional[float | int] = Field(default=1, description="est. duration for the task execution or respective weight of the target node (1 low - 10 high priority)")
131
150
 
132
- type: DependencyType = Field(default=DependencyType.FINISH_TO_START)
133
- weight: Optional[float] = Field(default=None, description="duration or weight of the dependency: 1 light 10 heavy")
134
- lag: Optional[float] = Field(default=None, description="lag time for the dependency to be executed")
135
- constraint: Optional[str] = Field(default=None, description="constraint to consider executing the dependency")
136
- priority: Optional[int] = Field(default=None, description="priority of the dependency if multiple depencencies are given")
151
+ dependency_type: DependencyType = Field(default=DependencyType.FINISH_TO_START)
152
+ required: bool = Field(default=True, description="whether to consider the source's status")
153
+ condition: Optional[Callable] = Field(default=None, description="conditional function to start executing the dependency")
154
+ condition_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict)
137
155
 
138
- data_transfer: bool = Field(True, description="whether the data transfer is required")
139
- data_format: Optional[str] = Field(default=None, description="Format of data transfer")
156
+ lag: Optional[float | int] = Field(default=None, description="lag time (sec) from the dependency met to the task execution")
157
+ data_transfer: bool = Field(default=True, description="whether the data transfer is required. by default transfer plane text output from in-degree nodes as context")
140
158
 
141
- # execution logic
142
- required: bool = Field(True, description="whether the execution of the dependency is required - False = conditional, optional task")
143
- condition: Optional[Callable] = Field(default=None, description="conditional function to start executing the dependency")
144
159
 
160
+ def dependency_met(self) -> bool:
161
+ """
162
+ Defines if the dependency is ready to execute:
145
163
 
146
- def is_dependency_met(self, predecessor_node: Node = None) -> bool:
164
+ required - condition - Dependency Met? - Dependent Task Can Start?
165
+ True Not Given Predecessor task finished Yes
166
+ True Given Predecessor task finished and condition True Yes
167
+ False Not Given Always (regardless of predecessor status) Yes
168
+ False Given Condition True (predecessor status irrelevant) Yes
147
169
  """
148
- Define if the dependency is ready to execute:
149
170
 
150
- required condition Dependency Met? Dependent Task Can Start?
151
- True Not Given Predecessor task finished Yes (if other deps met)
152
- True Given Predecessor task finished and condition True Yes (if other deps met)
153
- False Not Given Always (regardless of predecessor status) Yes (if other deps met)
154
- False Given Condition True (predecessor status irrelevant) Yes (if other deps met)
171
+ if not self.required:
172
+ return self.condition(**self.condition_kwargs) if self.condition else True
173
+
174
+ match self.dependency_type:
175
+ case DependencyType.FINISH_TO_START:
176
+ """target starts after source finishes"""
177
+ if self.source.status == TaskStatus.COMPLETED:
178
+ return self.condition(**self.conditon_kwargs) if self.condtion else True
179
+ else:
180
+ return False
181
+
182
+ case DependencyType.START_TO_START:
183
+ """target starts when source starts"""
184
+ if self.source.status != TaskStatus.NOT_STARTED:
185
+ return self.condition(**self.conditon_kwargs) if self.condtion else True
186
+ else:
187
+ return False
188
+
189
+ case DependencyType.FINISH_TO_FINISH:
190
+ """target finish when source start"""
191
+ if self.source.status != TaskStatus.COMPLETED:
192
+ return self.condition(**self.conditon_kwargs) if self.condtion else True
193
+ else:
194
+ return False
195
+
196
+ case DependencyType.START_TO_FINISH:
197
+ """target finishes when source start"""
198
+ if self.source.status == TaskStatus.IN_PROGRESS:
199
+ return self.condition(**self.conditon_kwargs) if self.condtion else True
200
+ else:
201
+ return False
202
+
203
+
204
+ def activate(self) -> TaskOutput | None:
205
+ """
206
+ Activates the edge to initiate task execution of the target node.
155
207
  """
156
208
 
157
- if self.required:
158
- if predecessor_node.status == TaskStatus.COMPLETED:
159
- return self.condition() if self.condtion else True
160
- else:
161
- return False
162
- else:
163
- return self.condition() if self.condition else True
209
+ if not self.dependency_met():
210
+ Logger(verbose=True).log(level="warning", message="Dependencies not met. We'll return None.", color="yellow")
211
+ return None
212
+
213
+ if not self.source or not self.target:
214
+ Logger(verbose=True).log(level="warning", message="Cannot find source or target nodes. We'll return None.", color="yellow")
215
+ return None
216
+
217
+ if self.lag:
218
+ import time
219
+ time.sleep(self.lag)
164
220
 
221
+ context = self.source.task.task_output.raw if self.data_transfer else None
222
+ res = self.target.handle_task_execution(context=context)
223
+ return res
165
224
 
166
225
 
167
226
  class Graph(ABC, BaseModel):
@@ -176,16 +235,20 @@ class Graph(ABC, BaseModel):
176
235
  edges: Dict[str, InstanceOf[Edge]] = Field(default_factory=dict)
177
236
 
178
237
  def __init__(self, directed: bool = False, **kwargs):
179
-
180
238
  super().__init__(directed=directed, **kwargs)
181
239
  self.graph = nx.DiGraph() if self.directed else nx.Graph()
182
240
 
241
+ def _return_node_object(self, node_identifier) -> Node | None:
242
+ return [v for k, v in self.nodes.items() if k == node_identifier][0] if [v for k, v in self.nodes.items() if k == node_identifier] else None
243
+
183
244
  def add_node(self, node: Node) -> None:
184
245
  self.graph.add_node(node.identifier, **node.model_dump())
185
246
  self.nodes[node.identifier] = node
186
247
 
187
248
  def add_edge(self, source: str, target: str, edge: Edge) -> None:
188
249
  self.graph.add_edge(source, target, **edge.model_dump())
250
+ edge.source = self._return_node_object(source)
251
+ edge.target = self._return_node_object(target)
189
252
  self.edges[(source, target)] = edge
190
253
 
191
254
  def add_weighted_edges_from(self, edges):
@@ -200,7 +263,7 @@ class Graph(ABC, BaseModel):
200
263
  def get_out_degree(self, node: Node) -> int:
201
264
  return self.graph.out_degree(node)
202
265
 
203
- def find_path(self, source: str, target: str, weight: Any) -> Any:
266
+ def find_path(self, source: Optional[str] | None, target: str, weight: Optional[Any] | None) -> Any:
204
267
  try:
205
268
  return nx.shortest_path(self.graph, source=source, target=target, weight=weight)
206
269
  except nx.NetworkXNoPath:
@@ -209,7 +272,6 @@ class Graph(ABC, BaseModel):
209
272
  def find_all_paths(self, source: str, target: str) -> List[Any]:
210
273
  return list(nx.all_simple_paths(self.graph, source=source, target=target))
211
274
 
212
-
213
275
  def find_critical_path(self) -> tuple[List[Any], int, Dict[str, int]]:
214
276
  """
215
277
  Finds the critical path in the graph.
@@ -245,7 +307,7 @@ class Graph(ABC, BaseModel):
245
307
  return False
246
308
 
247
309
 
248
- def visualize(self, title: str = "Graph Visualization", pos: Any = None, **graph_config):
310
+ def visualize(self, title: str = "Task Graph", pos: Any = None, **graph_config):
249
311
  pos = pos if pos else nx.spring_layout(self.graph, seed=42)
250
312
  nx.draw(
251
313
  self.graph,
@@ -270,12 +332,11 @@ class Graph(ABC, BaseModel):
270
332
  plt.show()
271
333
 
272
334
 
273
-
274
335
  class TaskGraph(Graph):
275
336
  id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
276
337
  should_reform: bool = Field(default=False)
277
338
  status: Dict[str, TaskStatus] = Field(default_factory=dict, description="store identifier (str) and TaskStatus of all task_nodes")
278
-
339
+ outputs: Dict[str, TaskOutput] = Field(default_factory=dict, description="store identifire and TaskOutput")
279
340
 
280
341
  def add_task(self, task: Node | Task) -> Node:
281
342
  """Convert `task` to a Node object and add it to G"""
@@ -352,7 +413,7 @@ class TaskGraph(Graph):
352
413
  for k, v in self.edges.items():
353
414
  # edge_weights = nx.get_edge_attributes(self.graph, 'weight')
354
415
  # edge_colors.append(plt.cm.viridis(v.weight / max(edge_weights.values())))
355
- edge_widths.append(v.weight * 0.5) # Width proportional to weight (adjust scaling as needed)
416
+ edge_widths.append(v.weight * 0.5)
356
417
 
357
418
  nx.draw(
358
419
  self.graph, pos,
@@ -392,3 +453,48 @@ class TaskGraph(Graph):
392
453
 
393
454
  except Exception as e:
394
455
  self._logger.log(level="error", message=f"Failed to save the graph {str(self.id)}: {str(e)}", color="red")
456
+
457
+
458
+ def activate(self, target_node_identifier: Optional[str] = None) -> Tuple[TaskOutput | None, Dict[str, TaskOutput]]:
459
+ """
460
+ Starts to execute all nodes in the graph or a specific node if the target is given, following the given conditons of the edge obeject.
461
+ Then returns tuple of the last task output and all task outputs (self.outputs)
462
+ """
463
+ if target_node_identifier:
464
+ if not [k for k in self.nodes.keys() if k == target_node_identifier]:
465
+ self._logger.log(level="error", message=f"The node {str(target_node_identifier)} is not in the graph.", color="red")
466
+ return None
467
+
468
+ # find a shortest path to each in-degree node of the node and see if dependency met.
469
+ node = self._return_node_object(target_node_identifier)
470
+ sources = node.in_degrees
471
+ edge_status = []
472
+ res = None
473
+
474
+ for item in sources:
475
+ edge = self.find_path(source=item, target=target_node_identifier)
476
+ edge_status.append(dict(edge=edge if edge else None, dep_met=edge.dependency_met() if edge else False))
477
+
478
+ if len([item for item in edge_status if item["dep_met"] == True]) == len(sources):
479
+ res = node.handle_task_execution()
480
+ self.outputs.update({ target_node_identifier: res })
481
+ self.status.update({ target_node_identifier: edge.target.status })
482
+
483
+ return res, self.outputs
484
+
485
+ else:
486
+ if not self.edges or self.nodes:
487
+ self._logger.log(level="error", message="Needs at least 2 nodes and 1 edge to activate the graph. We'll return None.", color="red")
488
+ return None
489
+
490
+ for edge in self.edges:
491
+ res = edge.activate()
492
+ if not res:
493
+ break
494
+
495
+ node_identifier = edge.target.identifier
496
+ self.outputs.update({ node_identifier: res })
497
+ self.status.update({ node_identifier: edge.target.status })
498
+
499
+ last_task_output = [v for v in self.outputs.values()][len([v for v in self.outputs.values()]) - 1] if [v for v in self.outputs.values()] else None
500
+ return last_task_output, self.outputs
@@ -72,7 +72,7 @@ class EvaluationItem(BaseModel):
72
72
 
73
73
  class Evaluation(BaseModel):
74
74
  items: List[EvaluationItem] = []
75
- latency: int = Field(default=None, description="job execution latency in seconds")
75
+ latency: float = Field(default=None, description="job execution latency in seconds")
76
76
  tokens: int = Field(default=None, description="tokens consumed")
77
77
  eval_by: Any = Field(default=None, description="stores agent object that evaluates the outcome")
78
78
 
@@ -79,7 +79,7 @@ def form_agent_network(
79
79
  if agents:
80
80
  vhq_task.description += "Consider adding following agents in the formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
81
81
 
82
- res = vhq_task.execute_sync(agent=vhq_formation_planner, context=context)
82
+ res = vhq_task.execute(agent=vhq_formation_planner, context=context)
83
83
  _formation = Formation.SUPERVISING
84
84
 
85
85
  if res.pydantic:
versionhq/task/model.py CHANGED
@@ -3,6 +3,7 @@ import threading
3
3
  import datetime
4
4
  import uuid
5
5
  import inspect
6
+ import enum
6
7
  from concurrent.futures import Future
7
8
  from hashlib import md5
8
9
  from typing import Any, Dict, List, Set, Optional, Callable, Type
@@ -11,18 +12,25 @@ from typing_extensions import Annotated, Self
11
12
  from pydantic import UUID4, BaseModel, Field, PrivateAttr, field_validator, model_validator, InstanceOf, field_validator
12
13
  from pydantic_core import PydanticCustomError
13
14
 
14
-
15
+ import versionhq as vhq
15
16
  from versionhq.task.log_handler import TaskOutputStorageHandler
16
17
  from versionhq.task.evaluate import Evaluation, EvaluationItem
17
18
  from versionhq.tool.model import Tool, ToolSet
18
19
  from versionhq._utils import process_config, Logger
19
20
 
20
21
 
22
+ class TaskExecutionType(enum.Enum):
23
+ """
24
+ Enumeration to store task execution types of independent tasks without dependencies.
25
+ """
26
+ SYNC = 1
27
+ ASYNC = 2
28
+
29
+
21
30
  class ResponseField(BaseModel):
22
31
  """
23
- A class to store the response format and schema that will cascade to the LLM.
24
- The `config` field can store additional params:
25
- https://community.openai.com/t/official-documentation-for-supported-schemas-for-response-format-parameter-in-calls-to-client-beta-chats-completions-parse/932422/3
32
+ A class to store a response format that will generate a JSON schema.
33
+ One layer of nested child is acceptable.
26
34
  """
27
35
 
28
36
  title: str = Field(default=None, description="title of the field")
@@ -31,7 +39,7 @@ class ResponseField(BaseModel):
31
39
  properties: Optional[List[BaseModel]] = Field(default=None, description="store dict items in ResponseField format")
32
40
  required: bool = Field(default=True)
33
41
  nullable: bool = Field(default=False)
34
- config: Optional[Dict[str, Any]] = Field(default=None, description="additional rules")
42
+ config: Optional[Dict[str, Any]] = Field(default_factory=dict, description="additional rules")
35
43
 
36
44
 
37
45
  @model_validator(mode="after")
@@ -57,36 +65,44 @@ class ResponseField(BaseModel):
57
65
 
58
66
  def _format_props(self) -> Dict[str, Any]:
59
67
  """
60
- Structure valid properties. We accept 2 nested objects.
68
+ Structure valid properties from the ResponseField object. 1 layer of nested child is accepted.
61
69
  """
62
70
  from versionhq.llm.llm_vars import SchemaType
63
71
 
64
72
  schema_type = SchemaType(type=self.data_type).convert()
65
73
  props: Dict[str, Any] = {}
66
74
 
67
- if self.data_type is list and self.items is not dict:
68
- props = {
69
- "type": schema_type,
70
- "items": { "type": SchemaType(type=self.items).convert() },
71
- }
72
-
73
- elif self.data_type is list and self.items is dict:
74
- nested_p, nested_r = dict(), list()
75
+ if self.data_type is list:
76
+ if self.items is dict:
77
+ nested_p, nested_r = dict(), list()
78
+
79
+ if self.properties:
80
+ for item in self.properties:
81
+ nested_p.update(**item._format_props())
82
+ nested_r.append(item.title)
83
+
84
+ props = {
85
+ "type": schema_type,
86
+ "items": {
87
+ "type": SchemaType(type=self.items).convert(),
88
+ "properties": nested_p,
89
+ "required": nested_r,
90
+ "additionalProperties": False
91
+ }
92
+ }
75
93
 
76
- if self.properties:
77
- for item in self.properties:
78
- nested_p.update(**item._format_props())
79
- nested_r.append(item.title)
94
+ elif self.items is list:
95
+ props = {
96
+ "type": schema_type,
97
+ "items": { "type": SchemaType(type=self.items).convert(), "items": { "type": SchemaType(type=str).convert() }},
98
+ }
80
99
 
81
- props = {
82
- "type": schema_type,
83
- "items": {
84
- "type": SchemaType(type=self.items).convert(),
85
- "properties": nested_p,
86
- "required": nested_r,
87
- "additionalProperties": False
100
+ else:
101
+ props = {
102
+ "type": schema_type,
103
+ "items": { "type": SchemaType(type=self.items).convert() },
88
104
  }
89
- }
105
+
90
106
 
91
107
  elif self.data_type is dict:
92
108
  p, r = dict(), list()
@@ -184,17 +200,16 @@ class TaskOutput(BaseModel):
184
200
  return json.dumps(self.json_dict) if self.json_dict else self.raw[0: 127]
185
201
 
186
202
 
187
- def evaluate(self, task, latency: int | float = None, tokens: int = None) -> Evaluation:
203
+ def evaluate(self, task) -> Evaluation:
188
204
  """
189
205
  Evaluate the output based on the criteria, score each from 0 to 1 scale, and raise suggestions for future improvement.
190
206
  """
191
207
  from versionhq.task.TEMPLATES.Description import EVALUATE
192
208
 
193
- if not self.evaluation:
194
- self.evaluation = Evaluation()
209
+ self.evaluation = Evaluation() if not self.evaluation else self.evaluation
195
210
 
196
- self.evaluation.latency = latency if latency is not None else task.latency
197
- self.evaluation.tokens = tokens if tokens is not None else task.tokens
211
+ # self.evaluation.latency = latency if latency is not None else task.latency
212
+ # self.evaluation.tokens = tokens if tokens is not None else task.tokens
198
213
 
199
214
  eval_criteria = task.eval_criteria if task.eval_criteria else ["Overall competitiveness", ]
200
215
 
@@ -203,7 +218,7 @@ class TaskOutput(BaseModel):
203
218
  description=EVALUATE.format(task_description=task.description, task_output=self.raw, eval_criteria=str(item)),
204
219
  pydantic_output=EvaluationItem
205
220
  )
206
- res = task_eval.execute_sync(agent=self.evaluation.eval_by)
221
+ res = task_eval.execute(agent=self.evaluation.eval_by)
207
222
 
208
223
  if res.pydantic:
209
224
  item = EvaluationItem(score=res.pydantic.score, suggestion=res.pydantic.suggestion, criteria=res.pydantic.criteria)
@@ -241,7 +256,7 @@ class TaskOutput(BaseModel):
241
256
 
242
257
  class Task(BaseModel):
243
258
  """
244
- A class that stores independent task information.
259
+ A class that stores independent task information and handles task executions.
245
260
  """
246
261
 
247
262
  __hash__ = object.__hash__
@@ -255,8 +270,8 @@ class Task(BaseModel):
255
270
  description: str = Field(description="Description of the actual task")
256
271
 
257
272
  # output
258
- pydantic_output: Optional[Any] = Field(default=None, description="store a custom Pydantic class as response format")
259
- response_fields: List[ResponseField] = Field(default_factory=list, description="store the list of ResponseFields to create the response format")
273
+ pydantic_output: Optional[Type[BaseModel]] = Field(default=None, description="store Pydantic class as structured response format")
274
+ response_fields: Optional[List[ResponseField]] = Field(default_factory=list, description="store list of ResponseField as structured response format")
260
275
  output: Optional[TaskOutput] = Field(default=None, description="store the final task output in TaskOutput class")
261
276
 
262
277
  # task setup
@@ -269,8 +284,8 @@ class Task(BaseModel):
269
284
  tool_res_as_final: bool = Field(default=False, description="when set True, tools res will be stored in the `TaskOutput`")
270
285
 
271
286
  # execution rules
287
+ execution_type: TaskExecutionType = Field(default=TaskExecutionType.SYNC)
272
288
  allow_delegation: bool = Field(default=False, description="ask other agents for help and run the task instead")
273
- async_execution: bool = Field(default=False,description="whether the task should be executed asynchronously or not")
274
289
  callback: Optional[Callable] = Field(default=None, description="callback to be executed after the task is completed.")
275
290
  callback_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict, description="kwargs for the callback when the callback is callable")
276
291
 
@@ -408,11 +423,7 @@ Ref. Output image: {output_formats_to_follow}
408
423
  properties, required_fields = {}, []
409
424
  for i, item in enumerate(self.response_fields):
410
425
  if item:
411
- if item.data_type is dict:
412
- properties.update(item._format_props())
413
- else:
414
- properties.update(item._format_props())
415
-
426
+ properties.update(item._format_props())
416
427
  required_fields.append(item.title)
417
428
 
418
429
  response_schema = {
@@ -421,7 +432,6 @@ Ref. Output image: {output_formats_to_follow}
421
432
  "required": required_fields,
422
433
  "additionalProperties": False,
423
434
  }
424
-
425
435
  response_format = {
426
436
  "type": "json_schema",
427
437
  "json_schema": { "name": "outcome", "schema": response_schema }
@@ -429,7 +439,7 @@ Ref. Output image: {output_formats_to_follow}
429
439
 
430
440
 
431
441
  elif self.pydantic_output:
432
- response_format = StructuredOutput(response_format=self.pydantic_output)._format()
442
+ response_format = StructuredOutput(response_format=self.pydantic_output, provider=model_provider)._format()
433
443
 
434
444
  return response_format
435
445
 
@@ -481,7 +491,6 @@ Ref. Output image: {output_formats_to_follow}
481
491
 
482
492
  for k, v in json_dict.items():
483
493
  setattr(output_pydantic, k, v)
484
-
485
494
  except:
486
495
  pass
487
496
 
@@ -536,9 +545,35 @@ Ref. Output image: {output_formats_to_follow}
536
545
  self._logger.log(level="error", message=f"Failed to add to the memory: {str(e)}", color="red")
537
546
  pass
538
547
 
548
+ def _build_agent_from_task(self, task_description: str = None) -> InstanceOf["vhq.Agent"]:
549
+ task_description = task_description if task_description else self.description
550
+ if not task_description:
551
+ self._logger.log(level="error", message="Task is missing the description.", color="red")
552
+ pass
553
+
554
+ agent = vhq.Agent(goal=task_description, role=task_description, maxit=1) #! REFINEME
555
+ return agent
556
+
539
557
 
540
558
  # task execution
541
- def execute_sync(self, agent, context: Optional[str | List[Any]] = None) -> TaskOutput:
559
+ def execute(self, type: TaskExecutionType = None, agent: Optional[Any] = None, context: Optional[str] = None) -> TaskOutput | Future[TaskOutput]:
560
+ """
561
+ A main method to handle task execution. Build an agent when the agent is not given.
562
+ """
563
+ type = type if type else self.execution_type if self.execution_type else TaskExecutionType.SYNC
564
+
565
+ if not agent:
566
+ agent = self._build_agent_from_task(task_description=self.description)
567
+
568
+ match type:
569
+ case TaskExecutionType.SYNC:
570
+ return self._execute_sync(agent=agent, context=context)
571
+
572
+ case TaskExecutionType.ASYNC:
573
+ return self._execute_async(agent=agent, context=context)
574
+
575
+
576
+ def _execute_sync(self, agent, context: Optional[str | List[Any]] = None) -> TaskOutput:
542
577
  """
543
578
  Execute the task synchronously.
544
579
  When the task has context, make sure we have executed all the tasks in the context first.
@@ -553,7 +588,7 @@ Ref. Output image: {output_formats_to_follow}
553
588
  return self._execute_core(agent, context)
554
589
 
555
590
 
556
- def execute_async(self, agent, context: Optional[str] = None) -> Future[TaskOutput]:
591
+ def _execute_async(self, agent, context: Optional[str] = None) -> Future[TaskOutput]:
557
592
  """
558
593
  Execute the task asynchronously.
559
594
  """
@@ -565,7 +600,7 @@ Ref. Output image: {output_formats_to_follow}
565
600
 
566
601
  def _execute_task_async(self, agent, context: Optional[str], future: Future[TaskOutput]) -> None:
567
602
  """
568
- Execute the task asynchronously with context handling.
603
+ Executes the task asynchronously with context handling.
569
604
  """
570
605
 
571
606
  result = self._execute_core(agent, context)
@@ -574,8 +609,8 @@ Ref. Output image: {output_formats_to_follow}
574
609
 
575
610
  def _execute_core(self, agent, context: Optional[str]) -> TaskOutput:
576
611
  """
577
- Execute the given task with the given agent.
578
- Handle 1. agent delegation, 2. tools, 3. context to consider, and 4. callbacks
612
+ A core method for task execution.
613
+ Handles 1. agent delegation, 2. tools, 3. context to add to the prompt, and 4. callbacks
579
614
  """
580
615
 
581
616
  from versionhq.agent.model import Agent
@@ -634,13 +669,13 @@ Ref. Output image: {output_formats_to_follow}
634
669
  json_dict=json_dict_output
635
670
  )
636
671
 
637
-
638
672
  self.latency = (ended_at - started_at).total_seconds()
673
+ task_output.evaluation = Evaluation(latency=self.latency, tokens=self.tokens)
639
674
  self.output = task_output
640
675
  self.processed_agents.add(agent.role)
641
676
 
642
677
  if self.should_evaluate:
643
- task_output.evaluate(task=self, latency=self.latency, tokens=self.tokens)
678
+ task_output.evaluate(task=self)
644
679
 
645
680
  self._create_short_and_long_term_memories(agent=agent, task_output=task_output)
646
681
 
@@ -648,7 +683,7 @@ Ref. Output image: {output_formats_to_follow}
648
683
  kwargs = { **self.callback_kwargs, **task_output.json_dict }
649
684
  sig = inspect.signature(self.callback)
650
685
  valid_keys = [param.name for param in sig.parameters.values() if param.kind == param.POSITIONAL_OR_KEYWORD]
651
- valid_kwargs = { k: kwargs[k] for k in valid_keys }
686
+ valid_kwargs = { k: kwargs[k] if k in kwargs else None for k in valid_keys }
652
687
  callback_res = self.callback(**valid_kwargs)
653
688
  task_output.callback_output = callback_res
654
689
 
@@ -687,50 +722,49 @@ Task ID: {str(self.id)}
687
722
 
688
723
 
689
724
 
690
- class ConditionalTask(Task):
691
- """
692
- A task that can be conditionally executed based on the output of another task.
693
- When the `condition` return True, execute the task, else skipped with `skipped task output`.
694
- """
725
+ # class ConditionalTask(Task):
726
+ # """
727
+ # A task that can be conditionally executed based on the output of another task.
728
+ # When the `condition` return True, execute the task, else skipped with `skipped task output`.
729
+ # """
695
730
 
696
- condition: Callable[[TaskOutput], bool] = Field(
697
- default=None,
698
- description="max. number of retries for an agent to execute a task when an error occurs",
699
- )
731
+ # condition: Callable[[TaskOutput], bool] = Field(
732
+ # default=None,
733
+ # description="max. number of retries for an agent to execute a task when an error occurs",
734
+ # )
700
735
 
701
736
 
702
- def __init__(self, condition: Callable[[Any], bool], **kwargs):
703
- super().__init__(**kwargs)
704
- self.condition = condition
705
- self._logger = Logger(verbose=True)
737
+ # def __init__(self, condition: Callable[[Any], bool], **kwargs):
738
+ # super().__init__(**kwargs)
739
+ # self.condition = condition
706
740
 
707
741
 
708
- def should_execute(self, context: TaskOutput) -> bool:
709
- """
710
- Decide whether the conditional task should be executed based on the provided context.
711
- Return `True` if it should be executed.
712
- """
713
- return self.condition(context)
742
+ # def should_execute(self, context: TaskOutput) -> bool:
743
+ # """
744
+ # Decide whether the conditional task should be executed based on the provided context.
745
+ # Return `True` if it should be executed.
746
+ # """
747
+ # return self.condition(context)
714
748
 
715
749
 
716
- def get_skipped_task_output(self):
717
- return TaskOutput(task_id=self.id, raw="", pydantic=None, json_dict={})
750
+ # def get_skipped_task_output(self):
751
+ # return TaskOutput(task_id=self.id, raw="", pydantic=None, json_dict={})
718
752
 
719
753
 
720
- def _handle_conditional_task(self, task_outputs: List[TaskOutput], task_index: int, was_replayed: bool) -> Optional[TaskOutput]:
721
- """
722
- When the conditional task should be skipped, return `skipped_task_output` as task_output else return None
723
- """
754
+ # def _handle_conditional_task(self, task_outputs: List[TaskOutput], task_index: int, was_replayed: bool) -> Optional[TaskOutput]:
755
+ # """
756
+ # When the conditional task should be skipped, return `skipped_task_output` as task_output else return None
757
+ # """
724
758
 
725
- previous_output = task_outputs[task_index - 1] if task_outputs and len(task_outputs) > 1 else None
759
+ # previous_output = task_outputs[task_index - 1] if task_outputs and len(task_outputs) > 1 else None
726
760
 
727
- if previous_output and not self.should_execute(previous_output):
728
- self._logger.log(level="warning", message=f"Skipping conditional task: {self.description}", color="yellow")
729
- skipped_task_output = self.get_skipped_task_output()
730
- self.output = skipped_task_output
761
+ # if previous_output and not self.should_execute(previous_output):
762
+ # self._logger.log(level="warning", message=f"Skipping conditional task: {self.description}", color="yellow")
763
+ # skipped_task_output = self.get_skipped_task_output()
764
+ # self.output = skipped_task_output
731
765
 
732
- if not was_replayed:
733
- self._store_execution_log(self, task_index=task_index, was_replayed=was_replayed, inputs={})
734
- return skipped_task_output
766
+ # if not was_replayed:
767
+ # self._store_execution_log(self, task_index=task_index, was_replayed=was_replayed, inputs={})
768
+ # return skipped_task_output
735
769
 
736
- return None
770
+ # return None
@@ -3,11 +3,11 @@ from typing import Dict, Type, List, Any
3
3
  from pydantic import BaseModel, Field, InstanceOf
4
4
 
5
5
  from versionhq.llm.llm_vars import SchemaType
6
- from versionhq.llm.model import LLM
6
+ from versionhq.llm.model import LLM, DEFAULT_MODEL_PROVIDER_NAME
7
7
 
8
8
 
9
9
  """
10
- Structure a response schema (json schema) from the given Pydantic model.
10
+ Generate a JSON schema from the given Pydantic model.
11
11
  """
12
12
 
13
13
 
@@ -15,7 +15,7 @@ class StructuredObject:
15
15
  """
16
16
  A class to store the structured dictionary.
17
17
  """
18
- provider: str = "openai"
18
+ provider: str = None
19
19
  field: Type[Field]
20
20
 
21
21
  title: str
@@ -24,20 +24,21 @@ class StructuredObject:
24
24
  required: List[str] = list()
25
25
  additionalProperties: bool = False
26
26
 
27
- def __init__(self, name, field: Type[Field], provider: str | InstanceOf[LLM] = "openai"):
27
+ def __init__(self, name, field: Type[Field], provider: str | InstanceOf[LLM] = None):
28
28
  self.title = name
29
29
  self.field = field
30
30
  self.dtype = "object"
31
31
  self.additionalProperties = False
32
- self.provider = provider if isinstance(provider, str) else provider.provider
32
+ self.provider = provider if isinstance(provider, str) else provider.provider if isinstance(provider, LLM) else DEFAULT_MODEL_PROVIDER_NAME
33
33
 
34
34
  def _format(self):
35
35
  if not self.field:
36
36
  pass
37
37
  else:
38
38
  description = self.field.description if hasattr(self.field, "description") and self.field.description is not None else ""
39
- self.properties.update({"item": { "type": SchemaType(self.field.annotation.__args__).convert() }})
40
- self.required.append("item")
39
+ field_name = self.field.__name__ if hasattr(self.field, "__name__") and self.field.__name__ else self.title
40
+ self.properties.update({ field_name : { "type": SchemaType(self.field.annotation.__args__).convert() }})
41
+ self.required.append(field_name)
41
42
 
42
43
  return {
43
44
  self.title: {
@@ -55,13 +56,13 @@ class StructuredList:
55
56
  """
56
57
  A class to store a structured list with 1 nested object.
57
58
  """
58
- provider: str = "openai"
59
+ provider: str = DEFAULT_MODEL_PROVIDER_NAME
59
60
  field: Type[Field]
60
61
  title: str = ""
61
62
  dtype: str = "array"
62
63
  items: Dict[str, Dict[str, str]] = dict()
63
64
 
64
- def __init__(self, name, field: Type[Field], provider: str | LLM = "openai"):
65
+ def __init__(self, name, field: Type[Field], provider: str | LLM = DEFAULT_MODEL_PROVIDER_NAME):
65
66
  self.provider = provider if isinstance(provider, str) else provider.provider
66
67
  self.field = field
67
68
  self.title = name
@@ -77,15 +78,15 @@ class StructuredList:
77
78
  description = "" if field.description is None else field.description
78
79
  props = {}
79
80
 
80
- for item in field.annotation.__args__:
81
+ for i, item in enumerate(field.annotation.__args__):
81
82
  nested_object_type = item.__origin__ if hasattr(item, "__origin__") else item
82
83
 
83
84
  if nested_object_type == dict:
84
85
  props.update({
85
86
  # "nest": {
86
87
  "type": "object",
87
- "properties": { "item": { "type": "string"} }, #! REFINEME - field title <>`item`
88
- "required": ["item",],
88
+ "properties": { f"{str(i)}": { "type": "string"} },
89
+ "required": [f"{str(i)}",],
89
90
  "additionalProperties": False
90
91
  # }
91
92
  })
@@ -94,13 +95,14 @@ class StructuredList:
94
95
  props.update({
95
96
  # "nest": {
96
97
  "type": "array",
97
- "items": { "type": "string" } , #! REFINEME - field title <>`item`
98
+ "items": { "type": "string" },
98
99
  # }
99
100
  })
100
101
  else:
101
102
  props.update({ "type": SchemaType(nested_object_type).convert() })
102
103
 
103
104
  self.items = { **props }
105
+
104
106
  return {
105
107
  self.title: {
106
108
  "type": self.dtype,
@@ -112,7 +114,7 @@ class StructuredList:
112
114
 
113
115
  class StructuredOutput(BaseModel):
114
116
  response_format: Any = None # pydantic base model
115
- provider: str = "openai"
117
+ provider: str = None
116
118
  applicable_models: List[InstanceOf[LLM] | str] = list()
117
119
  name: str = ""
118
120
  schema: Dict[str, Any] = dict(type="object", additionalProperties=False, properties=dict(), required=list())
versionhq/team/model.py CHANGED
@@ -10,7 +10,7 @@ from pydantic._internal._generate_schema import GenerateSchema
10
10
  from pydantic_core import PydanticCustomError, core_schema
11
11
 
12
12
  from versionhq.agent.model import Agent
13
- from versionhq.task.model import Task, TaskOutput, ConditionalTask
13
+ from versionhq.task.model import Task, TaskOutput, TaskExecutionType
14
14
  from versionhq.task.formatter import create_raw_outputs
15
15
  from versionhq.team.team_planner import TeamPlanner
16
16
  from versionhq._utils.logger import Logger
@@ -215,7 +215,7 @@ class Team(BaseModel):
215
215
  for task in reversed(self.tasks):
216
216
  if not task:
217
217
  break
218
- elif task.async_execution:
218
+ elif task.execution_type == TaskExecutionType.ASYNC:
219
219
  async_task_count += 1
220
220
  else:
221
221
  break
@@ -341,7 +341,7 @@ class Team(BaseModel):
341
341
  for task_index, task in enumerate(tasks):
342
342
  if start_index is not None and task_index < start_index:
343
343
  if task.output:
344
- if task.async_execution:
344
+ if task.execution_type == TaskExecutionType.ASYNC:
345
345
  task_outputs.append(task.output)
346
346
  else:
347
347
  task_outputs = [task.output]
@@ -352,20 +352,21 @@ class Team(BaseModel):
352
352
  if responsible_agent is None:
353
353
  self._assign_tasks()
354
354
 
355
- if isinstance(task, ConditionalTask):
356
- skipped_task_output = task._handle_conditional_task(task_outputs, futures, task_index, was_replayed)
357
- if skipped_task_output:
358
- continue
355
+ ## commented out - this will be handled by node objects
356
+ # if isinstance(task, ConditionalTask):
357
+ # skipped_task_output = task._handle_conditional_task(task_outputs, futures, task_index, was_replayed)
358
+ # if skipped_task_output:
359
+ # continue
359
360
 
360
361
  # self._log_task_start(task, responsible_agent)
361
362
 
362
- if task.async_execution:
363
+ if task.execution_type == TaskExecutionType.ASYNC:
363
364
  context = create_raw_outputs(tasks=[task, ], task_outputs=([last_sync_output,] if last_sync_output else []))
364
- future = task.execute_async(agent=responsible_agent, context=context)
365
+ future = task._execute_async(agent=responsible_agent, context=context)
365
366
  futures.append((task, future, task_index))
366
367
  else:
367
368
  context = create_raw_outputs(tasks=[task,], task_outputs=([last_sync_output,] if last_sync_output else [] ))
368
- task_output = task.execute_sync(agent=responsible_agent, context=context)
369
+ task_output = task.execute(agent=responsible_agent, context=context)
369
370
  if self.managers and responsible_agent in [manager.agent for manager in self.managers]:
370
371
  lead_task_output = task_output
371
372
 
@@ -3,8 +3,6 @@ from typing import Any, List, Optional, Dict
3
3
  from pydantic import BaseModel, Field
4
4
 
5
5
 
6
-
7
-
8
6
  class TeamPlanner:
9
7
  """
10
8
  A class to handle agent formations based on the given task description.
@@ -50,7 +48,7 @@ class TeamPlanner:
50
48
  ResponseField(title="role", data_type=str, required=True),
51
49
  ],
52
50
  )
53
- res = task.execute_sync(agent=agent_creator)
51
+ res = task.execute(agent=agent_creator)
54
52
  agent = Agent(
55
53
  role=res.json_dict["role"] if "role" in res.json_dict else res.raw,
56
54
  goal=res.json_dict["goal"] if "goal" in res.json_dict else task.description
@@ -90,5 +88,5 @@ class TeamPlanner:
90
88
  """,
91
89
  pydantic_output=TeamPlanIdea
92
90
  )
93
- output = task.execute_sync(agent=team_planner, context=context, tools=tools)
91
+ output = task.execute(agent=team_planner, context=context, tools=tools)
94
92
  return output
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: versionhq
3
- Version: 1.1.13.1
3
+ Version: 1.2.0.2
4
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
@@ -86,8 +86,8 @@ Requires-Dist: matplotlib>=3.10.0; extra == "matplotlib"
86
86
  [![DL](https://img.shields.io/badge/Download-15K+-red)](https://clickpy.clickhouse.com/dashboard/versionhq)
87
87
  ![MIT license](https://img.shields.io/badge/License-MIT-green)
88
88
  [![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)
89
- ![PyPI](https://img.shields.io/badge/PyPI-v1.1.12+-blue)
90
- ![python ver](https://img.shields.io/badge/Python-3.11+-purple)
89
+ ![PyPI](https://img.shields.io/badge/PyPI-v1.2.0+-blue)
90
+ ![python ver](https://img.shields.io/badge/Python-3.11/3.12-purple)
91
91
  ![pyenv ver](https://img.shields.io/badge/pyenv-2.5.0-orange)
92
92
 
93
93
 
@@ -266,9 +266,6 @@ By default, agents prioritize JSON over plane text outputs.
266
266
  def dummy_func(message: str, test1: str, test2: list[str]) -> str:
267
267
  return f"""{message}: {test1}, {", ".join(test2)}"""
268
268
 
269
-
270
- agent = vhq.Agent(role="demo", goal="amazing project goal")
271
-
272
269
  task = vhq.Task(
273
270
  description="Amazing task",
274
271
  pydantic_output=CustomOutput,
@@ -276,7 +273,7 @@ By default, agents prioritize JSON over plane text outputs.
276
273
  callback_kwargs=dict(message="Hi! Here is the result: ")
277
274
  )
278
275
 
279
- res = task.execute_sync(agent=agent, context="amazing context to consider.")
276
+ res = task.execute(context="amazing context to consider.")
280
277
  print(res)
281
278
  ```
282
279
 
@@ -377,6 +374,7 @@ Tasks can be delegated to a team manager, peers in the team, or completely new a
377
374
  └── workflows/ # Github actions
378
375
 
379
376
  docs/ # Documentation built by MkDocs
377
+ mkdocs.yml # MkDocs config
380
378
 
381
379
  src/
382
380
  └── versionhq/ # Orchestration framework package
@@ -392,6 +390,8 @@ src/
392
390
  │ └── ...
393
391
 
394
392
  └── uploads/ [.gitignore] # Local directory to store uploaded files such as graphviz diagrams generatd by `Network` class
393
+ |
394
+ pyproject.toml # Project config
395
395
 
396
396
  ```
397
397
 
@@ -1,4 +1,4 @@
1
- versionhq/__init__.py,sha256=JJMdTbmmTzzublAjg3tFmOm13CKLwVsNz3cdsDP0bJQ,2780
1
+ versionhq/__init__.py,sha256=RwVa60s3Puko2L4VU8tS7jAFhPy5zqbYqyJUdDhyjHA,2783
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
@@ -7,7 +7,7 @@ versionhq/_utils/usage_metrics.py,sha256=NXF18dn5NNvGK7EsQ4AAghpR8ppYOjMx6ABenLL
7
7
  versionhq/_utils/vars.py,sha256=bZ5Dx_bFKlt3hi4-NNGXqdk7B23If_WaTIju2fiTyPQ,57
8
8
  versionhq/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  versionhq/agent/inhouse_agents.py,sha256=vSobrH1gXDWlaNsiges3sqETeUrEssRzQvCZCY2hQZA,2374
10
- versionhq/agent/model.py,sha256=NLHQPYqYFg9GX3NoDP1Rs3vHFlYv6Fl6JZ4QlNOYMn0,25429
10
+ versionhq/agent/model.py,sha256=kSYCAiRxF62ztj-S-_KfN0mhPKikjbFZ0fy2Yj5J7Jo,25400
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
@@ -34,7 +34,7 @@ versionhq/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
34
34
  versionhq/memory/contextual_memory.py,sha256=tCsOOAUnfrOL7YiakqGoi3uShzzS870TmGnlGd3z_A4,3556
35
35
  versionhq/memory/model.py,sha256=4wow2O3UuMZ0AbC2NyxddGZac3-_GjNZbK9wsA015NA,8145
36
36
  versionhq/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- versionhq/network/model.py,sha256=npSV5RUYdmkM9cMGkNLiKD4vYb3yNVyh6qCdNKkVIN8,15223
37
+ versionhq/network/model.py,sha256=nqRlekld7Hy0cQZCOXbOGpanTFJwWfHyJt1OodhHJI0,20316
38
38
  versionhq/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  versionhq/storage/base.py,sha256=p-Jas0fXQan_qotnRD6seQxrT2lj-uw9-SmHQhdppcs,355
40
40
  versionhq/storage/ltm_sqlite_storage.py,sha256=wdUiuwHfJocdk0UGqyrdU4S5Nae1rgsoRNu3LWmGFcI,3951
@@ -43,16 +43,16 @@ versionhq/storage/rag_storage.py,sha256=ScWC0vH327vnGw8UGscAOoIfqrq3mhvXT3vEKzHZ
43
43
  versionhq/storage/task_output_storage.py,sha256=E1t_Fkt78dPYIOl3MP7LfQ8oGtjlzxBuSNq_8ZXKho8,4573
44
44
  versionhq/storage/utils.py,sha256=ByYXPoEIGJYLUqz-DWjbCAnneNrH1otiYbp12SCILpM,747
45
45
  versionhq/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- versionhq/task/evaluate.py,sha256=fBLL6sm763e3Ev6uU0tMqFRUVwAcBj6dLM5N062qHuc,3952
47
- versionhq/task/formation.py,sha256=QXFZfY604cpS79X7HEBmwFKn91H8jS3Ak6EhJGgaRIg,6355
46
+ versionhq/task/evaluate.py,sha256=WdUgjbZL62XrxyWe5MTz29scfzwmuAHGxJ7GvAB8Fmk,3954
47
+ versionhq/task/formation.py,sha256=iHhW2webMirYC78G8kHpaMTYjCdq7c3yFD2u2Egd8Eo,6350
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=qbH_fMnkDqZUp-Sgd7LB7LMsXSaytMFMVffS5q3En1c,29160
51
- versionhq/task/structured_response.py,sha256=uVqgeUxNOACPe2hdc0RELSbtKd1vrwonfjXMOGTT0TI,4818
50
+ versionhq/task/model.py,sha256=zAnZ-_5YKZRs43gpM2AL9IVtttq5GqGfqfCMj_Oyz9g,30624
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/team/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
- versionhq/team/model.py,sha256=qPsV3O3O2TIOT-3h142KVVFdO587LhoOeVkK2niZHEc,19062
55
- versionhq/team/team_planner.py,sha256=x1eLkdfQwW6w3Kyi_wVaVlA41TaNJDIYWhtEqp_OcaI,3675
54
+ versionhq/team/model.py,sha256=kxrjF3RFGO9l3IsJIffI1nqgl_Vh5RLuhqrwIqbEoBY,19214
55
+ versionhq/team/team_planner.py,sha256=Zc3zvhPR7T0O8RQoq9CKOrvrll4klemY1rONLFeJ96M,3663
56
56
  versionhq/tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  versionhq/tool/cache_handler.py,sha256=iL8FH7X0G-cdT0uhJwzuhLDaadTXOdfybZcDy151-es,1085
58
58
  versionhq/tool/composio_tool.py,sha256=38mEiVvTkuw1BLD233Bl1Gwxbpss1yfQiZLTWwX6BdA,8648
@@ -60,8 +60,8 @@ versionhq/tool/composio_tool_vars.py,sha256=FvBuEXsOQUYnN7RTFxT20kAkiEYkxWKkiVtg
60
60
  versionhq/tool/decorator.py,sha256=C4ZM7Xi2gwtEMaSeRo-geo_g_MAkY77WkSLkAuY0AyI,1205
61
61
  versionhq/tool/model.py,sha256=PO4zNWBZcJhYVur381YL1dy6zqurio2jWjtbxOxZMGI,12194
62
62
  versionhq/tool/tool_handler.py,sha256=2m41K8qo5bGCCbwMFferEjT-XZ-mE9F0mDUOBkgivOI,1416
63
- versionhq-1.1.13.1.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
64
- versionhq-1.1.13.1.dist-info/METADATA,sha256=QAjiAx25V2-0nc9PGPNII3-31IHw8hHAghUfdQxZogA,21448
65
- versionhq-1.1.13.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
66
- versionhq-1.1.13.1.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
67
- versionhq-1.1.13.1.dist-info/RECORD,,
63
+ versionhq-1.2.0.2.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
64
+ versionhq-1.2.0.2.dist-info/METADATA,sha256=YT-m2KqdExbv9rb2uLSVoRjkMiB06pKExUqkduXFoM0,21462
65
+ versionhq-1.2.0.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
66
+ versionhq-1.2.0.2.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
67
+ versionhq-1.2.0.2.dist-info/RECORD,,