versionhq 1.1.7.9__tar.gz → 1.1.8__tar.gz

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.
Files changed (71) hide show
  1. {versionhq-1.1.7.9 → versionhq-1.1.8}/PKG-INFO +1 -1
  2. {versionhq-1.1.7.9 → versionhq-1.1.8}/pyproject.toml +1 -1
  3. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/__init__.py +1 -1
  4. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/storage/task_output_storage.py +19 -22
  5. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/task/model.py +5 -9
  6. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/team/model.py +3 -4
  7. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq.egg-info/PKG-INFO +1 -1
  8. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/task/task_test.py +7 -0
  9. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/team/team_test.py +42 -26
  10. {versionhq-1.1.7.9 → versionhq-1.1.8}/uv.lock +1 -1
  11. {versionhq-1.1.7.9 → versionhq-1.1.8}/.github/workflows/publish.yml +0 -0
  12. {versionhq-1.1.7.9 → versionhq-1.1.8}/.github/workflows/publish_testpypi.yml +0 -0
  13. {versionhq-1.1.7.9 → versionhq-1.1.8}/.github/workflows/run_tests.yml +0 -0
  14. {versionhq-1.1.7.9 → versionhq-1.1.8}/.github/workflows/security_check.yml +0 -0
  15. {versionhq-1.1.7.9 → versionhq-1.1.8}/.gitignore +0 -0
  16. {versionhq-1.1.7.9 → versionhq-1.1.8}/.pre-commit-config.yaml +0 -0
  17. {versionhq-1.1.7.9 → versionhq-1.1.8}/.python-version +0 -0
  18. {versionhq-1.1.7.9 → versionhq-1.1.8}/LICENSE +0 -0
  19. {versionhq-1.1.7.9 → versionhq-1.1.8}/README.md +0 -0
  20. {versionhq-1.1.7.9 → versionhq-1.1.8}/SECURITY.md +0 -0
  21. {versionhq-1.1.7.9 → versionhq-1.1.8}/db/preprocess.py +0 -0
  22. {versionhq-1.1.7.9 → versionhq-1.1.8}/requirements-dev.txt +0 -0
  23. {versionhq-1.1.7.9 → versionhq-1.1.8}/requirements.txt +0 -0
  24. {versionhq-1.1.7.9 → versionhq-1.1.8}/runtime.txt +0 -0
  25. {versionhq-1.1.7.9 → versionhq-1.1.8}/setup.cfg +0 -0
  26. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/_utils/__init__.py +0 -0
  27. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/_utils/cache_handler.py +0 -0
  28. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/_utils/i18n.py +0 -0
  29. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/_utils/logger.py +0 -0
  30. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/_utils/process_config.py +0 -0
  31. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/_utils/rpm_controller.py +0 -0
  32. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/_utils/usage_metrics.py +0 -0
  33. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/agent/TEMPLATES/Backstory.py +0 -0
  34. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/agent/TEMPLATES/__init__.py +0 -0
  35. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/agent/__init__.py +0 -0
  36. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/agent/model.py +0 -0
  37. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/agent/parser.py +0 -0
  38. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/cli/__init__.py +0 -0
  39. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/clients/__init__.py +0 -0
  40. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/clients/customer/__init__.py +0 -0
  41. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/clients/customer/model.py +0 -0
  42. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/clients/product/__init__.py +0 -0
  43. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/clients/product/model.py +0 -0
  44. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/clients/workflow/__init__.py +0 -0
  45. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/clients/workflow/model.py +0 -0
  46. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/llm/__init__.py +0 -0
  47. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/llm/llm_vars.py +0 -0
  48. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/llm/model.py +0 -0
  49. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/storage/__init__.py +0 -0
  50. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/task/__init__.py +0 -0
  51. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/task/formatter.py +0 -0
  52. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/task/log_handler.py +0 -0
  53. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/team/__init__.py +0 -0
  54. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/team/team_planner.py +0 -0
  55. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/tool/__init__.py +0 -0
  56. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/tool/decorator.py +0 -0
  57. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/tool/model.py +0 -0
  58. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq/tool/tool_handler.py +0 -0
  59. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq.egg-info/SOURCES.txt +0 -0
  60. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq.egg-info/dependency_links.txt +0 -0
  61. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq.egg-info/requires.txt +0 -0
  62. {versionhq-1.1.7.9 → versionhq-1.1.8}/src/versionhq.egg-info/top_level.txt +0 -0
  63. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/__init__.py +0 -0
  64. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/agent/__init__.py +0 -0
  65. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/agent/agent_test.py +0 -0
  66. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/cli/__init__.py +0 -0
  67. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/clients/workflow_test.py +0 -0
  68. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/conftest.py +0 -0
  69. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/task/__init__.py +0 -0
  70. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/team/Prompts/Demo_test.py +0 -0
  71. {versionhq-1.1.7.9 → versionhq-1.1.8}/tests/team/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: versionhq
3
- Version: 1.1.7.9
3
+ Version: 1.1.8
4
4
  Summary: LLM orchestration frameworks for model-agnostic AI agents that handle complex outbound workflows
5
5
  Author-email: Kuriko Iwai <kuriko@versi0n.io>
6
6
  License: MIT License
@@ -15,7 +15,7 @@ exclude = ["test*", "__pycache__"]
15
15
 
16
16
  [project]
17
17
  name = "versionhq"
18
- version = "1.1.7.9"
18
+ version = "1.1.8"
19
19
  authors = [{ name = "Kuriko Iwai", email = "kuriko@versi0n.io" }]
20
20
  description = "LLM orchestration frameworks for model-agnostic AI agents that handle complex outbound workflows"
21
21
  readme = "README.md"
@@ -17,7 +17,7 @@ from versionhq.team.model import Team, TeamOutput
17
17
  from versionhq.tool.model import Tool
18
18
 
19
19
 
20
- __version__ = "1.1.7.9"
20
+ __version__ = "1.1.8"
21
21
  __all__ = [
22
22
  "Agent",
23
23
  "Customer",
@@ -11,11 +11,9 @@ from versionhq._utils.logger import Logger
11
11
 
12
12
  load_dotenv(override=True)
13
13
 
14
-
15
14
  def fetch_db_storage_path():
16
- project_directory_name = os.environ.get("STORAGE_DIR", Path.cwd().name)
17
- app_author = "versionhq"
18
- data_dir = Path(appdirs.user_data_dir(project_directory_name, app_author))
15
+ directory_name = Path.cwd().name
16
+ data_dir = Path(appdirs.user_data_dir(appname=directory_name, appauthor="Version IO Sdn Bhd.", version=None, roaming=False))
19
17
  data_dir.mkdir(parents=True, exist_ok=True)
20
18
  return data_dir
21
19
 
@@ -39,25 +37,25 @@ class TaskOutputSQLiteStorage:
39
37
  Initializes the SQLite database and creates LTM table.
40
38
  """
41
39
 
42
- try:
43
- with sqlite3.connect(self.db_path) as conn:
44
- cursor = conn.cursor()
45
- cursor.execute(
46
- """
47
- CREATE TABLE IF NOT EXISTS task_outputs (
48
- task_id TEXT PRIMARY KEY,
49
- output JSON,
50
- task_index INTEGER,
51
- inputs JSON,
52
- was_replayed BOOLEAN,
53
- timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
54
- )
40
+ # try:
41
+ with sqlite3.connect(self.db_path) as conn:
42
+ cursor = conn.cursor()
43
+ cursor.execute(
55
44
  """
45
+ CREATE TABLE IF NOT EXISTS task_outputs (
46
+ task_id TEXT PRIMARY KEY,
47
+ output JSON,
48
+ task_index INTEGER,
49
+ inputs JSON,
50
+ was_replayed BOOLEAN,
51
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
56
52
  )
57
- conn.commit()
53
+ """
54
+ )
55
+ conn.commit()
58
56
 
59
- except sqlite3.Error as e:
60
- self._logger.log(level="error", message=f"DATABASE INITIALIZATION ERROR: {e}", color="red")
57
+ # except sqlite3.Error as e:
58
+ # self._logger.log(level="error", message=f"DATABASE INITIALIZATION ERROR: {e}", color="red")
61
59
 
62
60
 
63
61
  def add(self, task, output: Dict[str, Any], task_index: int, was_replayed: bool = False, inputs: Dict[str, Any] = {}):
@@ -81,13 +79,12 @@ class TaskOutputSQLiteStorage:
81
79
  try:
82
80
  with sqlite3.connect(self.db_path) as conn:
83
81
  cursor = conn.cursor()
84
-
85
82
  fields, values = [], []
86
83
  for k, v in kwargs.items():
87
84
  fields.append(f"{k} = ?")
88
85
  values.append(json.dumps(v) if isinstance(v, dict) else v)
89
86
 
90
- query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?" # nosec
87
+ query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?"
91
88
  values.append(task_index)
92
89
  cursor.execute(query, tuple(values))
93
90
  conn.commit()
@@ -206,10 +206,10 @@ Your outputs MUST adhere to the following format and should NOT include any irre
206
206
  @property
207
207
  def summary(self) -> str:
208
208
  return f"""
209
- Task: {self.id} - {self.description}
210
- "task_description": {self.description}
211
- "task_expected_output": {self.output_prompt}
212
- "task_tools": {", ".join([tool_called.tool.name for tool_called in self.tools_called])}
209
+ Task ID: {str(self.id)}
210
+ "Description": {self.description}
211
+ "Prompt": {self.output_prompt}
212
+ "Tools": {", ".join([tool_called.tool.name for tool_called in self.tools_called])}
213
213
  """
214
214
 
215
215
 
@@ -382,11 +382,7 @@ Your outputs MUST adhere to the following format and should NOT include any irre
382
382
  """
383
383
 
384
384
  future: Future[TaskOutput] = Future()
385
- threading.Thread(
386
- daemon=True,
387
- target=self._execute_task_async,
388
- args=(agent, context, tools, future),
389
- ).start()
385
+ threading.Thread(daemon=True, target=self._execute_task_async, args=(agent, context, tools, future)).start()
390
386
  return future
391
387
 
392
388
 
@@ -123,7 +123,7 @@ class Team(BaseModel):
123
123
  __hash__ = object.__hash__
124
124
  _execution_span: Any = PrivateAttr()
125
125
  _logger: Logger = PrivateAttr()
126
- # _inputs: Optional[Dict[str, Any]] = PrivateAttr(default=None)
126
+ _inputs: Optional[Dict[str, Any]] = PrivateAttr(default=None)
127
127
 
128
128
  id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
129
129
  name: Optional[str] = Field(default=None)
@@ -382,14 +382,13 @@ class Team(BaseModel):
382
382
  futures.append((task, future, task_index))
383
383
  else:
384
384
  context = create_raw_outputs(tasks=[task,], task_outputs=([last_sync_output,] if last_sync_output else [] ))
385
- task_output = task.execute_sync(agent=responsible_agent, context=context, tools=responsible_agent.tools
386
- )
385
+ task_output = task.execute_sync(agent=responsible_agent, context=context, tools=responsible_agent.tools)
387
386
  if responsible_agent is self.manager_agent:
388
387
  lead_task_output = task_output
389
388
 
390
389
  task_outputs.append(task_output)
391
390
  # self._process_task_result(task, task_output)
392
- task._store_execution_log(task_index, was_replayed)
391
+ task._store_execution_log(task_index, was_replayed, self._inputs)
393
392
 
394
393
 
395
394
  if futures:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: versionhq
3
- Version: 1.1.7.9
3
+ Version: 1.1.8
4
4
  Summary: LLM orchestration frameworks for model-agnostic AI agents that handle complex outbound workflows
5
5
  Author-email: Kuriko Iwai <kuriko@versi0n.io>
6
6
  License: MIT License
@@ -277,4 +277,11 @@ def test_conditional_task():
277
277
  assert conditional_res.task_id is conditional_task.id
278
278
 
279
279
 
280
+ def test_store_task_log():
281
+ task = Task(
282
+ description="Analyze the client's business model.",
283
+ output_field_list=[ResponseField(title="task_1", type=str, required=True),],
284
+ )
285
+ assert task._task_output_handler.load() is not None
286
+
280
287
  # tools, token usage
@@ -127,28 +127,17 @@ def test_form_team_without_leader():
127
127
  assert item.id is task_1.id or item.id is task_2.id
128
128
 
129
129
 
130
- def test_kickoff_team_without_leader():
131
- agent_a = Agent(
132
- role="agent a",
133
- goal="My amazing goals",
134
- llm=MODEL_NAME
135
- )
136
-
137
- agent_b = Agent(
138
- role="agent b",
139
- goal="My amazing goals",
140
- llm=MODEL_NAME
141
- )
142
-
130
+ def test_kickoff_without_leader():
131
+ agent_a = Agent(role="agent a", goal="My amazing goals", llm=MODEL_NAME)
132
+ agent_b = Agent(role="agent b", goal="My amazing goals", llm=MODEL_NAME)
143
133
  task_1 = Task(
144
- description="Analyze the client's business model.",
145
- expected_output_json=True,
146
- output_field_list=[
147
- ResponseField(title="test1", type=str, required=True),
148
- ResponseField(title="test2", type=list, required=True),
149
- ],
134
+ description="Analyze the client's business model.",
135
+ expected_output_json=True,
136
+ output_field_list=[
137
+ ResponseField(title="test1", type=str, required=True),
138
+ ResponseField(title="test2", type=list, required=True),
139
+ ],
150
140
  )
151
-
152
141
  task_2 = Task(
153
142
  description="Define the cohort.",
154
143
  expected_output_json=True,
@@ -157,7 +146,6 @@ def test_kickoff_team_without_leader():
157
146
  ResponseField(title="test2", type=list, required=True),
158
147
  ],
159
148
  )
160
-
161
149
  team = Team(
162
150
  members=[
163
151
  TeamMember(agent=agent_a, is_manager=False, task=task_1),
@@ -185,7 +173,6 @@ def test_kickoff_team_without_leader():
185
173
  assert res.token_usage.total_tokens == 0 # as we dont set token usage on agent
186
174
 
187
175
 
188
-
189
176
  def team_kickoff_with_task_callback():
190
177
  """
191
178
  Each task has callback with callback kwargs.
@@ -236,7 +223,6 @@ def team_kickoff_with_task_callback():
236
223
  assert "pytest" in demo_list[1]
237
224
 
238
225
 
239
-
240
226
  def test_delegate_in_team():
241
227
  """
242
228
  When the agent belongs to the team, the team manager or peers are prioritized to delegete the task.
@@ -279,7 +265,37 @@ def test_delegate_in_team():
279
265
  assert "agent b" in task_1.processed_by_agents
280
266
 
281
267
 
268
+ def test_kickoff_with_leader():
269
+ agent_a = Agent(role="agent a", goal="My amazing goals", llm=MODEL_NAME)
270
+ agent_b = Agent(role="agent b", goal="My amazing goals", llm=MODEL_NAME)
271
+ task_1 = Task(
272
+ description="Analyze the client's business model.",
273
+ output_field_list=[ResponseField(title="task_1", type=str, required=True),],
274
+ )
275
+ task_2 = Task(
276
+ description="Define the cohort timeframe.",
277
+ output_field_list=[
278
+ ResponseField(title="task_2_1", type=int, required=True),
279
+ ResponseField(title="task_2_2", type=list, required=True),
280
+ ],
281
+ )
282
+ team = Team(
283
+ members=[
284
+ TeamMember(agent=agent_a, is_manager=False, task=task_1),
285
+ TeamMember(agent=agent_b, is_manager=True, task=task_2),
286
+ ],
287
+ )
288
+ res = team.kickoff()
289
+
290
+ assert isinstance(res, TeamOutput)
291
+ assert res.team_id is team.id
292
+ assert res.raw is not None
293
+ assert res.json_dict is not None
294
+ assert team.manager_agent.id is agent_b.id
295
+ assert len(res.task_output_list) == 2
296
+ assert [item.raw is not None for item in res.task_output_list]
297
+ assert len(team.tasks) == 2
298
+ assert team.tasks[0].output.raw == res.raw
299
+
282
300
 
283
- if __name__ == "__main__":
284
- test_kickoff_team_without_leader()
285
- # kickoff with teamleader, async, task handling process
301
+ # async, task handling process
@@ -1644,7 +1644,7 @@ wheels = [
1644
1644
 
1645
1645
  [[package]]
1646
1646
  name = "versionhq"
1647
- version = "1.1.7.8"
1647
+ version = "1.1.8"
1648
1648
  source = { editable = "." }
1649
1649
  dependencies = [
1650
1650
  { name = "appdirs" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes