versionhq 1.2.0.3__py3-none-any.whl → 1.2.1.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 +2 -2
- versionhq/_utils/logger.py +1 -1
- versionhq/{network → graph}/model.py +175 -117
- versionhq/knowledge/source.py +1 -1
- versionhq/task/formation.py +2 -2
- versionhq/task/model.py +8 -9
- versionhq/team/model.py +38 -60
- {versionhq-1.2.0.3.dist-info → versionhq-1.2.1.0.dist-info}/METADATA +23 -12
- {versionhq-1.2.0.3.dist-info → versionhq-1.2.1.0.dist-info}/RECORD +13 -13
- /versionhq/{network → graph}/__init__.py +0 -0
- {versionhq-1.2.0.3.dist-info → versionhq-1.2.1.0.dist-info}/LICENSE +0 -0
- {versionhq-1.2.0.3.dist-info → versionhq-1.2.1.0.dist-info}/WHEEL +0 -0
- {versionhq-1.2.0.3.dist-info → versionhq-1.2.1.0.dist-info}/top_level.txt +0 -0
versionhq/__init__.py
CHANGED
@@ -16,7 +16,7 @@ from versionhq.clients.workflow.model import MessagingWorkflow, MessagingCompone
|
|
16
16
|
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
|
-
from versionhq.
|
19
|
+
from versionhq.graph.model import TaskStatus, TaskGraph, Node, Edge, DependencyType
|
20
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
|
@@ -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.2.0
|
33
|
+
__version__ = "1.2.1.0"
|
34
34
|
__all__ = [
|
35
35
|
"Agent",
|
36
36
|
|
versionhq/_utils/logger.py
CHANGED
@@ -38,7 +38,7 @@ class Printer:
|
|
38
38
|
class Logger(BaseModel):
|
39
39
|
"""
|
40
40
|
Control CLI messages.
|
41
|
-
Color: red = error, yellow = warning, blue = info (from vhq), green = info (from third
|
41
|
+
Color: red = error, yellow = warning, blue = info (from vhq), green = info (from third parties)
|
42
42
|
"""
|
43
43
|
|
44
44
|
verbose: bool = Field(default=True)
|
@@ -1,51 +1,37 @@
|
|
1
1
|
import enum
|
2
2
|
import uuid
|
3
|
+
import networkx as nx
|
4
|
+
import matplotlib.pyplot as plt
|
3
5
|
from abc import ABC
|
4
6
|
from typing import List, Any, Optional, Callable, Dict, Type, Tuple
|
5
7
|
|
6
|
-
from pydantic import BaseModel, InstanceOf, Field, UUID4, PrivateAttr, field_validator
|
8
|
+
from pydantic import BaseModel, InstanceOf, Field, UUID4, PrivateAttr, field_validator
|
7
9
|
from pydantic_core import PydanticCustomError
|
8
10
|
|
9
11
|
from versionhq.task.model import Task, TaskOutput
|
10
12
|
from versionhq.agent.model import Agent
|
11
13
|
from versionhq._utils.logger import Logger
|
12
14
|
|
13
|
-
try:
|
14
|
-
import networkx as nx
|
15
|
-
except ImportError:
|
16
|
-
try:
|
17
|
-
import os
|
18
|
-
os.system("uv add networkx --optional networkx")
|
19
|
-
import networkx as nx
|
20
|
-
except:
|
21
|
-
try:
|
22
|
-
import os
|
23
|
-
os.system("pip install network --save")
|
24
|
-
import networkx as nx
|
25
|
-
except:
|
26
|
-
raise ImportError("networkx is not installed. Please install it with: uv add networkx --optional networkx")
|
27
|
-
|
28
|
-
try:
|
29
|
-
import matplotlib.pyplot as plt
|
30
|
-
except ImportError:
|
31
|
-
try:
|
32
|
-
import os
|
33
|
-
os.system("uv add matplotlib --optional matplotlib")
|
34
|
-
import matplotlib.pyplot as plt
|
35
|
-
except:
|
36
|
-
try:
|
37
|
-
import os
|
38
|
-
os.system("pip install matplotlib --save")
|
39
|
-
import matplotlib.pyplot as plt
|
40
|
-
|
41
|
-
except:
|
42
|
-
raise ImportError("matplotlib is not installed. Please install it with: uv add matplotlib --optional matplotlib")
|
43
15
|
|
44
|
-
|
45
|
-
|
46
|
-
|
16
|
+
def gen_network():
|
17
|
+
goal = "make a promo plan to increase gross sales of Temu, an ecommerce site with affordable items."
|
18
|
+
agent = Agent(
|
19
|
+
role="Network Generator", goal="draft a best graph with nodes (tasks) and edges", llm="gemini-2.0", maxit=1,
|
20
|
+
knowledge_sources=["https://en.wikipedia.org/wiki/Graph_theory", "https://www.temu.com", ]
|
21
|
+
)
|
22
|
+
class Outcome(BaseModel):
|
23
|
+
task_descriptions: list[str]
|
47
24
|
|
48
25
|
|
26
|
+
task = Task(
|
27
|
+
description=" 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. ",
|
28
|
+
pydantic_output=Outcome
|
29
|
+
)
|
30
|
+
task = Task(
|
31
|
+
description="Form best network with nodes and edges for the task described as following: Draft a promo plan for the client.",
|
32
|
+
pydantic_output=Edge
|
33
|
+
)
|
34
|
+
res = task.execute(agent=agent)
|
49
35
|
|
50
36
|
class TaskStatus(enum.Enum):
|
51
37
|
"""
|
@@ -71,21 +57,21 @@ class DependencyType(enum.Enum):
|
|
71
57
|
|
72
58
|
|
73
59
|
|
74
|
-
class TriggerEvent(enum.Enum):
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
60
|
+
# class TriggerEvent(enum.Enum):
|
61
|
+
# """
|
62
|
+
# Concise enumeration of key trigger events for task execution.
|
63
|
+
# """
|
64
|
+
# IMMEDIATE = 0 # execute immediately
|
65
|
+
# DEPENDENCIES_MET = 1 # All/required dependencies are satisfied
|
66
|
+
# RESOURCES_AVAILABLE = 2 # Necessary resources are available
|
67
|
+
# SCHEDULED_TIME = 3 # Scheduled start time or time window reached
|
68
|
+
# EXTERNAL_EVENT = 4 # Triggered by an external event/message
|
69
|
+
# DATA_AVAILABLE = 5 # Required data is available both internal/external
|
70
|
+
# APPROVAL_RECEIVED = 6 # Necessary approvals have been granted
|
71
|
+
# STATUS_CHANGED = 7 # Relevant task/system status has changed
|
72
|
+
# RULE_MET = 8 # A predefined rule or condition has been met
|
73
|
+
# MANUAL_TRIGGER = 9 # Manually initiated by a user
|
74
|
+
# ERROR_HANDLED = 10 # A previous error/exception has been handled
|
89
75
|
|
90
76
|
|
91
77
|
|
@@ -96,8 +82,8 @@ class Node(BaseModel):
|
|
96
82
|
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
|
97
83
|
task: InstanceOf[Task] = Field(default=None)
|
98
84
|
# trigger_event: TriggerEvent = Field(default=TriggerEvent.IMMEDIATE, description="store trigger event for starting the task execution")
|
99
|
-
in_degree_nodes: List[Any] = Field(
|
100
|
-
out_degree_nodes: List[Any] = Field(
|
85
|
+
in_degree_nodes: List[Any] = Field(default_factory=list, description="list of Node objects")
|
86
|
+
out_degree_nodes: List[Any] = Field(default_factory=list, description="list of Node objects")
|
101
87
|
assigned_to: InstanceOf[Agent] = Field(default=None)
|
102
88
|
status: TaskStatus = Field(default=TaskStatus.NOT_STARTED)
|
103
89
|
|
@@ -111,22 +97,6 @@ class Node(BaseModel):
|
|
111
97
|
def is_independent(self) -> bool:
|
112
98
|
return not self.in_degree_nodes and not self.out_degree_nodes
|
113
99
|
|
114
|
-
@property
|
115
|
-
def in_degrees(self) -> int:
|
116
|
-
return len(self.in_degree_nodes) if self.in_degree_nodes else 0
|
117
|
-
|
118
|
-
@property
|
119
|
-
def out_degrees(self) -> int:
|
120
|
-
return len(self.out_degree_nodes) if self.out_degree_nodes else 0
|
121
|
-
|
122
|
-
@property
|
123
|
-
def degrees(self) -> int:
|
124
|
-
return self.in_degrees + self.out_degrees
|
125
|
-
|
126
|
-
@property
|
127
|
-
def identifier(self) -> str:
|
128
|
-
"""Unique identifier for the node"""
|
129
|
-
return f"{str(self.id)}"
|
130
100
|
|
131
101
|
def handle_task_execution(self, agent: Agent = None, context: str = None) -> TaskOutput | None:
|
132
102
|
"""
|
@@ -144,15 +114,33 @@ class Node(BaseModel):
|
|
144
114
|
self.status = TaskStatus.COMPLETED if res else TaskStatus.ERROR
|
145
115
|
return res
|
146
116
|
|
117
|
+
|
118
|
+
@property
|
119
|
+
def in_degrees(self) -> int:
|
120
|
+
return len(self.in_degree_nodes) if self.in_degree_nodes else 0
|
121
|
+
|
122
|
+
@property
|
123
|
+
def out_degrees(self) -> int:
|
124
|
+
return len(self.out_degree_nodes) if self.out_degree_nodes else 0
|
125
|
+
|
126
|
+
@property
|
127
|
+
def degrees(self) -> int:
|
128
|
+
return self.in_degrees + self.out_degrees
|
129
|
+
|
130
|
+
@property
|
131
|
+
def identifier(self) -> str:
|
132
|
+
"""Unique identifier for the node"""
|
133
|
+
return f"{str(self.id)}"
|
134
|
+
|
147
135
|
def __str__(self):
|
148
136
|
return self.identifier
|
149
137
|
|
150
138
|
|
151
|
-
|
152
139
|
class Edge(BaseModel):
|
153
140
|
"""
|
154
141
|
A class to store an edge object that connects source and target nodes.
|
155
142
|
"""
|
143
|
+
|
156
144
|
source: Node = Field(default=None)
|
157
145
|
target: Node = Field(default=None)
|
158
146
|
|
@@ -186,28 +174,28 @@ class Edge(BaseModel):
|
|
186
174
|
case DependencyType.FINISH_TO_START:
|
187
175
|
"""target starts after source finishes"""
|
188
176
|
if self.source.status == TaskStatus.COMPLETED:
|
189
|
-
return self.condition(**self.conditon_kwargs) if self.
|
177
|
+
return self.condition(**self.conditon_kwargs) if self.condition else True
|
190
178
|
else:
|
191
179
|
return False
|
192
180
|
|
193
181
|
case DependencyType.START_TO_START:
|
194
182
|
"""target starts when source starts"""
|
195
183
|
if self.source.status != TaskStatus.NOT_STARTED:
|
196
|
-
return self.condition(**self.conditon_kwargs) if self.
|
184
|
+
return self.condition(**self.conditon_kwargs) if self.condition else True
|
197
185
|
else:
|
198
186
|
return False
|
199
187
|
|
200
188
|
case DependencyType.FINISH_TO_FINISH:
|
201
189
|
"""target finish when source start"""
|
202
190
|
if self.source.status != TaskStatus.COMPLETED:
|
203
|
-
return self.condition(**self.conditon_kwargs) if self.
|
191
|
+
return self.condition(**self.conditon_kwargs) if self.condition else True
|
204
192
|
else:
|
205
193
|
return False
|
206
194
|
|
207
195
|
case DependencyType.START_TO_FINISH:
|
208
196
|
"""target finishes when source start"""
|
209
197
|
if self.source.status == TaskStatus.IN_PROGRESS:
|
210
|
-
return self.condition(**self.conditon_kwargs) if self.
|
198
|
+
return self.condition(**self.conditon_kwargs) if self.condition else True
|
211
199
|
else:
|
212
200
|
return False
|
213
201
|
|
@@ -217,19 +205,20 @@ class Edge(BaseModel):
|
|
217
205
|
Activates the edge to initiate task execution of the target node.
|
218
206
|
"""
|
219
207
|
|
220
|
-
if not self.dependency_met():
|
221
|
-
Logger(verbose=True).log(level="warning", message="Dependencies not met. We'll return None.", color="yellow")
|
222
|
-
return None
|
223
|
-
|
224
208
|
if not self.source or not self.target:
|
225
209
|
Logger(verbose=True).log(level="warning", message="Cannot find source or target nodes. We'll return None.", color="yellow")
|
226
210
|
return None
|
227
211
|
|
212
|
+
if not self.dependency_met():
|
213
|
+
Logger(verbose=True).log(level="warning", message="Dependencies not met. We'll see the source node status.", color="yellow")
|
214
|
+
return None
|
215
|
+
|
216
|
+
|
228
217
|
if self.lag:
|
229
218
|
import time
|
230
219
|
time.sleep(self.lag)
|
231
220
|
|
232
|
-
context = self.source.task.
|
221
|
+
context = self.source.task.output.raw if self.data_transfer else None
|
233
222
|
res = self.target.handle_task_execution(context=context)
|
234
223
|
return res
|
235
224
|
|
@@ -239,15 +228,14 @@ class Graph(ABC, BaseModel):
|
|
239
228
|
An abstract class to store G using NetworkX library.
|
240
229
|
"""
|
241
230
|
|
242
|
-
_logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=True))
|
243
231
|
directed: bool = Field(default=False, description="Whether the graph is directed")
|
244
232
|
graph: Type[nx.Graph] = Field(default=None)
|
245
233
|
nodes: Dict[str, InstanceOf[Node]] = Field(default_factory=dict, description="identifier: Node - for the sake of ")
|
246
234
|
edges: Dict[str, InstanceOf[Edge]] = Field(default_factory=dict)
|
247
235
|
|
248
236
|
def __init__(self, directed: bool = False, **kwargs):
|
249
|
-
|
250
|
-
|
237
|
+
super().__init__(directed=directed, **kwargs)
|
238
|
+
self.graph = nx.DiGraph() if self.directed else nx.Graph()
|
251
239
|
|
252
240
|
def _return_node_object(self, node_identifier) -> Node | None:
|
253
241
|
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
|
@@ -259,7 +247,9 @@ class Graph(ABC, BaseModel):
|
|
259
247
|
def add_edge(self, source: str, target: str, edge: Edge) -> None:
|
260
248
|
self.graph.add_edge(source, target, **edge.model_dump())
|
261
249
|
edge.source = self._return_node_object(source)
|
250
|
+
edge.source.out_degree_nodes.append(target)
|
262
251
|
edge.target = self._return_node_object(target)
|
252
|
+
edge.target.in_degree_nodes.append(source)
|
263
253
|
self.edges[(source, target)] = edge
|
264
254
|
|
265
255
|
def add_weighted_edges_from(self, edges):
|
@@ -274,6 +264,28 @@ class Graph(ABC, BaseModel):
|
|
274
264
|
def get_out_degree(self, node: Node) -> int:
|
275
265
|
return self.graph.out_degree(node)
|
276
266
|
|
267
|
+
def find_start_nodes(self) -> Tuple[Node]:
|
268
|
+
return [v for k, v in self.nodes.items() if v.in_degrees == 0 and v.out_degrees > 0]
|
269
|
+
|
270
|
+
def find_end_nodes(self) -> Tuple[Node]:
|
271
|
+
return [v for k, v in self.nodes.items() if v.out_degrees == 0 and v.in_degrees > 0]
|
272
|
+
|
273
|
+
def find_critical_end_node(self) -> Node | None:
|
274
|
+
"""
|
275
|
+
Find a critical end node from all the end nodes to lead a conclusion of the entire graph.
|
276
|
+
"""
|
277
|
+
end_nodes = self.find_end_nodes()
|
278
|
+
if not end_nodes:
|
279
|
+
return None
|
280
|
+
|
281
|
+
if len(end_nodes) == 1:
|
282
|
+
return end_nodes[0]
|
283
|
+
|
284
|
+
edges = [v for k, v in self.edges if isinstance(v, Edge) and v.source in end_nodes]
|
285
|
+
critical_edge = max(edges, key=lambda item: item['weight']) if edges else None
|
286
|
+
return critical_edge.target if critical_edge else None
|
287
|
+
|
288
|
+
|
277
289
|
def find_path(self, source: Optional[str] | None, target: str, weight: Optional[Any] | None) -> Any:
|
278
290
|
try:
|
279
291
|
return nx.shortest_path(self.graph, source=source, target=target, weight=weight)
|
@@ -288,14 +300,14 @@ class Graph(ABC, BaseModel):
|
|
288
300
|
Finds the critical path in the graph.
|
289
301
|
Returns:
|
290
302
|
A tuple containing:
|
291
|
-
- The critical path (a list of
|
303
|
+
- The critical path (a list of edge identifiers).
|
292
304
|
- The duration of the critical path.
|
293
305
|
- A dictionary of all paths and their durations.
|
294
306
|
"""
|
295
307
|
|
296
308
|
all_paths = {}
|
297
|
-
for start_node in
|
298
|
-
for end_node in
|
309
|
+
for start_node in self.find_start_nodes():
|
310
|
+
for end_node in self.find_end_nodes(): # End at nodes with 0 out-degree
|
299
311
|
for edge in nx.all_simple_paths(self.graph, source=start_node.identifier, target=end_node.identifier):
|
300
312
|
edge_weight = sum(self.edges.get(item).weight if self.edges.get(item) else 0 for item in edge)
|
301
313
|
all_paths[tuple(edge)] = edge_weight
|
@@ -347,7 +359,26 @@ class TaskGraph(Graph):
|
|
347
359
|
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
|
348
360
|
should_reform: bool = Field(default=False)
|
349
361
|
status: Dict[str, TaskStatus] = Field(default_factory=dict, description="store identifier (str) and TaskStatus of all task_nodes")
|
350
|
-
outputs: Dict[str, TaskOutput] = Field(default_factory=dict, description="store identifire and TaskOutput")
|
362
|
+
outputs: Dict[str, TaskOutput] = Field(default_factory=dict, description="store node identifire and TaskOutput")
|
363
|
+
conclusion: Any = Field(default=None, description="store the final result of the entire task graph. critical path target/end node")
|
364
|
+
|
365
|
+
|
366
|
+
def _save(self, abs_file_path: str = None) -> None:
|
367
|
+
"""
|
368
|
+
Save the graph image in the local directory.
|
369
|
+
"""
|
370
|
+
|
371
|
+
try:
|
372
|
+
import os
|
373
|
+
project_root = os.path.abspath(os.getcwd())
|
374
|
+
abs_file_path = abs_file_path if abs_file_path else f"{project_root}/uploads"
|
375
|
+
|
376
|
+
os.makedirs(abs_file_path, exist_ok=True)
|
377
|
+
plt.savefig(f"{abs_file_path}/{str(self.id)}.png")
|
378
|
+
|
379
|
+
except Exception as e:
|
380
|
+
Logger().log(level="error", message=f"Failed to save the graph {str(self.id)}: {str(e)}", color="red")
|
381
|
+
|
351
382
|
|
352
383
|
def add_task(self, task: Node | Task) -> Node:
|
353
384
|
"""Convert `task` to a Node object and add it to G"""
|
@@ -365,7 +396,7 @@ class TaskGraph(Graph):
|
|
365
396
|
"""
|
366
397
|
|
367
398
|
if not edge_attributes:
|
368
|
-
|
399
|
+
Logger(verbose=True).log(level="error", message="Edge attributes are missing.", color="red")
|
369
400
|
|
370
401
|
edge = Edge()
|
371
402
|
for k in Edge.model_fields.keys():
|
@@ -382,14 +413,15 @@ class TaskGraph(Graph):
|
|
382
413
|
if identifier in self.status:
|
383
414
|
self.status[identifier] = status
|
384
415
|
else:
|
385
|
-
|
416
|
+
Logger().log(level="warning", message=f"Task '{identifier}' not found in the graph.", color="yellow")
|
386
417
|
pass
|
387
418
|
|
419
|
+
|
388
420
|
def get_task_status(self, identifier):
|
389
421
|
if identifier in self.status:
|
390
422
|
return self.status[identifier]
|
391
423
|
else:
|
392
|
-
|
424
|
+
Logger().log(level="warning", message=f"Task '{identifier}' not found in the graph.", color="yellow")
|
393
425
|
return None
|
394
426
|
|
395
427
|
|
@@ -448,24 +480,6 @@ class TaskGraph(Graph):
|
|
448
480
|
plt.show()
|
449
481
|
|
450
482
|
|
451
|
-
def _save(self, abs_file_path: str = None) -> None:
|
452
|
-
"""
|
453
|
-
Save the graph image in the local directory.
|
454
|
-
"""
|
455
|
-
|
456
|
-
try:
|
457
|
-
import os
|
458
|
-
project_root = os.path.abspath(os.getcwd())
|
459
|
-
abs_file_path = abs_file_path if abs_file_path else f"{project_root}/uploads"
|
460
|
-
|
461
|
-
os.makedirs(abs_file_path, exist_ok=True)
|
462
|
-
|
463
|
-
plt.savefig(f"{abs_file_path}/{str(self.id)}.png")
|
464
|
-
|
465
|
-
except Exception as e:
|
466
|
-
self._logger.log(level="error", message=f"Failed to save the graph {str(self.id)}: {str(e)}", color="red")
|
467
|
-
|
468
|
-
|
469
483
|
def activate(self, target_node_identifier: Optional[str] = None) -> Tuple[TaskOutput | None, Dict[str, TaskOutput]]:
|
470
484
|
"""
|
471
485
|
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.
|
@@ -473,7 +487,7 @@ class TaskGraph(Graph):
|
|
473
487
|
"""
|
474
488
|
if target_node_identifier:
|
475
489
|
if not [k for k in self.nodes.keys() if k == target_node_identifier]:
|
476
|
-
|
490
|
+
Logger().log(level="error", message=f"The node {str(target_node_identifier)} is not in the graph.", color="red")
|
477
491
|
return None
|
478
492
|
|
479
493
|
# find a shortest path to each in-degree node of the node and see if dependency met.
|
@@ -494,18 +508,62 @@ class TaskGraph(Graph):
|
|
494
508
|
return res, self.outputs
|
495
509
|
|
496
510
|
else:
|
497
|
-
if not self.edges or self.nodes:
|
498
|
-
|
511
|
+
if not self.edges or not self.nodes:
|
512
|
+
Logger().log(level="error", message="TaskGraph needs at least 2 nodes and 1 edge to activate. We'll return None.", color="red")
|
499
513
|
return None
|
500
514
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
515
|
+
start_nodes = self.find_start_nodes()
|
516
|
+
end_nodes = self.find_end_nodes()
|
517
|
+
critical_end_node = self.find_critical_end_node()
|
518
|
+
critical_path, _, _ = self.find_critical_path()
|
519
|
+
res = None
|
520
|
+
|
521
|
+
# When all nodes are completed, return the output of the critical end node or end node.
|
522
|
+
if end_nodes and len([node for node in end_nodes if node.status == TaskStatus.COMPLETED]) == len(end_nodes):
|
523
|
+
if critical_end_node:
|
524
|
+
return critical_end_node.task.output, self.outputs
|
525
|
+
|
526
|
+
else:
|
527
|
+
return [v.task.output.raw for k, v in end_nodes.items()][0], self.outputs
|
528
|
+
|
529
|
+
# Else, execute nodes connected with the critical_path
|
530
|
+
elif critical_path:
|
531
|
+
for item in critical_path:
|
532
|
+
edge = [v for k, v in self.edges.items() if item in k]
|
533
|
+
|
534
|
+
if edge:
|
535
|
+
edge = edge[0]
|
505
536
|
|
506
|
-
|
507
|
-
|
508
|
-
self.status.update({ node_identifier: edge.target.status })
|
537
|
+
if edge.target.status == TaskStatus.COMPLETED:
|
538
|
+
res = edge.target.output
|
509
539
|
|
510
|
-
|
511
|
-
|
540
|
+
else:
|
541
|
+
res = edge.activate()
|
542
|
+
node_identifier = edge.target.identifier
|
543
|
+
self.outputs.update({ node_identifier: res })
|
544
|
+
self.status.update({ node_identifier: edge.target.status })
|
545
|
+
|
546
|
+
if not res and start_nodes:
|
547
|
+
for node in start_nodes:
|
548
|
+
res = node.handle_task_execution()
|
549
|
+
self.outputs.update({ node.identifier: res })
|
550
|
+
self.status.update({ node.identifier: node.status })
|
551
|
+
|
552
|
+
# if no critical paths in the graph, simply start from the start nodes.
|
553
|
+
elif start_nodes:
|
554
|
+
for node in start_nodes:
|
555
|
+
res = node.handle_task_execution()
|
556
|
+
self.outputs.update({ node.identifier: res })
|
557
|
+
self.status.update({ node.identifier: node.status })
|
558
|
+
|
559
|
+
|
560
|
+
# if none of above is applicable, try to activate all the edges.
|
561
|
+
else:
|
562
|
+
for k, edge in self.edges.items():
|
563
|
+
res = edge.activate()
|
564
|
+
node_identifier = edge.target.identifier
|
565
|
+
self.outputs.update({ node_identifier: res })
|
566
|
+
self.status.update({ node_identifier: edge.target.status })
|
567
|
+
|
568
|
+
# 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
|
569
|
+
return res, self.outputs
|
versionhq/knowledge/source.py
CHANGED
@@ -265,7 +265,7 @@ class PDFKnowledgeSource(BaseFileKnowledgeSource):
|
|
265
265
|
import os
|
266
266
|
os.system("uv add pdfplumber --optional pdfplumber")
|
267
267
|
except:
|
268
|
-
raise ImportError("pdfplumber is not installed. Please install it with: uv add pdfplumber")
|
268
|
+
raise ImportError("pdfplumber is not installed. Please install it with: uv add pdfplumber --optional pdfplumber")
|
269
269
|
|
270
270
|
|
271
271
|
def add(self) -> None:
|
versionhq/task/formation.py
CHANGED
@@ -68,7 +68,7 @@ def form_agent_network(
|
|
68
68
|
|
69
69
|
vhq_task = Task(
|
70
70
|
description=f"""
|
71
|
-
Create a team of specialized agents designed to automate the following task and deliver the expected outcome. Consider the necessary roles for each agent with a clear task description. If you think we neeed a leader to handle the automation, return a leader_agent role as well, but if not, leave the a leader_agent role blank.
|
71
|
+
Create a team of specialized agents designed to automate the following task and deliver the expected outcome. Consider the necessary roles for each agent with a clear task description. If you think we neeed a leader to handle the automation, return a leader_agent role as well, but if not, leave the a leader_agent role blank. When you have a leader_agent, the formation must be SUPERVISING or HYBRID.
|
72
72
|
Task: {str(task)}
|
73
73
|
Expected outcome: {str(expected_outcome)}
|
74
74
|
Formation: {prompt_formation}
|
@@ -109,7 +109,7 @@ def form_agent_network(
|
|
109
109
|
team_tasks.extend(created_tasks[len(created_agents):len(created_tasks)])
|
110
110
|
|
111
111
|
members.sort(key=lambda x: x.is_manager == False)
|
112
|
-
team = Team(
|
112
|
+
team = Team(members=members, formation=_formation, team_tasks=team_tasks, planner_llm=vhq_formation_planner.llm)
|
113
113
|
return team
|
114
114
|
|
115
115
|
else:
|
versionhq/task/model.py
CHANGED
@@ -149,9 +149,16 @@ class ResponseField(BaseModel):
|
|
149
149
|
return value
|
150
150
|
|
151
151
|
|
152
|
+
def _annotate(self, value: Any) -> Annotated:
|
153
|
+
"""
|
154
|
+
Address Pydantic's `create_model`
|
155
|
+
"""
|
156
|
+
return Annotated[self.type, value] if isinstance(value, self.type) else Annotated[str, str(value)]
|
157
|
+
|
158
|
+
|
152
159
|
def create_pydantic_model(self, result: Dict, base_model: InstanceOf[BaseModel] | Any) -> Any:
|
153
160
|
"""
|
154
|
-
Create a Pydantic model from the given result
|
161
|
+
Create a Pydantic model from the given result.
|
155
162
|
"""
|
156
163
|
for k, v in result.items():
|
157
164
|
if k is not self.title:
|
@@ -164,14 +171,6 @@ class ResponseField(BaseModel):
|
|
164
171
|
return base_model
|
165
172
|
|
166
173
|
|
167
|
-
def _annotate(self, value: Any) -> Annotated:
|
168
|
-
"""
|
169
|
-
Address Pydantic's `create_model`
|
170
|
-
"""
|
171
|
-
return Annotated[self.type, value] if isinstance(value, self.type) else Annotated[str, str(value)]
|
172
|
-
|
173
|
-
|
174
|
-
|
175
174
|
class TaskOutput(BaseModel):
|
176
175
|
"""
|
177
176
|
A class to store the final output of the given task in raw (string), json_dict, and pydantic class formats.
|
versionhq/team/model.py
CHANGED
@@ -27,33 +27,25 @@ def match_type(self, obj):
|
|
27
27
|
|
28
28
|
GenerateSchema.match_type = match_type
|
29
29
|
warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")
|
30
|
-
load_dotenv(override=True)
|
31
|
-
|
32
|
-
# agentops = None
|
33
|
-
# if os.environ.get("AGENTOPS_API_KEY"):
|
34
|
-
# try:
|
35
|
-
# import agentops # type: ignore
|
36
|
-
# except ImportError:
|
37
|
-
# pass
|
38
|
-
|
39
30
|
|
40
31
|
|
41
32
|
class Formation(str, Enum):
|
42
33
|
UNDEFINED = 0
|
43
34
|
SOLO = 1
|
44
35
|
SUPERVISING = 2
|
45
|
-
|
36
|
+
SQUAD = 3
|
46
37
|
RANDOM = 4
|
47
38
|
HYBRID = 10
|
48
39
|
|
49
40
|
|
50
41
|
class TaskHandlingProcess(str, Enum):
|
51
42
|
"""
|
52
|
-
|
43
|
+
A class representing task handling processes to tackle multiple tasks.
|
44
|
+
When the team has multiple tasks that connect with edges, follow the edge conditions.
|
53
45
|
"""
|
54
|
-
|
55
|
-
|
56
|
-
|
46
|
+
SEQUENT = 1
|
47
|
+
HIERARCHY = 2
|
48
|
+
CONSENSUAL = 3 # either from managers or peers or (human) - most likely controlled by edge
|
57
49
|
|
58
50
|
|
59
51
|
class TeamOutput(TaskOutput):
|
@@ -63,19 +55,16 @@ class TeamOutput(TaskOutput):
|
|
63
55
|
|
64
56
|
team_id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="store the team ID that generate the TeamOutput")
|
65
57
|
task_description: str = Field(default=None, description="store initial request (task description) from the client")
|
66
|
-
task_outputs: list[TaskOutput] = Field(default=list, description="store
|
67
|
-
token_usage: UsageMetrics = Field(default=dict, description="processed token summary")
|
68
|
-
|
58
|
+
task_outputs: list[TaskOutput] = Field(default=list, description="store TaskOutput objects of all tasks that the team has executed")
|
59
|
+
# token_usage: UsageMetrics = Field(default=dict, description="processed token summary")
|
69
60
|
|
70
61
|
def return_all_task_outputs(self) -> List[Dict[str, Any]]:
|
71
62
|
res = [output.json_dict for output in self.task_outputs]
|
72
63
|
return res
|
73
64
|
|
74
|
-
|
75
65
|
def __str__(self):
|
76
66
|
return (str(self.pydantic) if self.pydantic else str(self.json_dict) if self.json_dict else self.raw)
|
77
67
|
|
78
|
-
|
79
68
|
def __getitem__(self, key):
|
80
69
|
if self.pydantic and hasattr(self.pydantic, key):
|
81
70
|
return getattr(self.pydantic, key)
|
@@ -88,7 +77,7 @@ class TeamOutput(TaskOutput):
|
|
88
77
|
|
89
78
|
class Member(BaseModel):
|
90
79
|
"""
|
91
|
-
A class to store a member in the
|
80
|
+
A class to store a member in the team and connect the agent as a member with tasks and memory/knowledge share settings.
|
92
81
|
"""
|
93
82
|
agent: Agent | None = Field(default=None)
|
94
83
|
is_manager: bool = Field(default=False)
|
@@ -103,7 +92,8 @@ class Member(BaseModel):
|
|
103
92
|
|
104
93
|
class Team(BaseModel):
|
105
94
|
"""
|
106
|
-
A class to store
|
95
|
+
A class to store a team with members and tasks.
|
96
|
+
Tasks can be 1. multiple individual tasks, 2. multiple dependant tasks connected via Graph, and 3. hybrid.
|
107
97
|
"""
|
108
98
|
|
109
99
|
__hash__ = object.__hash__
|
@@ -123,7 +113,7 @@ class Team(BaseModel):
|
|
123
113
|
|
124
114
|
# task execution rules
|
125
115
|
prompt_file: str = Field(default="", description="absolute path to the prompt json file")
|
126
|
-
process: TaskHandlingProcess = Field(default=TaskHandlingProcess.
|
116
|
+
process: TaskHandlingProcess = Field(default=TaskHandlingProcess.SEQUENT)
|
127
117
|
|
128
118
|
# callbacks
|
129
119
|
pre_launch_callbacks: List[Callable[[Optional[Dict[str, Any]]], Optional[Dict[str, Any]]]] = Field(
|
@@ -174,21 +164,19 @@ class Team(BaseModel):
|
|
174
164
|
|
175
165
|
|
176
166
|
@model_validator(mode="after")
|
177
|
-
def
|
167
|
+
def check_manager(self):
|
178
168
|
"""
|
179
169
|
Check if the team has a manager
|
180
170
|
"""
|
181
|
-
|
182
|
-
if self.process == TaskHandlingProcess.hierarchical or self.formation == Formation.SUPERVISING:
|
171
|
+
if self.process == TaskHandlingProcess.HIERARCHY or self.formation == Formation.SUPERVISING:
|
183
172
|
if not self.managers:
|
184
173
|
self._logger.log(level="error", message="The process or formation created needs at least 1 manager agent.", color="red")
|
185
|
-
raise PydanticCustomError(
|
186
|
-
"missing_manager_llm_or_manager","Attribute `manager_llm` or `manager` is required when using hierarchical process.", {})
|
174
|
+
raise PydanticCustomError("missing_manager", "`manager` is required when using hierarchical process.", {})
|
187
175
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
176
|
+
## comment out for the formation flexibilities
|
177
|
+
# if self.managers and (self.manager_tasks is None or self.team_tasks is None):
|
178
|
+
# self._logger.log(level="error", message="The manager is idling. At least 1 task needs to be assigned to the manager.", color="red")
|
179
|
+
# raise PydanticCustomError("missing_manager_task", "manager needs to have at least one manager task or team task.", {})
|
192
180
|
|
193
181
|
return self
|
194
182
|
|
@@ -198,7 +186,7 @@ class Team(BaseModel):
|
|
198
186
|
"""
|
199
187
|
Sequential task processing without any team tasks require a task-agent pairing.
|
200
188
|
"""
|
201
|
-
if self.process == TaskHandlingProcess.
|
189
|
+
if self.process == TaskHandlingProcess.SEQUENT and self.team_tasks is None:
|
202
190
|
for task in self.tasks:
|
203
191
|
if not [member.task == task for member in self.members]:
|
204
192
|
self._logger.log(level="error", message=f"The following task needs a dedicated agent to be assinged: {task.description}", color="red")
|
@@ -278,31 +266,6 @@ class Team(BaseModel):
|
|
278
266
|
return task_outputs
|
279
267
|
|
280
268
|
|
281
|
-
def _create_team_output(self, task_outputs: List[TaskOutput], lead_task_output: TaskOutput = None) -> TeamOutput:
|
282
|
-
"""
|
283
|
-
Take the output of the first task or the lead task output as the team output `raw` value.
|
284
|
-
Note that `tasks` are already sorted by the importance.
|
285
|
-
"""
|
286
|
-
|
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")
|
290
|
-
|
291
|
-
final_task_output = lead_task_output if lead_task_output is not None else task_outputs[0] #! REFINEME
|
292
|
-
# final_string_output = final_task_output.raw
|
293
|
-
# self._finish_execution(final_string_output)
|
294
|
-
token_usage = self._calculate_usage_metrics()
|
295
|
-
|
296
|
-
return TeamOutput(
|
297
|
-
team_id=self.id,
|
298
|
-
raw=final_task_output.raw,
|
299
|
-
json_dict=final_task_output.json_dict,
|
300
|
-
pydantic=final_task_output.pydantic,
|
301
|
-
task_outputs=task_outputs,
|
302
|
-
token_usage=token_usage,
|
303
|
-
)
|
304
|
-
|
305
|
-
|
306
269
|
def _calculate_usage_metrics(self) -> UsageMetrics:
|
307
270
|
"""
|
308
271
|
Calculate and return the usage metrics that consumed by the team.
|
@@ -371,14 +334,29 @@ class Team(BaseModel):
|
|
371
334
|
lead_task_output = task_output
|
372
335
|
|
373
336
|
task_outputs.append(task_output)
|
374
|
-
# self._process_task_result(task, task_output)
|
375
337
|
task._store_execution_log(task_index, was_replayed, self._inputs)
|
376
338
|
|
377
339
|
|
378
340
|
if futures:
|
379
341
|
task_outputs = self._process_async_tasks(futures, was_replayed)
|
380
342
|
|
381
|
-
|
343
|
+
if not task_outputs:
|
344
|
+
self._logger.log(level="error", message="Missing task outcomes. Failed to launch the task.", color="red")
|
345
|
+
raise ValueError("Failed to launch tasks")
|
346
|
+
|
347
|
+
final_task_output = lead_task_output if lead_task_output is not None else task_outputs[0] #! REFINEME
|
348
|
+
|
349
|
+
# token_usage = self._calculate_usage_metrics() #! combining with Eval
|
350
|
+
|
351
|
+
return TeamOutput(
|
352
|
+
team_id=self.id,
|
353
|
+
raw=final_task_output.raw,
|
354
|
+
json_dict=final_task_output.json_dict,
|
355
|
+
pydantic=final_task_output.pydantic,
|
356
|
+
task_outputs=task_outputs,
|
357
|
+
# token_usage=token_usage,
|
358
|
+
)
|
359
|
+
|
382
360
|
|
383
361
|
|
384
362
|
def launch(self, kwargs_pre: Optional[Dict[str, str]] = None, kwargs_post: Optional[Dict[str, Any]] = None) -> TeamOutput:
|
@@ -412,7 +390,7 @@ class Team(BaseModel):
|
|
412
390
|
agent.callbacks.append(self.step_callback)
|
413
391
|
|
414
392
|
if self.process is None:
|
415
|
-
self.process = TaskHandlingProcess.
|
393
|
+
self.process = TaskHandlingProcess.SEQUENT
|
416
394
|
|
417
395
|
result = self._execute_tasks(self.tasks)
|
418
396
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: versionhq
|
3
|
-
Version: 1.2.0
|
3
|
+
Version: 1.2.1.0
|
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
|
@@ -64,6 +64,8 @@ Requires-Dist: chromadb>=0.6.3
|
|
64
64
|
Requires-Dist: wheel>=0.45.1
|
65
65
|
Requires-Dist: envoy>=0.0.3
|
66
66
|
Requires-Dist: composio-core==0.7.0
|
67
|
+
Requires-Dist: networkx>=3.4.2
|
68
|
+
Requires-Dist: matplotlib>=3.10.0
|
67
69
|
Provides-Extra: docling
|
68
70
|
Requires-Dist: docling>=2.17.0; extra == "docling"
|
69
71
|
Provides-Extra: mem0ai
|
@@ -76,17 +78,13 @@ Provides-Extra: numpy
|
|
76
78
|
Requires-Dist: numpy>=1.26.4; extra == "numpy"
|
77
79
|
Provides-Extra: pygraphviz
|
78
80
|
Requires-Dist: pygraphviz>=1.14; extra == "pygraphviz"
|
79
|
-
Provides-Extra: networkx
|
80
|
-
Requires-Dist: networkx>=3.4.2; extra == "networkx"
|
81
|
-
Provides-Extra: matplotlib
|
82
|
-
Requires-Dist: matplotlib>=3.10.0; extra == "matplotlib"
|
83
81
|
|
84
82
|
# Overview
|
85
83
|
|
86
|
-
[](https://clickpy.clickhouse.com/dashboard/versionhq)
|
87
85
|

|
88
86
|
[](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
|
89
|
-

|
90
88
|

|
91
89
|

|
92
90
|
|
@@ -108,9 +106,10 @@ A Python framework for agentic orchestration that handles complex task automatio
|
|
108
106
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
109
107
|
|
110
108
|
- [Key Features](#key-features)
|
111
|
-
- [Agent
|
109
|
+
- [Agent Network](#agent-network)
|
112
110
|
- [Graph Theory Concept](#graph-theory-concept)
|
113
|
-
- [
|
111
|
+
- [Task Graph](#task-graph)
|
112
|
+
- [Optimization](#optimization)
|
114
113
|
- [Quick Start](#quick-start)
|
115
114
|
- [Package installation](#package-installation)
|
116
115
|
- [Forming a agent network](#forming-a-agent-network)
|
@@ -141,14 +140,14 @@ A Python framework for agentic orchestration that handles complex task automatio
|
|
141
140
|
Agents are model-agnostic, and will improve task output, while oprimizing token cost and job latency, by sharing their memory, knowledge base, and RAG tools with other agents in the network.
|
142
141
|
|
143
142
|
|
144
|
-
### Agent
|
143
|
+
### Agent Network
|
145
144
|
|
146
145
|
Agents adapt their formation based on task complexity.
|
147
146
|
|
148
147
|
You can specify a desired formation or allow the agents to determine it autonomously (default).
|
149
148
|
|
150
149
|
|
151
|
-
| | **Solo Agent** | **Supervising** | **
|
150
|
+
| | **Solo Agent** | **Supervising** | **Squad** | **Random** |
|
152
151
|
| :--- | :--- | :--- | :--- | :--- |
|
153
152
|
| **Formation** | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/rbgxttfoeqqis1ettlfz.png" alt="solo" width="200"> | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/zhungor3elxzer5dum10.png" alt="solo" width="200"> | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/dnusl7iy7kiwkxwlpmg8.png" alt="solo" width="200"> | <img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1738818211/pj_m_agents/sndpczatfzbrosxz9ama.png" alt="solo" width="200"> |
|
154
153
|
| **Usage** | <ul><li>A single agent with tools, knowledge, and memory.</li><li>When self-learning mode is on - it will turn into **Random** formation.</li></ul> | <ul><li>Leader agent gives directions, while sharing its knowledge and memory.</li><li>Subordinates can be solo agents or networks.</li></ul> | <ul><li>Share tasks, knowledge, and memory among network members.</li></ul> | <ul><li>A single agent handles tasks, asking help from other agents without sharing its memory or knowledge.</li></ul> |
|
@@ -194,7 +193,19 @@ task_graph.visualize()
|
|
194
193
|
|
195
194
|
<hr />
|
196
195
|
|
197
|
-
###
|
196
|
+
### Task Graph
|
197
|
+
|
198
|
+
A `TaskGraph` represents tasks as `nodes` and their execution dependencies as `edges`, automating rule-based execution.
|
199
|
+
|
200
|
+
`Agent Networks` can handle `TaskGraph` objects by optimizing their formations.
|
201
|
+
|
202
|
+
The following example demonstrates a simple concept of a `supervising` agent network handling a task graph with three tasks and one critical edge.
|
203
|
+
|
204
|
+
<img src="https://res.cloudinary.com/dfeirxlea/image/upload/v1739337639/pj_m_home/zfg4ccw1m1ww1tpnb0pa.png">
|
205
|
+
|
206
|
+
<hr />
|
207
|
+
|
208
|
+
### Optimization
|
198
209
|
|
199
210
|
Agents are model-agnostic and can handle multiple tasks, leveraging their own and their peers' knowledge sources, memories, and tools.
|
200
211
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
versionhq/__init__.py,sha256=
|
1
|
+
versionhq/__init__.py,sha256=OvDVdnABXYcdjKCYL58tOw2duy59rQ2lxc7e1AoxUjc,2781
|
2
2
|
versionhq/_utils/__init__.py,sha256=dzoZr4cBlh-2QZuPzTdehPUCe9lP1dmRtauD7qTjUaA,158
|
3
3
|
versionhq/_utils/i18n.py,sha256=TwA_PnYfDLA6VqlUDPuybdV9lgi3Frh_ASsb_X8jJo8,1483
|
4
|
-
versionhq/_utils/logger.py,sha256=
|
4
|
+
versionhq/_utils/logger.py,sha256=2qkODR6y8ApOoMjQZVecTboXvCLrMy2v_mTSOnNMKFY,1581
|
5
5
|
versionhq/_utils/process_config.py,sha256=jbPGXK2Kb4iyCugJ3FwRJuU0wL5Trq2x4xFQz2uOyFY,746
|
6
6
|
versionhq/_utils/usage_metrics.py,sha256=NXF18dn5NNvGK7EsQ4AAghpR8ppYOjMx6ABenLLHnmM,1066
|
7
7
|
versionhq/_utils/vars.py,sha256=bZ5Dx_bFKlt3hi4-NNGXqdk7B23If_WaTIju2fiTyPQ,57
|
@@ -20,11 +20,13 @@ versionhq/clients/product/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
20
20
|
versionhq/clients/product/model.py,sha256=3w__pug9XRe4LIm9wX8C8WKqi40r081Eb1q2vWk9UaU,3694
|
21
21
|
versionhq/clients/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
22
|
versionhq/clients/workflow/model.py,sha256=FNftenLLoha0bkivrjId32awLHAkBwIT8iNljdic_bw,6003
|
23
|
+
versionhq/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
|
+
versionhq/graph/model.py,sha256=k4NbNn0D3r3ujfkQHOsfiiEJRxZx7iK5DczBnOyn1ik,23912
|
23
25
|
versionhq/knowledge/__init__.py,sha256=qW7IgssTA4_bFFV9ziOcYRfGjlq1c8bkb-HnfWknpuQ,567
|
24
26
|
versionhq/knowledge/_utils.py,sha256=YWRF8U533cfZes_gZqUvdj-K24MD2ri1R0gjc_aPYyc,402
|
25
27
|
versionhq/knowledge/embedding.py,sha256=KfHc__1THxb5jrg1EMrF-v944RDuIr2hE0l-MtM3Bp0,6826
|
26
28
|
versionhq/knowledge/model.py,sha256=w29mrJv1kiznCh4P4yJMUQxIuyRw1Sk0XYtBXzCxaG4,1786
|
27
|
-
versionhq/knowledge/source.py,sha256=
|
29
|
+
versionhq/knowledge/source.py,sha256=HxlwK7E5qWMknJeswnFCCdTIOB01hV4JrEiMGkp7j1E,13631
|
28
30
|
versionhq/knowledge/source_docling.py,sha256=hhHn3rS4KVsFKEPWcfllM8VxSL86PckZdAHDZNQNOq8,5411
|
29
31
|
versionhq/knowledge/storage.py,sha256=7oxCg3W9mFjYH1YmuH9kFtTbNxquzYFjuUjd_TlsB9E,8170
|
30
32
|
versionhq/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -33,8 +35,6 @@ versionhq/llm/model.py,sha256=wlzDUMEyIOm808d1vzqu9gmbB4ch-s_EUvwFR60gR80,17177
|
|
33
35
|
versionhq/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
36
|
versionhq/memory/contextual_memory.py,sha256=tCsOOAUnfrOL7YiakqGoi3uShzzS870TmGnlGd3z_A4,3556
|
35
37
|
versionhq/memory/model.py,sha256=4wow2O3UuMZ0AbC2NyxddGZac3-_GjNZbK9wsA015NA,8145
|
36
|
-
versionhq/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
-
versionhq/network/model.py,sha256=AfakEac4twHIIextUzrV5S-9qzrpaEEoaku6b9cg74A,20612
|
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
|
@@ -44,14 +44,14 @@ versionhq/storage/task_output_storage.py,sha256=E1t_Fkt78dPYIOl3MP7LfQ8oGtjlzxBu
|
|
44
44
|
versionhq/storage/utils.py,sha256=ByYXPoEIGJYLUqz-DWjbCAnneNrH1otiYbp12SCILpM,747
|
45
45
|
versionhq/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
46
|
versionhq/task/evaluate.py,sha256=WdUgjbZL62XrxyWe5MTz29scfzwmuAHGxJ7GvAB8Fmk,3954
|
47
|
-
versionhq/task/formation.py,sha256=
|
47
|
+
versionhq/task/formation.py,sha256=8YQxGZ7cs7Q-a7UnwheHxXei9LSt1AWpCQmQiN1ZkgI,6423
|
48
48
|
versionhq/task/formatter.py,sha256=N8Kmk9vtrMtBdgJ8J7RmlKNMdZWSmV8O1bDexmCWgU0,643
|
49
49
|
versionhq/task/log_handler.py,sha256=LT7YnO7gcPR9IZS7eRvMjnHh8crMBFtqduxd8dxIbkk,1680
|
50
|
-
versionhq/task/model.py,sha256=
|
50
|
+
versionhq/task/model.py,sha256=aXsFRhpmIyfoYpF8BU2cBU4nh4OsaSxc9D98DJ9auqE,30624
|
51
51
|
versionhq/task/structured_response.py,sha256=4q-hQPu7oMMHHXEzh9YW4SJ7N5eCZ7OfZ65juyl_jCI,5000
|
52
52
|
versionhq/task/TEMPLATES/Description.py,sha256=V-4kh8xpQTKOcDMi2xnuP-fcNk6kuoz1_5tYBlDLQWQ,420
|
53
53
|
versionhq/team/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
|
-
versionhq/team/model.py,sha256=
|
54
|
+
versionhq/team/model.py,sha256=88l2lxlAZGIauRJr607IMbV7jP3FIvveVHrT7O4AVpk,18614
|
55
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
|
@@ -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.2.0.
|
64
|
-
versionhq-1.2.0.
|
65
|
-
versionhq-1.2.0.
|
66
|
-
versionhq-1.2.0.
|
67
|
-
versionhq-1.2.0.
|
63
|
+
versionhq-1.2.1.0.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
|
64
|
+
versionhq-1.2.1.0.dist-info/METADATA,sha256=Ilxj9ZEYzoFzEnzdDE6umFKruSRN3pmcm_D2LvpVi8A,21854
|
65
|
+
versionhq-1.2.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
66
|
+
versionhq-1.2.1.0.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
|
67
|
+
versionhq-1.2.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|