versionhq 1.2.2.0__py3-none-any.whl → 1.2.2.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
@@ -31,7 +31,7 @@ from versionhq.agent_network.formation import form_agent_network
31
31
  from versionhq.task_graph.draft import workflow
32
32
 
33
33
 
34
- __version__ = "1.2.2.0"
34
+ __version__ = "1.2.2.2"
35
35
  __all__ = [
36
36
  "Agent",
37
37
 
@@ -61,7 +61,7 @@ def form_agent_network(
61
61
  # try:
62
62
  prompt_formation = formation.name if formation and isinstance(formation, Formation) else f"Select the best formation to effectively execute the tasks from the given Enum sets: {str(Formation.__dict__)}."
63
63
 
64
- prompt_expected_outcome = expected_outcome if isinstance(expected_outcome, str) else expected_outcome.model_dump_json()
64
+ prompt_expected_outcome = expected_outcome if isinstance(expected_outcome, str) else str(expected_outcome.model_dump()) if type(expected_outcome) == BaseModel else ""
65
65
 
66
66
  class Outcome(BaseModel):
67
67
  formation: Enum
@@ -76,7 +76,7 @@ def form_agent_network(
76
76
  )
77
77
 
78
78
  if agents:
79
- vhq_task.description += "Consider adding following agents in the formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
79
+ vhq_task.description += "You MUST add the following agents' roles in the network formation: " + ", ".join([agent.role for agent in agents if isinstance(agent, Agent)])
80
80
 
81
81
  res = vhq_task.execute(agent=vhq_formation_planner, context=context)
82
82
 
@@ -88,7 +88,17 @@ def form_agent_network(
88
88
  members = []
89
89
  leader = str(res.pydantic.leader_agent) if res.pydantic else str(res.json_dict["leader_agent"])
90
90
 
91
- created_agents = [Agent(role=str(item), goal=str(item)) for item in res.pydantic.agent_roles]
91
+ agent_roles = res.pydantic.agent_roles if res.pydantic else res.json_dict["agent_roles"]
92
+ created_agents = [Agent(role=str(item), goal=str(item)) for item in agent_roles]
93
+
94
+ if agents:
95
+ for i, agent in enumerate(created_agents):
96
+ matches = [item for item in agents if item.role == agent.role]
97
+ if matches:
98
+ created_agents[i] = matches[0]
99
+ else:
100
+ pass
101
+
92
102
  created_tasks = []
93
103
 
94
104
  if res.pydantic:
@@ -255,44 +255,6 @@ class AgentNetwork(BaseModel):
255
255
  return member.agent
256
256
 
257
257
 
258
- # task execution
259
- # def _process_async_tasks(self, futures: List[Tuple[Task, Future[TaskOutput], int]], was_replayed: bool = False) -> List[TaskOutput]:
260
- # """
261
- # When we have `Future` tasks, updated task outputs and task execution logs accordingly.
262
- # """
263
-
264
- # task_outputs: List[TaskOutput] = []
265
-
266
- # for future_task, future, task_index in futures:
267
- # task_output = future.result()
268
- # task_outputs.append(task_output)
269
- # future_task._store_execution_log(task_index, was_replayed)
270
-
271
- # return task_outputs
272
-
273
-
274
- # def _calculate_usage_metrics(self) -> UsageMetrics:
275
- # """
276
- # Calculate and return the usage metrics that consumed by the agent network.
277
- # """
278
- # total_usage_metrics = UsageMetrics()
279
-
280
- # for member in self.members:
281
- # agent = member.agent
282
- # if hasattr(agent, "_token_process"):
283
- # token_sum = agent._token_process.get_summary()
284
- # total_usage_metrics.add_usage_metrics(token_sum)
285
-
286
- # if self.managers:
287
- # for manager in self.managers:
288
- # if hasattr(manager.agent, "_token_process"):
289
- # token_sum = manager.agent._token_process.get_summary()
290
- # total_usage_metrics.add_usage_metrics(token_sum)
291
-
292
- # self.usage_metrics = total_usage_metrics
293
- # return total_usage_metrics
294
-
295
-
296
258
  def _execute_tasks(self, tasks: List[Task], start_index: Optional[int] = None) -> Tuple[TaskOutput, TaskGraph]:
297
259
  """Executes tasks and returns TaskOutput object as concl or latest response in the network."""
298
260
  res, task_graph = None, None
@@ -301,9 +263,10 @@ class AgentNetwork(BaseModel):
301
263
  task = self.tasks[0]
302
264
  responsible_agent = self._get_responsible_agent(task=task)
303
265
  res = task.execute(agent=responsible_agent)
266
+ node = Node(task=task)
267
+ task_graph = TaskGraph(nodes={ node.identifier: node, }, concl=res if res else None)
304
268
  return res, task_graph
305
269
 
306
-
307
270
  nodes = [
308
271
  Node(
309
272
  task=task,
@@ -332,47 +295,6 @@ class AgentNetwork(BaseModel):
332
295
  else:
333
296
  res, _ = task_graph.activate()
334
297
 
335
- # task_outputs: List[TaskOutput] = []
336
- # lead_task_output: TaskOutput = None
337
- # futures: List[Tuple[Task, Future[TaskOutput], int]] = []
338
- # last_sync_output: Optional[TaskOutput] = None
339
-
340
- # for task_index, task in enumerate(tasks):
341
- # if start_index is not None and task_index < start_index:
342
- # if task.output:
343
- # if task.execution_type == TaskExecutionType.ASYNC:
344
- # task_outputs.append(task.output)
345
- # else:
346
- # task_outputs = [task.output]
347
- # last_sync_output = task.output
348
- # continue
349
-
350
- # responsible_agent = self._get_responsible_agent(task=task)
351
-
352
- # ## commented out - this will be handled by node objects
353
- # # if isinstance(task, ConditionalTask):
354
- # # skipped_task_output = task._handle_conditional_task(task_outputs, futures, task_index, was_replayed)
355
- # # if skipped_task_output:
356
- # # continue
357
-
358
- # # self._log_task_start(task, responsible_agent)
359
-
360
- # context = create_raw_outputs(tasks=[task, ], task_outputs=([last_sync_output,] if last_sync_output else []))
361
- # res = task.execute(agent=responsible_agent, context=context)
362
-
363
- # if isinstance(res, Future):
364
- # futures.append((task, res, task_index))
365
- # else:
366
- # # if self.managers and responsible_agent in [manager.agent for manager in self.managers]:
367
- # # lead_task_output = task_output
368
-
369
- # task_outputs.append(res)
370
- # task._store_log(task_index, was_replayed, self._inputs)
371
-
372
-
373
- # if futures:
374
- # task_outputs = self._process_async_tasks(futures, was_replayed)
375
-
376
298
  if not res:
377
299
  Logger().log(level="error", message="Missing task outputs.", color="red")
378
300
  raise ValueError("Missing task outputs")
@@ -380,7 +302,6 @@ class AgentNetwork(BaseModel):
380
302
  return res, task_graph
381
303
 
382
304
 
383
-
384
305
  def launch(
385
306
  self, kwargs_pre: Optional[Dict[str, str]] = None, kwargs_post: Optional[Dict[str, Any]] = None, start_index: int = None
386
307
  ) -> Tuple[TaskOutput, TaskGraph]:
@@ -391,7 +312,7 @@ class AgentNetwork(BaseModel):
391
312
  self._assign_tasks()
392
313
 
393
314
  if kwargs_pre:
394
- for func in self.pre_launch_callbacks: # signature check
315
+ for func in self.pre_launch_callbacks: #! REFINEME - signature check
395
316
  func(**kwargs_pre)
396
317
 
397
318
  for member in self.members:
@@ -420,12 +341,6 @@ class AgentNetwork(BaseModel):
420
341
  case _:
421
342
  pass
422
343
 
423
- # metrics += [member.agent._token_process.get_summary() for member in self.members]
424
-
425
- # self.usage_metrics = UsageMetrics()
426
- # for metric in metrics:
427
- # self.usage_metrics.add_usage_metrics(metric)
428
-
429
344
  return result, tg
430
345
 
431
346
 
@@ -9,10 +9,10 @@ try:
9
9
  from docling_core.transforms.chunker.hierarchical_chunker import HierarchicalChunker
10
10
  from docling_core.types.doc.document import DoclingDocument
11
11
  DOCLING_AVAILABLE = True
12
- except ImportError:
13
- import envoy
14
- envoy.run("uv add docling --optional docling")
15
- DOCLING_AVAILABLE = True
12
+ # except ImportError:
13
+ # import envoy
14
+ # envoy.run("uv add docling --optional docling")
15
+ # DOCLING_AVAILABLE = True
16
16
  except:
17
17
  DOCLING_AVAILABLE = False
18
18
 
@@ -23,25 +23,25 @@ class ContextualMemory:
23
23
  self.um = um
24
24
 
25
25
 
26
- def build_context_for_task(self, query: str = None) -> str:
27
- """
28
- Automatically builds a minimal, highly relevant set of contextual information for a given task.
29
- """
26
+ def _sanitize_query(self, query: str = None) -> str:
30
27
  if not query:
31
28
  return ""
32
29
 
33
- context = []
34
- context.append(self._fetch_stm_context(query))
35
- if self.memory_provider == "mem0":
36
- context.append(self._fetch_user_context(query))
37
- return "\n".join(filter(None, context))
30
+ return query.replace("{", "").replace("}", "")
38
31
 
39
32
 
40
- def _fetch_stm_context(self, query) -> str:
33
+ def _fetch_stm_context(self, query: str = None) -> str:
41
34
  """
42
- Fetches recent relevant insights from STM related to the task's description and expected_output, formatted as bullet points.
35
+ Fetches recent relevant insights from STM
43
36
  """
37
+ if not query:
38
+ return ""
39
+
40
+
44
41
  stm_results = self.stm.search(query)
42
+ if not stm_results:
43
+ return ""
44
+
45
45
  formatted_results = "\n".join(
46
46
  [
47
47
  f"- {result['memory'] if self.memory_provider == 'mem0' else result['context']}"
@@ -51,13 +51,16 @@ class ContextualMemory:
51
51
  return f"Recent Insights:\n{formatted_results}" if stm_results else ""
52
52
 
53
53
 
54
- def _fetch_ltm_context(self, task) -> Optional[str]:
54
+ def _fetch_ltm_context(self, query: str = None) -> Optional[str]:
55
55
  """
56
56
  Fetches historical data or insights from LTM that are relevant to the task's description and expected_output, formatted as bullet points.
57
57
  """
58
- ltm_results = self.ltm.search(task, latest_n=2)
58
+ if not query:
59
+ return ""
60
+
61
+ ltm_results = self.ltm.search(query, latest_n=2)
59
62
  if not ltm_results:
60
- return None
63
+ return ""
61
64
 
62
65
  formatted_results = [suggestion for result in ltm_results for suggestion in result["metadata"]["suggestions"]]
63
66
  formatted_results = list(dict.fromkeys(formatted_results))
@@ -65,10 +68,12 @@ class ContextualMemory:
65
68
  return f"Historical Data:\n{formatted_results}" if ltm_results else ""
66
69
 
67
70
 
68
- def _fetch_user_context(self, query: str) -> str:
71
+ def _fetch_user_context(self, query: str = None) -> str:
69
72
  """
70
73
  Fetches and formats relevant user information from User Memory.
71
74
  """
75
+ if not query:
76
+ return ""
72
77
 
73
78
  user_memories = self.um.search(query)
74
79
  if not user_memories:
@@ -78,6 +83,23 @@ class ContextualMemory:
78
83
  return f"User memories/preferences:\n{formatted_memories}"
79
84
 
80
85
 
86
+ def build_context_for_task(self, query: str = None) -> str:
87
+ """
88
+ Automatically builds a minimal, highly relevant set of contextual information for a given task.
89
+ """
90
+ if not query:
91
+ return ""
92
+
93
+ query = self._sanitize_query(query=query)
94
+
95
+ context = []
96
+ context.append(self._fetch_stm_context(query))
97
+ context.append(self._fetch_ltm_context(query))
98
+ if self.memory_provider == "mem0":
99
+ context.append(self._fetch_user_context(query))
100
+ return "\n".join(filter(None, context))
101
+
102
+
81
103
  # def _fetch_entity_context(self, query) -> str:
82
104
  # """
83
105
  # Fetches relevant entity information from Entity Memory related to the task's description and expected_output,
@@ -97,7 +97,7 @@ class LTMSQLiteStorage:
97
97
  ]
98
98
 
99
99
  except sqlite3.Error as e:
100
- self._logger.log(level="error", message=f"MEMORY ERROR: An error occurred while querying LTM: {e}",color="red")
100
+ self._logger.log(level="error", message=f"MEMORY ERROR: An error occurred while querying LTM: {str(e)}",color="red")
101
101
  return None
102
102
 
103
103
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: versionhq
3
- Version: 1.2.2.0
3
+ Version: 1.2.2.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
@@ -84,7 +84,7 @@ Requires-Dist: pygraphviz>=1.14; extra == "pygraphviz"
84
84
  [![DL](https://img.shields.io/badge/Download-20K+-red)](https://clickpy.clickhouse.com/dashboard/versionhq)
85
85
  ![MIT license](https://img.shields.io/badge/License-MIT-green)
86
86
  [![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)
87
- ![PyPI](https://img.shields.io/badge/PyPI-v1.2.1+-blue)
87
+ ![PyPI](https://img.shields.io/badge/PyPI-v1.2.2+-blue)
88
88
  ![python ver](https://img.shields.io/badge/Python-3.11/3.12-purple)
89
89
  ![pyenv ver](https://img.shields.io/badge/pyenv-2.5.0-orange)
90
90
 
@@ -255,14 +255,14 @@ agent.update(
255
255
  ```python
256
256
  import versionhq as vhq
257
257
 
258
- network = vhq.form_agent_network(
258
+ network = work(
259
259
  task="YOUR AMAZING TASK OVERVIEW",
260
260
  expected_outcome="YOUR OUTCOME EXPECTATION",
261
261
  )
262
- res, _ = network.launch()
262
+ res, tg = network.launch()
263
263
  ```
264
264
 
265
- This will form a network with multiple agents on `Formation` and return `TaskOutput` object with output in JSON, plane text, Pydantic model format with evaluation.
265
+ This will form a agent network with multiple agents on `Formation` and return response in `TaskOutput` object and `TaskGraph` that connects multiple tasks as nodes.
266
266
 
267
267
 
268
268
  ### Executing tasks
@@ -295,7 +295,6 @@ res = task.execute(context="context to consider")
295
295
  assert isinstance(res, vhq.TaskOutput)
296
296
  ```
297
297
 
298
-
299
298
  This will return a `TaskOutput` object that stores response in plane text, JSON, and Pydantic model: `CustomOutput` formats with a callback result, tool output (if given), and evaluation results (if given).
300
299
 
301
300
  ```python
@@ -338,11 +337,13 @@ network =vhq.AgentNetwork(
338
337
  vhq.Member(agent=agent_b, is_manager=True, tasks=[task_2]), # Agent B as a manager
339
338
  ],
340
339
  )
341
- res, _ = network.launch()
340
+ res, tg = network.launch()
342
341
 
343
342
  assert isinstance(res, vhq.TaskOutput)
344
- assert not [item for item in task_1.processed_agents if "vhq-Delegated-Agent" == item]
345
- assert [item for item in task_1.processed_agents if "agent b" == item]
343
+ assert agent_b.key in task_1.processed_agents
344
+ assert agent_b.key in task_2.processed_agents
345
+
346
+ assert isinstance(tg, vhq.TaskGraph)
346
347
  ```
347
348
 
348
349
  This will return a list with dictionaries with keys defined in the `ResponseField` of each task.
@@ -1,4 +1,4 @@
1
- versionhq/__init__.py,sha256=uh8uc3vSkOPeiz6MBNYdjJeCzH_KPMN-x5Xp5MWyOPo,2857
1
+ versionhq/__init__.py,sha256=vGvxfqX6PuindBNVOX_OoewD27MqktESYpdzvLvkX_U,2857
2
2
  versionhq/_utils/__init__.py,sha256=d-vYVcORZKG-kkLe_fzE8VbViDpAk9DDOKe2fVK25ew,178
3
3
  versionhq/_utils/i18n.py,sha256=TwA_PnYfDLA6VqlUDPuybdV9lgi3Frh_ASsb_X8jJo8,1483
4
4
  versionhq/_utils/logger.py,sha256=iHxGjm3BvUo5dHKLU88_pc0Z45wzSHOjyJGQkb7OADk,3255
@@ -13,8 +13,8 @@ versionhq/agent/rpm_controller.py,sha256=grezIxyBci_lDlwAlgWFRyR5KOocXeOhYkgN02d
13
13
  versionhq/agent/TEMPLATES/Backstory.py,sha256=IAhGnnt6VUMe3wO6IzeyZPDNu7XE7Uiu3VEXUreOcKs,532
14
14
  versionhq/agent/TEMPLATES/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  versionhq/agent_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- versionhq/agent_network/formation.py,sha256=7iGw20Dj2sFLAho6yfrdmonAwFcxINBDGFr2RU-Qz3s,7505
17
- versionhq/agent_network/model.py,sha256=93VVOtON6_TEren4FnWfLtUGl_T6DLVA0BhZgY9yhIA,19059
16
+ versionhq/agent_network/formation.py,sha256=CYKNUeKC392wW3leIDIAaGiKADSsumC_vTe0VOnNwRs,7901
17
+ versionhq/agent_network/model.py,sha256=lr63cmH7ecQrfVAtnN44mtijrnOsfKe-8xOVNBXv-3Q,15696
18
18
  versionhq/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  versionhq/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  versionhq/clients/customer/__init__.py,sha256=-YXh1FQfvpfLacK8SUC7bD7Wx_eIEi4yrkCC_cUasFg,217
@@ -28,17 +28,17 @@ versionhq/knowledge/_utils.py,sha256=YWRF8U533cfZes_gZqUvdj-K24MD2ri1R0gjc_aPYyc
28
28
  versionhq/knowledge/embedding.py,sha256=KfHc__1THxb5jrg1EMrF-v944RDuIr2hE0l-MtM3Bp0,6826
29
29
  versionhq/knowledge/model.py,sha256=ixH8n5kLtJEp1nPAFYA0piYm-n0nnFDtWFp0r9YEVAs,1787
30
30
  versionhq/knowledge/source.py,sha256=-hEUPtJUHHMx4rUKtiHl19J8xAMw-WVBw34zwa2jZ08,13630
31
- versionhq/knowledge/source_docling.py,sha256=mg7bgvKePHn2LlA_XzSFCbS0zOo9xfu_aNOf5cEo6c4,5421
31
+ versionhq/knowledge/source_docling.py,sha256=OUdWUZ6CCaddvmOKNYpYVzXCzqy9kwuTqsl0supT6GI,5429
32
32
  versionhq/knowledge/storage.py,sha256=7oxCg3W9mFjYH1YmuH9kFtTbNxquzYFjuUjd_TlsB9E,8170
33
33
  versionhq/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  versionhq/llm/llm_vars.py,sha256=wjQK20cKvph6Vq1v71o4d16zBGcHlwq0bzOT_zWno7w,7041
35
35
  versionhq/llm/model.py,sha256=HIBmf8FYV6-cDbZK1ZBu6z3dmF0ZUbKbCelfwxMlgyY,17177
36
36
  versionhq/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- versionhq/memory/contextual_memory.py,sha256=WeDujcEp4oud30OusXSPPNrMEQP-vGrt1mcfYopQruU,3483
37
+ versionhq/memory/contextual_memory.py,sha256=QEMVvHuEXxY7M6-12S8HhyFKf108KfX8Zzt7paPW048,3882
38
38
  versionhq/memory/model.py,sha256=VQR1229t7GQPMItlGAHLtJrb6LrZfSoRA1DRW4z0SOU,8234
39
39
  versionhq/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  versionhq/storage/base.py,sha256=p-Jas0fXQan_qotnRD6seQxrT2lj-uw9-SmHQhdppcs,355
41
- versionhq/storage/ltm_sqlite_storage.py,sha256=wdUiuwHfJocdk0UGqyrdU4S5Nae1rgsoRNu3LWmGFcI,3951
41
+ versionhq/storage/ltm_sqlite_storage.py,sha256=K0Assani1QfFuq5FMxkcMmRlmLUOvw-RNkKyMV70U3g,3956
42
42
  versionhq/storage/mem0_storage.py,sha256=ZY8MELBWaINRv9YuRW5MxH7dj2cII-L0i3xSD6o1-2M,3781
43
43
  versionhq/storage/rag_storage.py,sha256=bS2eE874obarYl-4hT6ZWYWTRsqtfuGpKgKzERmM6Uo,7433
44
44
  versionhq/storage/task_output_storage.py,sha256=M8vInLJ5idGAq17w1juHKXtyPyF-B-rK_P8UcqD-Px8,5357
@@ -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.2.0.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
64
- versionhq-1.2.2.0.dist-info/METADATA,sha256=dEEdYYA25P_WeaIrGwXDKdXmlWSgWcl-pMGEleVuaU0,22059
65
- versionhq-1.2.2.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
66
- versionhq-1.2.2.0.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
67
- versionhq-1.2.2.0.dist-info/RECORD,,
63
+ versionhq-1.2.2.2.dist-info/LICENSE,sha256=cRoGGdM73IiDs6nDWKqPlgSv7aR4n-qBXYnJlCMHCeE,1082
64
+ versionhq-1.2.2.2.dist-info/METADATA,sha256=XZ5QDEnQP1G4U-VFHBsGKBPvVc4vqZMafEp6rZvj89s,22015
65
+ versionhq-1.2.2.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
66
+ versionhq-1.2.2.2.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
67
+ versionhq-1.2.2.2.dist-info/RECORD,,