versionhq 1.1.10.3__tar.gz → 1.1.10.5__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 (81) hide show
  1. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/PKG-INFO +1 -1
  2. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/pyproject.toml +1 -1
  3. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/__init__.py +1 -1
  4. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/agent/model.py +3 -10
  5. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/llm/model.py +1 -2
  6. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/task/model.py +5 -3
  7. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/task/structured_response.py +5 -7
  8. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq.egg-info/PKG-INFO +1 -1
  9. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/agent/agent_test.py +4 -8
  10. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/task/task_test.py +47 -4
  11. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/.github/workflows/publish.yml +0 -0
  12. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/.github/workflows/publish_testpypi.yml +0 -0
  13. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/.github/workflows/run_tests.yml +0 -0
  14. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/.github/workflows/security_check.yml +0 -0
  15. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/.gitignore +0 -0
  16. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/.pre-commit-config.yaml +0 -0
  17. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/.python-version +0 -0
  18. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/LICENSE +0 -0
  19. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/README.md +0 -0
  20. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/SECURITY.md +0 -0
  21. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/db/preprocess.py +0 -0
  22. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/requirements-dev.txt +0 -0
  23. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/requirements.txt +0 -0
  24. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/runtime.txt +0 -0
  25. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/setup.cfg +0 -0
  26. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/_utils/__init__.py +0 -0
  27. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/_utils/i18n.py +0 -0
  28. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/_utils/logger.py +0 -0
  29. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/_utils/process_config.py +0 -0
  30. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/_utils/rpm_controller.py +0 -0
  31. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/_utils/usage_metrics.py +0 -0
  32. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/agent/TEMPLATES/Backstory.py +0 -0
  33. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/agent/TEMPLATES/__init__.py +0 -0
  34. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/agent/__init__.py +0 -0
  35. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/agent/parser.py +0 -0
  36. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/cli/__init__.py +0 -0
  37. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/clients/__init__.py +0 -0
  38. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/clients/customer/__init__.py +0 -0
  39. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/clients/customer/model.py +0 -0
  40. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/clients/product/__init__.py +0 -0
  41. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/clients/product/model.py +0 -0
  42. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/clients/workflow/__init__.py +0 -0
  43. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/clients/workflow/model.py +0 -0
  44. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/llm/__init__.py +0 -0
  45. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/llm/llm_vars.py +0 -0
  46. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/storage/__init__.py +0 -0
  47. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/storage/task_output_storage.py +0 -0
  48. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/task/__init__.py +0 -0
  49. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/task/formatter.py +0 -0
  50. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/task/log_handler.py +0 -0
  51. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/team/__init__.py +0 -0
  52. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/team/model.py +0 -0
  53. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/team/team_planner.py +0 -0
  54. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/tool/__init__.py +0 -0
  55. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/tool/cache_handler.py +0 -0
  56. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/tool/composio_tool.py +0 -0
  57. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/tool/composio_tool_vars.py +0 -0
  58. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/tool/decorator.py +0 -0
  59. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/tool/model.py +0 -0
  60. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq/tool/tool_handler.py +0 -0
  61. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq.egg-info/SOURCES.txt +0 -0
  62. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq.egg-info/dependency_links.txt +0 -0
  63. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq.egg-info/requires.txt +0 -0
  64. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/src/versionhq.egg-info/top_level.txt +0 -0
  65. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/__init__.py +0 -0
  66. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/agent/__init__.py +0 -0
  67. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/cli/__init__.py +0 -0
  68. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/clients/customer_test.py +0 -0
  69. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/clients/product_test.py +0 -0
  70. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/clients/workflow_test.py +0 -0
  71. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/conftest.py +0 -0
  72. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/llm/__init__.py +0 -0
  73. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/llm/llm_test.py +0 -0
  74. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/task/__init__.py +0 -0
  75. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/team/Prompts/Demo_test.py +0 -0
  76. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/team/__init__.py +0 -0
  77. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/team/team_test.py +0 -0
  78. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/tool/__init__.py +0 -0
  79. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/tool/composio_test.py +0 -0
  80. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/tests/tool/tool_test.py +0 -0
  81. {versionhq-1.1.10.3 → versionhq-1.1.10.5}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: versionhq
3
- Version: 1.1.10.3
3
+ Version: 1.1.10.5
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.10.3"
18
+ version = "1.1.10.5"
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"
@@ -18,7 +18,7 @@ from versionhq.tool.model import Tool
18
18
  from versionhq.tool.composio_tool import ComposioHandler
19
19
 
20
20
 
21
- __version__ = "1.1.10.3"
21
+ __version__ = "1.1.10.5"
22
22
  __all__ = [
23
23
  "Agent",
24
24
  "Customer",
@@ -255,9 +255,9 @@ class Agent(BaseModel):
255
255
  llm.timeout = self.max_execution_time if llm.timeout is None else llm.timeout
256
256
  llm.max_tokens = self.max_tokens if self.max_tokens else llm.max_tokens
257
257
 
258
- # if self.callbacks:
259
- # llm.callbacks = self.callbacks
260
- # llm._set_callbacks(llm.callbacks)
258
+ if self.callbacks:
259
+ llm.callbacks = self.callbacks
260
+ llm._set_callbacks(llm.callbacks)
261
261
 
262
262
  if self.respect_context_window == False:
263
263
  llm.context_window_size = DEFAULT_CONTEXT_WINDOW_SIZE
@@ -364,9 +364,6 @@ class Agent(BaseModel):
364
364
  task_execution_counter += 1
365
365
  self._logger.log(level="info", message=f"Agent response: {raw_response}", color="blue")
366
366
 
367
- if raw_response and self.callbacks:
368
- for item in self.callbacks:
369
- raw_response = item(raw_response)
370
367
 
371
368
  except Exception as e:
372
369
  self._logger.log(level="error", message=f"An error occured. The agent will retry: {str(e)}", color="red")
@@ -379,10 +376,6 @@ class Agent(BaseModel):
379
376
  task_execution_counter += 1
380
377
  self._logger.log(level="info", message=f"Agent #{task_execution_counter} response: {raw_response}", color="blue")
381
378
 
382
- if raw_response and self.callbacks:
383
- for item in self.callbacks:
384
- raw_response = item(raw_response)
385
-
386
379
  if not raw_response:
387
380
  self._logger.log(level="error", message="Received None or empty response from the model", color="red")
388
381
  raise ValueError("Invalid response from LLM call - None or empty.")
@@ -200,7 +200,7 @@ class LLM(BaseModel):
200
200
 
201
201
  with suppress_warnings():
202
202
  if len(self.callbacks) > 0:
203
- self._set_callbacks(self.callbacks)
203
+ self._set_callbacks(self.callbacks) # passed by agent
204
204
 
205
205
  try:
206
206
  if tools:
@@ -261,7 +261,6 @@ class LLM(BaseModel):
261
261
  return tool_res
262
262
 
263
263
  else:
264
- print(messages)
265
264
  res = litellm.completion(messages=messages, stream=False, **params)
266
265
 
267
266
  return res["choices"][0]["message"]["content"]
@@ -169,6 +169,7 @@ class TaskOutput(BaseModel):
169
169
  json_dict: Dict[str, Any] = Field(default=None, description="`raw` converted to dictionary")
170
170
  pydantic: Optional[Any] = Field(default=None)
171
171
  tool_output: Optional[Any] = Field(default=None, description="store tool result when the task takes tool output as its final output")
172
+ callback_output: Optional[Any] = Field(default=None, description="store task or agent callback outcome")
172
173
 
173
174
  def __str__(self) -> str:
174
175
  return str(self.pydantic) if self.pydantic else str(self.json_dict) if self.json_dict else self.raw
@@ -243,7 +244,7 @@ class Task(BaseModel):
243
244
  # execution rules
244
245
  allow_delegation: bool = Field(default=False, description="ask other agents for help and run the task instead")
245
246
  async_execution: bool = Field(default=False,description="whether the task should be executed asynchronously or not")
246
- callback: Optional[Any] = Field(default=None, description="callback to be executed after the task is completed.")
247
+ callback: Optional[Callable] = Field(default=None, description="callback to be executed after the task is completed.")
247
248
  callback_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict, description="kwargs for the callback when the callback is callable")
248
249
 
249
250
  # recording
@@ -574,8 +575,9 @@ Ref. Output image: {output_formats_to_follow}
574
575
  self.output = task_output
575
576
  self.processed_by_agents.add(agent.role)
576
577
 
577
- if self.callback:
578
- self.callback({ **self.callback_kwargs, **self.output.__dict__ })
578
+ if self.callback and isinstance(self.callback, Callable):
579
+ callback_res = self.callback(**self.callback_kwargs, **task_output.json_dict)
580
+ task_output.callback_output = callback_res
579
581
 
580
582
  # if self.output_file: ## disabled for now
581
583
  # content = (
@@ -1,4 +1,3 @@
1
- #! FIXME
2
1
  from typing import Dict, Optional, Type, List, Any, TypeVar
3
2
 
4
3
  from pydantic import BaseModel, Field, InstanceOf
@@ -92,12 +91,13 @@ class StructuredList:
92
91
 
93
92
  elif nested_object_type == list:
94
93
  props.update({
95
- "nest": {
94
+ # "nest": {
96
95
  "type": "array",
97
- "items": { "item": { "type": "string" } }, #! REFINEME - field title <>`item`
98
- }})
96
+ "items": { "type": "string" } , #! REFINEME - field title <>`item`
97
+ # }
98
+ })
99
99
  else:
100
- props.update({ "nest": { "type": SchemaType(nested_object_type).convert() }})
100
+ props.update({ "type": SchemaType(nested_object_type).convert() })
101
101
 
102
102
  self.items = { **props }
103
103
  return {
@@ -109,8 +109,6 @@ class StructuredList:
109
109
  }
110
110
 
111
111
 
112
-
113
-
114
112
  class StructuredOutput(BaseModel):
115
113
  response_format: Any = None
116
114
  provider: str = "openai"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: versionhq
3
- Version: 1.1.10.3
3
+ Version: 1.1.10.5
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
@@ -122,7 +122,7 @@ def test_build_agent_with_llm_config():
122
122
  assert agent.llm.max_tokens == 4000
123
123
  assert agent.llm.logprobs == False
124
124
  assert [hasattr(agent.llm, k) and v for k, v in llm_config.items() if v is not None]
125
- assert agent.llm.callbacks == []
125
+ assert agent.llm.callbacks == [dummy_func]
126
126
 
127
127
 
128
128
  def test_build_agent_with_llm_instance():
@@ -142,7 +142,7 @@ def test_build_agent_with_llm_instance():
142
142
  assert agent.llm.api_key is not None
143
143
  assert agent.llm.max_tokens == 3000
144
144
  assert agent.llm.logprobs == False
145
- assert agent.llm.callbacks == []
145
+ assert agent.llm.callbacks == [dummy_func]
146
146
 
147
147
 
148
148
  def test_build_agent_with_llm_and_func_llm_config():
@@ -163,7 +163,7 @@ def test_build_agent_with_llm_and_func_llm_config():
163
163
  assert agent.function_calling_llm.api_key is not None
164
164
  assert agent.function_calling_llm.max_tokens == 4000
165
165
  assert agent.function_calling_llm.logprobs == False
166
- assert agent.function_calling_llm.callbacks == []
166
+ assert agent.function_calling_llm.callbacks == [dummy_func]
167
167
 
168
168
 
169
169
  def test_build_agent_with_llm_and_func_llm_instance():
@@ -185,7 +185,7 @@ def test_build_agent_with_llm_and_func_llm_instance():
185
185
  assert agent.function_calling_llm.api_key is not None
186
186
  assert agent.function_calling_llm.max_tokens == 3000
187
187
  assert agent.function_calling_llm.logprobs == False
188
- assert agent.function_calling_llm.callbacks == []
188
+ assert agent.function_calling_llm.callbacks == [dummy_func]
189
189
 
190
190
 
191
191
  def test_agent_with_random_dict_tools():
@@ -234,7 +234,3 @@ def test_agent_custom_max_iterations():
234
234
  )
235
235
  agent.execute_task(task=task)
236
236
  assert private_mock.call_count == 1
237
-
238
-
239
- if __name__ == "__main__":
240
- test_agent_custom_max_iterations()
@@ -1,5 +1,7 @@
1
1
  import os
2
2
  import pytest
3
+ import sys
4
+ import threading
3
5
  from unittest.mock import patch
4
6
  from typing import Dict, Any, List, Optional, Callable
5
7
 
@@ -10,7 +12,8 @@ from versionhq.task.model import Task, ResponseField, TaskOutput, ConditionalTas
10
12
  from versionhq.tool.model import Tool, ToolSet
11
13
  from tests.task import DemoOutcome, demo_response_fields, base_agent
12
14
 
13
-
15
+ sys.setrecursionlimit(2097152)
16
+ threading.stack_size(134217728)
14
17
 
15
18
  def test_sync_execute_task_with_pydantic_outcome():
16
19
  task = Task(
@@ -144,7 +147,7 @@ def test_callback():
144
147
 
145
148
 
146
149
  def test_delegate():
147
- agent = Agent(role="demo agent 6", goal="My amazing goals")
150
+ agent = Agent(role="demo agent 6", goal="My amazing goals", maxit=1, max_tokens=3000)
148
151
  task = Task(
149
152
  description="return the output following the given prompt.",
150
153
  response_fields=[
@@ -193,7 +196,7 @@ def test_store_task_log():
193
196
 
194
197
  def test_task_with_agent_tools():
195
198
  simple_tool = Tool(name="simple tool", func=lambda x: "simple func")
196
- agent = Agent(role="demo", goal="execute tools", tools=[simple_tool,])
199
+ agent = Agent(role="demo", goal="execute tools", tools=[simple_tool,], maxit=1, max_tokens=3000)
197
200
  task = Task(description="execute tool", can_use_agent_tools=True, tool_res_as_final=True)
198
201
  res = task.execute_sync(agent=agent)
199
202
  assert res.tool_output == "simple func"
@@ -227,7 +230,7 @@ def test_task_with_tools():
227
230
  tool = Tool(name="tool", func=random_func)
228
231
  tool_set = ToolSet(tool=tool, kwargs=dict(message="empty func"))
229
232
 
230
- agent = Agent(role="Tool Handler", goal="execute tools")
233
+ agent = Agent(role="Tool Handler", goal="execute tools", maxit=1, max_tokens=3000)
231
234
  task = Task(description="execute the function", tools=[tool_set,], tool_res_as_final=True)
232
235
  res = task.execute_sync(agent=agent)
233
236
  assert res.tool_output == "empty func_demo"
@@ -272,5 +275,45 @@ def test_build_agent_without_developer_prompt():
272
275
 
273
276
 
274
277
 
278
+ def test_callback():
279
+ from pydantic import BaseModel
280
+ from versionhq.agent.model import Agent
281
+ from versionhq.task.model import Task
282
+
283
+ class CustomOutput(BaseModel):
284
+ test1: str
285
+ test2: list[str]
286
+
287
+ def dummy_func(message: str, test1: str, test2: list[str]) -> str:
288
+ return f"{message}: {test1}, {", ".join(test2)}"
289
+
290
+ agent = Agent(role="demo", goal="amazing project goal", maxit=1, max_tokens=3000)
291
+
292
+ task = Task(
293
+ description="Amazing task",
294
+ pydantic_custom_output=CustomOutput,
295
+ callback=dummy_func,
296
+ callback_kwargs=dict(message="Hi! Here is the result: ")
297
+ )
298
+ res = task.execute_sync(agent=agent, context="amazing context to consider.")
299
+
300
+ assert res.task_id == task.id
301
+ assert res.pydantic.test1 and res.pydantic.test2
302
+ assert "Hi! Here is the result: " in res.callback_output and res.pydantic.test1 in res.callback_output and ", ".join(res.pydantic.test2) in res.callback_output
303
+
304
+
305
+ def test_task_with_agent_callback():
306
+ def dummy_func(*args, **kwargs) -> str:
307
+ return "Demo func"
308
+
309
+ agent = Agent(role="demo", goal="amazing project goal", maxit=1, max_tokens=3000, callbacks=[dummy_func,])
310
+ task = Task(description="Amazing task")
311
+ res = task.execute_sync(agent=agent)
312
+
313
+ assert res.raw and res.task_id == task.id
275
314
 
276
315
  # task - maxit, loop, rpm
316
+
317
+
318
+ if __name__ == "__main__":
319
+ test_task_with_agent_callback()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes