versionhq 1.1.7.5__py3-none-any.whl → 1.1.7.8__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 +1 -1
- versionhq/_utils/process_config.py +2 -4
- versionhq/agent/model.py +9 -9
- versionhq/agent/parser.py +2 -2
- versionhq/clients/workflow/model.py +13 -9
- versionhq/llm/model.py +3 -3
- versionhq/storage/__init__.py +0 -0
- versionhq/storage/task_output_storage.py +141 -0
- versionhq/task/log_handler.py +59 -0
- versionhq/task/model.py +53 -35
- versionhq/team/model.py +30 -73
- versionhq/tool/model.py +2 -9
- versionhq/tool/tool_handler.py +3 -2
- {versionhq-1.1.7.5.dist-info → versionhq-1.1.7.8.dist-info}/METADATA +9 -7
- {versionhq-1.1.7.5.dist-info → versionhq-1.1.7.8.dist-info}/RECORD +18 -15
- {versionhq-1.1.7.5.dist-info → versionhq-1.1.7.8.dist-info}/LICENSE +0 -0
- {versionhq-1.1.7.5.dist-info → versionhq-1.1.7.8.dist-info}/WHEEL +0 -0
- {versionhq-1.1.7.5.dist-info → versionhq-1.1.7.8.dist-info}/top_level.txt +0 -0
versionhq/__init__.py
CHANGED
@@ -2,9 +2,7 @@ from typing import Any, Dict, Type
|
|
2
2
|
from pydantic import BaseModel
|
3
3
|
|
4
4
|
|
5
|
-
def process_config(
|
6
|
-
values_to_update: Dict[str, Any], model_class: Type[BaseModel]
|
7
|
-
) -> Dict[str, Any]:
|
5
|
+
def process_config(values_to_update: Dict[str, Any], model_class: Type[BaseModel]) -> Dict[str, Any]:
|
8
6
|
"""
|
9
7
|
Process the config dictionary and update the values accordingly.
|
10
8
|
Refer to the Pydantic model class for field validation.
|
@@ -15,7 +13,7 @@ def process_config(
|
|
15
13
|
else:
|
16
14
|
return values_to_update
|
17
15
|
|
18
|
-
|
16
|
+
|
19
17
|
for key, value in config.items():
|
20
18
|
if key not in model_class.model_fields or values_to_update.get(key) is not None:
|
21
19
|
continue
|
versionhq/agent/model.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
import uuid
|
3
3
|
from abc import ABC
|
4
|
-
from typing import Any, Dict, List, Optional, TypeVar
|
4
|
+
from typing import Any, Dict, List, Optional, TypeVar
|
5
5
|
from dotenv import load_dotenv
|
6
6
|
from pydantic import UUID4, BaseModel, Field, InstanceOf, PrivateAttr, model_validator, field_validator
|
7
7
|
from pydantic_core import PydanticCustomError
|
@@ -22,7 +22,7 @@ load_dotenv(override=True)
|
|
22
22
|
T = TypeVar("T", bound="Agent")
|
23
23
|
|
24
24
|
|
25
|
-
# def _format_answer(agent, answer: str) ->
|
25
|
+
# def _format_answer(agent, answer: str) -> AgentAction | AgentFinish:
|
26
26
|
# return AgentParser(agent=agent).parse(answer)
|
27
27
|
|
28
28
|
# def mock_agent_ops_provider():
|
@@ -85,7 +85,7 @@ class Agent(ABC, BaseModel):
|
|
85
85
|
"""
|
86
86
|
|
87
87
|
__hash__ = object.__hash__
|
88
|
-
_logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=
|
88
|
+
_logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=True))
|
89
89
|
_rpm_controller: Optional[RPMController] = PrivateAttr(default=None)
|
90
90
|
_request_within_rpm_limit: Any = PrivateAttr(default=None)
|
91
91
|
_token_process: TokenProcess = PrivateAttr(default_factory=TokenProcess)
|
@@ -111,8 +111,8 @@ class Agent(ABC, BaseModel):
|
|
111
111
|
step_callback: Optional[Any] = Field(default=None,description="Callback to be executed after each step of the agent execution")
|
112
112
|
|
113
113
|
# llm settings cascaded to the LLM model
|
114
|
-
llm:
|
115
|
-
function_calling_llm:
|
114
|
+
llm: str | InstanceOf[LLM] | Any = Field(default=None)
|
115
|
+
function_calling_llm: str | InstanceOf[LLM] | Any = Field(default=None)
|
116
116
|
respect_context_window: bool = Field(default=True,description="Keep messages under the context window size by summarizing content")
|
117
117
|
max_tokens: Optional[int] = Field(default=None, description="max. number of tokens for the agent's execution")
|
118
118
|
max_execution_time: Optional[int] = Field(default=None, description="max. execution time for an agent to execute a task")
|
@@ -327,7 +327,7 @@ class Agent(ABC, BaseModel):
|
|
327
327
|
messages = []
|
328
328
|
messages.append({"role": "user", "content": prompts}) #! REFINEME
|
329
329
|
messages.append({"role": "assistant", "content": self.backstory})
|
330
|
-
|
330
|
+
self._logger.log(level="info", message=f"Messages sent to the model: {messages}", color="blue")
|
331
331
|
|
332
332
|
callbacks = kwargs.get("callbacks", None)
|
333
333
|
|
@@ -338,7 +338,7 @@ class Agent(ABC, BaseModel):
|
|
338
338
|
callbacks=callbacks,
|
339
339
|
)
|
340
340
|
task_execution_counter += 1
|
341
|
-
|
341
|
+
self._logger.log(level="info", message=f"Agent's first response: {response}", color="blue")
|
342
342
|
|
343
343
|
if (response is None or response == "") and task_execution_counter < self.max_retry_limit:
|
344
344
|
while task_execution_counter <= self.max_retry_limit:
|
@@ -349,10 +349,10 @@ class Agent(ABC, BaseModel):
|
|
349
349
|
callbacks=callbacks,
|
350
350
|
)
|
351
351
|
task_execution_counter += 1
|
352
|
-
|
352
|
+
self._logger.log(level="info", message=f"Agent's next response: {response}", color="blue")
|
353
353
|
|
354
354
|
elif response is None or response == "":
|
355
|
-
|
355
|
+
self._logger.log(level="error", message="Received None or empty response from the model", color="red")
|
356
356
|
raise ValueError("Invalid response from LLM call - None or empty.")
|
357
357
|
|
358
358
|
return {"output": response.output if hasattr(response, "output") else response}
|
versionhq/agent/parser.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import re
|
2
|
-
from typing import Any
|
2
|
+
from typing import Any
|
3
3
|
from json_repair import repair_json
|
4
4
|
|
5
5
|
from versionhq._utils.i18n import I18N
|
@@ -70,7 +70,7 @@ class AgentParser:
|
|
70
70
|
def __init__(self, agent: Any):
|
71
71
|
self.agent = agent
|
72
72
|
|
73
|
-
def parse(self, text: str) ->
|
73
|
+
def parse(self, text: str) -> AgentAction | AgentFinish:
|
74
74
|
thought = self._extract_thought(text)
|
75
75
|
includes_answer = FINAL_ANSWER_ACTION in text
|
76
76
|
regex = (
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import uuid
|
2
2
|
from abc import ABC
|
3
3
|
from datetime import date, datetime, time, timedelta
|
4
|
-
from typing import Any, Dict, List,
|
5
|
-
from pydantic import UUID4, InstanceOf, BaseModel, ConfigDict, Field,
|
4
|
+
from typing import Any, Dict, List, Callable, Type, Optional, get_args, get_origin
|
5
|
+
from pydantic import UUID4, InstanceOf, BaseModel, ConfigDict, Field, field_validator, model_validator
|
6
6
|
from pydantic_core import PydanticCustomError
|
7
7
|
|
8
8
|
from versionhq.clients.product.model import Product
|
@@ -12,7 +12,7 @@ from versionhq.team.model import Team
|
|
12
12
|
|
13
13
|
|
14
14
|
class ScoreFormat:
|
15
|
-
def __init__(self, rate:
|
15
|
+
def __init__(self, rate: float | int = 0, weight: int = 1):
|
16
16
|
self.rate = rate
|
17
17
|
self.weight = weight
|
18
18
|
self.aggregate = rate * weight
|
@@ -39,7 +39,7 @@ class Score:
|
|
39
39
|
|
40
40
|
|
41
41
|
def result(self) -> int:
|
42
|
-
aggregate_score = self.brand_tone.aggregate + self.audience.aggregate + self.track_record.aggregate
|
42
|
+
aggregate_score = int(self.brand_tone.aggregate) + int(self.audience.aggregate) + int(self.track_record.aggregate)
|
43
43
|
denominator = self.brand_tone.weight + self.audience.weight + self.track_record.weight
|
44
44
|
|
45
45
|
for k, v in self.kwargs.items():
|
@@ -57,11 +57,12 @@ class MessagingComponent(ABC, BaseModel):
|
|
57
57
|
layer_id: int = Field(default=0, description="add id of the layer: 0, 1, 2")
|
58
58
|
message: str = Field(default=None, max_length=1024, description="text message content to be sent")
|
59
59
|
interval: Optional[str] = Field(
|
60
|
-
default=None,description="interval to move on to the next layer. if this is the last layer, set as `None`"
|
61
|
-
|
60
|
+
default=None, description="interval to move on to the next layer. if this is the last layer, set as `None`"
|
61
|
+
)
|
62
|
+
score: float | InstanceOf[Score] = Field(default=None)
|
62
63
|
|
63
64
|
|
64
|
-
def store_scoring_result(self, scoring_subject: str, score_raw:
|
65
|
+
def store_scoring_result(self, scoring_subject: str, score_raw: int | Score | ScoreFormat = None):
|
65
66
|
"""
|
66
67
|
Set up the `score` field
|
67
68
|
"""
|
@@ -109,8 +110,11 @@ class MessagingWorkflow(ABC, BaseModel):
|
|
109
110
|
product: InstanceOf[Product] = Field(default=None)
|
110
111
|
customer: InstanceOf[Customer] = Field(default=None)
|
111
112
|
|
112
|
-
metrics:
|
113
|
-
default=None,
|
113
|
+
metrics: List[Dict[str, Any]] | List[str] = Field(
|
114
|
+
default=None,
|
115
|
+
max_length=256,
|
116
|
+
description="store metrics that used to predict and track the performance of this workflow."
|
117
|
+
)
|
114
118
|
|
115
119
|
|
116
120
|
@property
|
versionhq/llm/model.py
CHANGED
@@ -7,7 +7,7 @@ import litellm
|
|
7
7
|
from dotenv import load_dotenv
|
8
8
|
from litellm import get_supported_openai_params
|
9
9
|
from contextlib import contextmanager
|
10
|
-
from typing import Any, Dict, List, Optional
|
10
|
+
from typing import Any, Dict, List, Optional
|
11
11
|
|
12
12
|
from versionhq.llm.llm_vars import LLM_CONTEXT_WINDOW_SIZES
|
13
13
|
from versionhq.task import TaskOutputFormat
|
@@ -103,7 +103,7 @@ class LLM:
|
|
103
103
|
def __init__(
|
104
104
|
self,
|
105
105
|
model: str,
|
106
|
-
timeout: Optional[
|
106
|
+
timeout: Optional[float | int] = None,
|
107
107
|
max_tokens: Optional[int] = None,
|
108
108
|
max_completion_tokens: Optional[int] = None,
|
109
109
|
context_window_size: Optional[int] = DEFAULT_CONTEXT_WINDOW,
|
@@ -111,7 +111,7 @@ class LLM:
|
|
111
111
|
temperature: Optional[float] = None,
|
112
112
|
top_p: Optional[float] = None,
|
113
113
|
n: Optional[int] = None,
|
114
|
-
stop: Optional[
|
114
|
+
stop: Optional[str | List[str]] = None,
|
115
115
|
presence_penalty: Optional[float] = None,
|
116
116
|
frequency_penalty: Optional[float] = None,
|
117
117
|
logit_bias: Optional[Dict[int, float]] = None,
|
File without changes
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import appdirs
|
2
|
+
import os
|
3
|
+
import json
|
4
|
+
import sqlite3
|
5
|
+
import datetime
|
6
|
+
from typing import Any, Dict, List, Optional
|
7
|
+
from dotenv import load_dotenv
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
from versionhq._utils.logger import Logger
|
11
|
+
|
12
|
+
load_dotenv(override=True)
|
13
|
+
|
14
|
+
|
15
|
+
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))
|
19
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
20
|
+
return data_dir
|
21
|
+
|
22
|
+
storage_path = fetch_db_storage_path()
|
23
|
+
default_db_name = "task_outputs"
|
24
|
+
|
25
|
+
|
26
|
+
class TaskOutputSQLiteStorage:
|
27
|
+
"""
|
28
|
+
An SQLite storage class to handle storing task outputs.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, db_path: str = f"{storage_path}/{default_db_name}.db") -> None:
|
32
|
+
self.db_path = db_path
|
33
|
+
self._logger = Logger(verbose=True)
|
34
|
+
self._initialize_db()
|
35
|
+
|
36
|
+
|
37
|
+
def _initialize_db(self):
|
38
|
+
"""
|
39
|
+
Initializes the SQLite database and creates LTM table.
|
40
|
+
"""
|
41
|
+
|
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
|
+
)
|
55
|
+
"""
|
56
|
+
)
|
57
|
+
conn.commit()
|
58
|
+
|
59
|
+
except sqlite3.Error as e:
|
60
|
+
self._logger.log(level="error", message=f"DATABASE INITIALIZATION ERROR: {e}", color="red")
|
61
|
+
|
62
|
+
|
63
|
+
def add(self, task, output: Dict[str, Any], task_index: int, was_replayed: bool = False, inputs: Dict[str, Any] = {}):
|
64
|
+
try:
|
65
|
+
with sqlite3.connect(self.db_path) as conn:
|
66
|
+
cursor = conn.cursor()
|
67
|
+
cursor.execute(
|
68
|
+
"""INSERT OR REPLACE INTO task_outputs
|
69
|
+
(task_id, output, task_index, inputs, was_replayed, timestamp)
|
70
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
71
|
+
""",
|
72
|
+
(str(task.id), json.dumps(output), task_index, json.dumps(inputs), was_replayed, datetime.datetime.now())
|
73
|
+
)
|
74
|
+
conn.commit()
|
75
|
+
|
76
|
+
except sqlite3.Error as e:
|
77
|
+
self._logger.log(level="error", message=f"SAVING TASK OUTPUTS ERROR: {e}", color="red")
|
78
|
+
|
79
|
+
|
80
|
+
def update(self, task_index: int, **kwargs):
|
81
|
+
try:
|
82
|
+
with sqlite3.connect(self.db_path) as conn:
|
83
|
+
cursor = conn.cursor()
|
84
|
+
|
85
|
+
fields, values = [], []
|
86
|
+
for k, v in kwargs.items():
|
87
|
+
fields.append(f"{k} = ?")
|
88
|
+
values.append(json.dumps(v) if isinstance(v, dict) else v)
|
89
|
+
|
90
|
+
query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?" # nosec
|
91
|
+
values.append(task_index)
|
92
|
+
cursor.execute(query, tuple(values))
|
93
|
+
conn.commit()
|
94
|
+
|
95
|
+
if cursor.rowcount == 0:
|
96
|
+
self._logger.log(
|
97
|
+
level="info", message=f"No row found with task_index {task_index}. No update performed.", color="yellow",
|
98
|
+
)
|
99
|
+
|
100
|
+
except sqlite3.Error as e:
|
101
|
+
self._logger.log(level="error", message=f"UPDATE TASK OUTPUTS ERROR: {e}", color="red")
|
102
|
+
|
103
|
+
|
104
|
+
def load(self) -> Optional[List[Dict[str, Any]]]:
|
105
|
+
try:
|
106
|
+
with sqlite3.connect(self.db_path) as conn:
|
107
|
+
cursor = conn.cursor()
|
108
|
+
cursor.execute("""
|
109
|
+
SELECT *
|
110
|
+
FROM task_outputs
|
111
|
+
ORDER BY task_index
|
112
|
+
""")
|
113
|
+
|
114
|
+
rows = cursor.fetchall()
|
115
|
+
results = []
|
116
|
+
for row in rows:
|
117
|
+
result = {
|
118
|
+
"task_id": row[0],
|
119
|
+
"output": json.loads(row[1]),
|
120
|
+
"task_index": row[2],
|
121
|
+
"inputs": json.loads(row[3]),
|
122
|
+
"was_replayed": row[4],
|
123
|
+
"timestamp": row[5],
|
124
|
+
}
|
125
|
+
results.append(result)
|
126
|
+
return results
|
127
|
+
|
128
|
+
except sqlite3.Error as e:
|
129
|
+
self._logger.log(level="error", message=f"LOADING TASK OUTPUTS ERROR: {e}", color="red")
|
130
|
+
return None
|
131
|
+
|
132
|
+
|
133
|
+
def delete_all(self):
|
134
|
+
try:
|
135
|
+
with sqlite3.connect(self.db_path) as conn:
|
136
|
+
cursor = conn.cursor()
|
137
|
+
cursor.execute("DELETE FROM task_outputs")
|
138
|
+
conn.commit()
|
139
|
+
|
140
|
+
except sqlite3.Error as e:
|
141
|
+
self._logger.log(level="error", message=f"ERROR: Failed to delete all: {e}", color="red")
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import Any, Dict, List, Optional
|
3
|
+
|
4
|
+
from pydantic import BaseModel, Field
|
5
|
+
|
6
|
+
from versionhq.storage.task_output_storage import TaskOutputSQLiteStorage
|
7
|
+
|
8
|
+
|
9
|
+
class ExecutionLog(BaseModel):
|
10
|
+
task_id: str
|
11
|
+
output: Dict[str, Any]
|
12
|
+
timestamp: datetime = Field(default_factory=datetime.now)
|
13
|
+
task_index: int
|
14
|
+
inputs: Dict[str, Any] = Field(default_factory=dict)
|
15
|
+
was_replayed: bool = False
|
16
|
+
|
17
|
+
def __getitem__(self, key: str) -> Any:
|
18
|
+
return getattr(self, key)
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
class TaskOutputStorageHandler:
|
23
|
+
|
24
|
+
def __init__(self):
|
25
|
+
self.storage = TaskOutputSQLiteStorage()
|
26
|
+
|
27
|
+
|
28
|
+
def update(self, task, task_index: int, was_replayed: bool = False, inputs: Dict[str, Any] = {}) -> None:
|
29
|
+
"""
|
30
|
+
task: task instance
|
31
|
+
"""
|
32
|
+
saved_outputs = self.load()
|
33
|
+
if saved_outputs is None:
|
34
|
+
raise ValueError("Logs cannot be None")
|
35
|
+
|
36
|
+
self.add(task, task_index, was_replayed, inputs)
|
37
|
+
|
38
|
+
|
39
|
+
def add(self, task, task_index: int, was_replayed: bool = False, inputs: Dict[str, Any] = {}) -> None:
|
40
|
+
from versionhq.task.model import Task
|
41
|
+
|
42
|
+
output_to_store = dict()
|
43
|
+
|
44
|
+
if isinstance(task, Task):
|
45
|
+
output_to_store = dict(
|
46
|
+
description=str(task.description),
|
47
|
+
raw=str(task.output.raw),
|
48
|
+
responsible_agent=str(task.processed_by_agents),
|
49
|
+
)
|
50
|
+
|
51
|
+
self.storage.add(task=task, output=output_to_store, task_index=task_index, was_replayed=was_replayed, inputs=inputs)
|
52
|
+
|
53
|
+
|
54
|
+
def reset(self) -> None:
|
55
|
+
self.storage.delete_all()
|
56
|
+
|
57
|
+
|
58
|
+
def load(self) -> Optional[List[Dict[str, Any]]]:
|
59
|
+
return self.storage.load()
|
versionhq/task/model.py
CHANGED
@@ -3,15 +3,17 @@ import threading
|
|
3
3
|
import uuid
|
4
4
|
from concurrent.futures import Future
|
5
5
|
from hashlib import md5
|
6
|
-
from typing import Any, Dict, List, Set, Optional, Tuple, Callable,
|
6
|
+
from typing import Any, Dict, List, Set, Optional, Tuple, Callable, Type
|
7
7
|
from typing_extensions import Annotated
|
8
8
|
|
9
|
-
from pydantic import UUID4, BaseModel, Field, PrivateAttr, field_validator, model_validator, create_model
|
9
|
+
from pydantic import UUID4, BaseModel, Field, PrivateAttr, field_validator, model_validator, create_model, InstanceOf
|
10
10
|
from pydantic_core import PydanticCustomError
|
11
11
|
|
12
12
|
from versionhq._utils.process_config import process_config
|
13
13
|
from versionhq.task import TaskOutputFormat
|
14
|
+
from versionhq.task.log_handler import TaskOutputStorageHandler
|
14
15
|
from versionhq.tool.model import Tool, ToolCalled
|
16
|
+
from versionhq._utils.logger import Logger
|
15
17
|
|
16
18
|
|
17
19
|
class ResponseField(BaseModel):
|
@@ -47,7 +49,7 @@ class ResponseField(BaseModel):
|
|
47
49
|
return value
|
48
50
|
|
49
51
|
|
50
|
-
def create_pydantic_model(self, result: Dict, base_model:
|
52
|
+
def create_pydantic_model(self, result: Dict, base_model: InstanceOf[BaseModel] | Any) -> Any:
|
51
53
|
for k, v in result.items():
|
52
54
|
if k is not self.title:
|
53
55
|
pass
|
@@ -59,20 +61,6 @@ class ResponseField(BaseModel):
|
|
59
61
|
return base_model
|
60
62
|
|
61
63
|
|
62
|
-
class AgentOutput(BaseModel):
|
63
|
-
"""
|
64
|
-
Keep adding agents' learning and recommendation and store it in `pydantic` field of `TaskOutput` class.
|
65
|
-
Since the TaskOutput class has `agent` field, we don't add any info on the agent that handled the task.
|
66
|
-
"""
|
67
|
-
customer_id: str = Field(default=None, max_length=126, description="customer uuid")
|
68
|
-
customer_analysis: str = Field(default=None, max_length=256, description="analysis of the customer")
|
69
|
-
product_overview: str = Field(default=None, max_length=256, description="analysis of the client's business")
|
70
|
-
usp: str = Field()
|
71
|
-
cohort_timeframe: int = Field(default=None, max_length=256, description="suitable cohort timeframe in days")
|
72
|
-
kpi_metrics: List[str] = Field(default=list, description="Ideal KPIs to be tracked")
|
73
|
-
assumptions: List[Dict[str, Any]] = Field(default=list, description="assumptions to test")
|
74
|
-
|
75
|
-
|
76
64
|
|
77
65
|
class TaskOutput(BaseModel):
|
78
66
|
"""
|
@@ -82,7 +70,7 @@ class TaskOutput(BaseModel):
|
|
82
70
|
|
83
71
|
task_id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="store Task ID")
|
84
72
|
raw: str = Field(default="", description="Raw output of the task")
|
85
|
-
json_dict:
|
73
|
+
json_dict: Dict[str, Any] = Field(default=None, description="`raw` converted to dictionary")
|
86
74
|
pydantic: Optional[Any] = Field(default=None, description="`raw` converted to the abs. pydantic model")
|
87
75
|
|
88
76
|
def __str__(self) -> str:
|
@@ -133,11 +121,13 @@ class Task(BaseModel):
|
|
133
121
|
"""
|
134
122
|
|
135
123
|
__hash__ = object.__hash__
|
124
|
+
_original_description: str = PrivateAttr(default=None)
|
125
|
+
_logger: Logger = PrivateAttr()
|
126
|
+
_task_output_handler = TaskOutputStorageHandler()
|
136
127
|
|
137
128
|
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="unique identifier for the object, not set by user")
|
138
129
|
name: Optional[str] = Field(default=None)
|
139
130
|
description: str = Field(description="Description of the actual task")
|
140
|
-
_original_description: str = PrivateAttr(default=None)
|
141
131
|
|
142
132
|
# output
|
143
133
|
expected_output_json: bool = Field(default=True)
|
@@ -161,7 +151,7 @@ class Task(BaseModel):
|
|
161
151
|
callback_kwargs: Optional[Dict[str, Any]] = Field(default_factory=dict, description="kwargs for the callback when the callback is callable")
|
162
152
|
|
163
153
|
# recording
|
164
|
-
processed_by_agents: Set[str] = Field(default_factory=set)
|
154
|
+
processed_by_agents: Set[str] = Field(default_factory=set, description="store responsible agents' roles")
|
165
155
|
used_tools: int = 0
|
166
156
|
tools_errors: int = 0
|
167
157
|
delegations: int = 0
|
@@ -372,7 +362,7 @@ Your outputs MUST adhere to the following format and should NOT include any irre
|
|
372
362
|
|
373
363
|
|
374
364
|
# task execution
|
375
|
-
def execute_sync(self, agent, context: Optional[str] = None) -> TaskOutput:
|
365
|
+
def execute_sync(self, agent, context: Optional[str] = None, tools: Optional[List[Any]] = None) -> TaskOutput:
|
376
366
|
"""
|
377
367
|
Execute the task synchronously.
|
378
368
|
When the task has context, make sure we have executed all the tasks in the context first.
|
@@ -386,7 +376,7 @@ Your outputs MUST adhere to the following format and should NOT include any irre
|
|
386
376
|
return self._execute_core(agent, context)
|
387
377
|
|
388
378
|
|
389
|
-
def execute_async(self, agent, context: Optional[str] = None) -> Future[TaskOutput]:
|
379
|
+
def execute_async(self, agent, context: Optional[str] = None, tools: Optional[List[Any]] = None) -> Future[TaskOutput]:
|
390
380
|
"""
|
391
381
|
Execute the task asynchronously.
|
392
382
|
"""
|
@@ -395,21 +385,21 @@ Your outputs MUST adhere to the following format and should NOT include any irre
|
|
395
385
|
threading.Thread(
|
396
386
|
daemon=True,
|
397
387
|
target=self._execute_task_async,
|
398
|
-
args=(agent, context, future),
|
388
|
+
args=(agent, context, tools, future),
|
399
389
|
).start()
|
400
390
|
return future
|
401
391
|
|
402
392
|
|
403
|
-
def _execute_task_async(self, agent, context: Optional[str], future: Future[TaskOutput]) -> None:
|
393
|
+
def _execute_task_async(self, agent, context: Optional[str], tools: Optional[List[Any]], future: Future[TaskOutput]) -> None:
|
404
394
|
"""
|
405
395
|
Execute the task asynchronously with context handling.
|
406
396
|
"""
|
407
397
|
|
408
|
-
result = self._execute_core(agent, context)
|
398
|
+
result = self._execute_core(agent, context, tools)
|
409
399
|
future.set_result(result)
|
410
400
|
|
411
401
|
|
412
|
-
def _execute_core(self, agent, context: Optional[str]) -> TaskOutput:
|
402
|
+
def _execute_core(self, agent, context: Optional[str], tools: Optional[List[Any]] = []) -> TaskOutput:
|
413
403
|
"""
|
414
404
|
Run the core execution logic of the task.
|
415
405
|
To speed up the process, when the format is not expected to return, we will skip the conversion process.
|
@@ -436,7 +426,7 @@ Your outputs MUST adhere to the following format and should NOT include any irre
|
|
436
426
|
agent = agent_to_delegate
|
437
427
|
self.delegations += 1
|
438
428
|
|
439
|
-
output_raw, output_json_dict, output_pydantic = agent.execute_task(task=self, context=context), None, None
|
429
|
+
output_raw, output_json_dict, output_pydantic = agent.execute_task(task=self, context=context, tools=tools), None, None
|
440
430
|
|
441
431
|
if self.expected_output_json:
|
442
432
|
output_json_dict = self.create_json_output(raw_result=output_raw)
|
@@ -472,24 +462,32 @@ Your outputs MUST adhere to the following format and should NOT include any irre
|
|
472
462
|
return task_output
|
473
463
|
|
474
464
|
|
465
|
+
def _store_execution_log(self, task_index: int, was_replayed: bool = False, inputs: Optional[Dict[str, Any]] = {}) -> None:
|
466
|
+
"""
|
467
|
+
Store the task execution log.
|
468
|
+
"""
|
469
|
+
|
470
|
+
self._task_output_handler.update(task=self, task_index=task_index, was_replayed=was_replayed, inputs=inputs)
|
471
|
+
|
472
|
+
|
473
|
+
|
475
474
|
class ConditionalTask(Task):
|
476
475
|
"""
|
477
476
|
A task that can be conditionally executed based on the output of another task.
|
478
|
-
|
477
|
+
When the `condition` return True, execute the task, else skipped with `skipped task output`.
|
479
478
|
"""
|
480
479
|
|
481
480
|
condition: Callable[[TaskOutput], bool] = Field(
|
482
481
|
default=None,
|
483
|
-
description="max. number of retries for an agent to execute a task when an error occurs
|
482
|
+
description="max. number of retries for an agent to execute a task when an error occurs",
|
484
483
|
)
|
485
484
|
|
486
|
-
|
487
|
-
|
488
|
-
condition: Callable[[Any], bool],
|
489
|
-
**kwargs,
|
490
|
-
):
|
485
|
+
|
486
|
+
def __init__(self, condition: Callable[[Any], bool], **kwargs):
|
491
487
|
super().__init__(**kwargs)
|
492
488
|
self.condition = condition
|
489
|
+
self._logger = Logger(verbose=True)
|
490
|
+
|
493
491
|
|
494
492
|
def should_execute(self, context: TaskOutput) -> bool:
|
495
493
|
"""
|
@@ -498,5 +496,25 @@ class ConditionalTask(Task):
|
|
498
496
|
"""
|
499
497
|
return self.condition(context)
|
500
498
|
|
499
|
+
|
501
500
|
def get_skipped_task_output(self):
|
502
|
-
return TaskOutput(task_id=self.id, raw="", pydantic=None, json_dict=
|
501
|
+
return TaskOutput(task_id=self.id, raw="", pydantic=None, json_dict={})
|
502
|
+
|
503
|
+
|
504
|
+
def _handle_conditional_task(self, task_outputs: List[TaskOutput], task_index: int, was_replayed: bool) -> Optional[TaskOutput]:
|
505
|
+
"""
|
506
|
+
When the conditional task should be skipped, return `skipped_task_output` as task_output else return None
|
507
|
+
"""
|
508
|
+
|
509
|
+
previous_output = task_outputs[task_index - 1] if task_outputs and len(task_outputs) > 1 else None
|
510
|
+
|
511
|
+
if previous_output and not self.should_execute(previous_output):
|
512
|
+
self._logger.log(level="debug", message=f"Skipping conditional task: {self.description}", color="yellow")
|
513
|
+
skipped_task_output = self.get_skipped_task_output()
|
514
|
+
self.output = skipped_task_output
|
515
|
+
|
516
|
+
if not was_replayed:
|
517
|
+
self._store_execution_log(self, task_index=task_index, was_replayed=was_replayed, inputs={})
|
518
|
+
return skipped_task_output
|
519
|
+
|
520
|
+
return None
|
versionhq/team/model.py
CHANGED
@@ -6,7 +6,7 @@ from enum import Enum
|
|
6
6
|
from dotenv import load_dotenv
|
7
7
|
from concurrent.futures import Future
|
8
8
|
from hashlib import md5
|
9
|
-
from typing import Any, Dict, List, TYPE_CHECKING, Callable, Optional, Tuple
|
9
|
+
from typing import Any, Dict, List, TYPE_CHECKING, Callable, Optional, Tuple
|
10
10
|
from pydantic import UUID4, InstanceOf, Json, BaseModel, Field, PrivateAttr, field_validator, model_validator
|
11
11
|
from pydantic._internal._generate_schema import GenerateSchema
|
12
12
|
from pydantic_core import PydanticCustomError, core_schema
|
@@ -61,7 +61,7 @@ class TeamOutput(BaseModel):
|
|
61
61
|
team_id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True, description="store the team ID that generate the TeamOutput")
|
62
62
|
raw: str = Field(default="", description="raw output of the team lead task handled by the team leader")
|
63
63
|
pydantic: Optional[Any] = Field(default=None, description="`raw` converted to the abs. pydantic model")
|
64
|
-
json_dict:
|
64
|
+
json_dict: Dict[str, Any] = Field(default=None, description="`raw` converted to dictionary")
|
65
65
|
task_output_list: list[TaskOutput] = Field(default=list, description="store output of all the tasks that the team has executed")
|
66
66
|
token_usage: UsageMetrics = Field(default=dict, description="processed token summary")
|
67
67
|
|
@@ -188,14 +188,9 @@ class Team(BaseModel):
|
|
188
188
|
2. manager_task,
|
189
189
|
3. members' tasks
|
190
190
|
"""
|
191
|
-
sorted_member_tasks = [
|
192
|
-
|
193
|
-
|
194
|
-
return (
|
195
|
-
self.team_tasks + sorted_member_tasks
|
196
|
-
if len(self.team_tasks) > 0
|
197
|
-
else sorted_member_tasks
|
198
|
-
)
|
191
|
+
sorted_member_tasks = [member.task for member in self.members if member.is_manager == True] + [member.task for member in self.members if member.is_manager == False]
|
192
|
+
return self.team_tasks + sorted_member_tasks if len(self.team_tasks) > 0 else sorted_member_tasks
|
193
|
+
|
199
194
|
|
200
195
|
# validators
|
201
196
|
@field_validator("id", mode="before")
|
@@ -205,10 +200,6 @@ class Team(BaseModel):
|
|
205
200
|
if v:
|
206
201
|
raise PydanticCustomError("may_not_set_field", "The 'id' field cannot be set by the user.", {})
|
207
202
|
|
208
|
-
# @field_validator("config", mode="before")
|
209
|
-
# @classmethod
|
210
|
-
# def check_config_type(cls, v: Union[Json, Dict[str, Any]]) -> Union[Json, Dict[str, Any]]:
|
211
|
-
# return json.loads(v) if isinstance(v, Json) else v
|
212
203
|
|
213
204
|
@model_validator(mode="after")
|
214
205
|
def check_manager_llm(self):
|
@@ -261,7 +252,7 @@ class Team(BaseModel):
|
|
261
252
|
if task.async_execution:
|
262
253
|
async_task_count += 1
|
263
254
|
else:
|
264
|
-
break
|
255
|
+
break
|
265
256
|
|
266
257
|
if async_task_count > 1:
|
267
258
|
raise PydanticCustomError(
|
@@ -287,47 +278,23 @@ class Team(BaseModel):
|
|
287
278
|
result[task_id] if hasattr(result, str(task_id)) else result
|
288
279
|
)
|
289
280
|
|
281
|
+
|
290
282
|
# task execution
|
291
283
|
def _process_async_tasks(
|
292
|
-
|
293
|
-
|
284
|
+
self, futures: List[Tuple[Task, Future[TaskOutput], int]], was_replayed: bool = False
|
285
|
+
) -> List[TaskOutput]:
|
286
|
+
"""
|
287
|
+
When we have `Future` tasks, updated task outputs and task execution logs accordingly.
|
288
|
+
"""
|
289
|
+
|
294
290
|
task_outputs: List[TaskOutput] = []
|
291
|
+
|
295
292
|
for future_task, future, task_index in futures:
|
296
293
|
task_output = future.result()
|
297
294
|
task_outputs.append(task_output)
|
298
|
-
|
299
|
-
self._store_execution_log(
|
300
|
-
future_task, task_output, task_index, was_replayed
|
301
|
-
)
|
302
|
-
return task_outputs
|
303
|
-
|
304
|
-
|
305
|
-
def _handle_conditional_task(
|
306
|
-
self,
|
307
|
-
task: ConditionalTask,
|
308
|
-
task_outputs: List[TaskOutput],
|
309
|
-
futures: List[Tuple[Task, Future[TaskOutput], int]],
|
310
|
-
task_index: int,
|
311
|
-
was_replayed: bool,
|
312
|
-
) -> Optional[TaskOutput]:
|
313
|
-
|
314
|
-
if futures:
|
315
|
-
task_outputs = self._process_async_tasks(futures, was_replayed)
|
316
|
-
futures.clear()
|
317
|
-
|
318
|
-
previous_output = task_outputs[task_index - 1] if task_outputs else None
|
319
|
-
if previous_output is not None and not task.should_execute(previous_output):
|
320
|
-
self._logger.log(
|
321
|
-
"debug",
|
322
|
-
f"Skipping conditional task: {task.description}",
|
323
|
-
color="yellow",
|
324
|
-
)
|
325
|
-
skipped_task_output = task.get_skipped_task_output()
|
295
|
+
future_task._store_execution_log(task_index, was_replayed)
|
326
296
|
|
327
|
-
|
328
|
-
self._store_execution_log(task, skipped_task_output, task_index)
|
329
|
-
return skipped_task_output
|
330
|
-
return None
|
297
|
+
return task_outputs
|
331
298
|
|
332
299
|
|
333
300
|
def _create_team_output(self, task_outputs: List[TaskOutput], lead_task_output: TaskOutput = None) -> TeamOutput:
|
@@ -401,38 +368,33 @@ class Team(BaseModel):
|
|
401
368
|
if responsible_agent is None:
|
402
369
|
responsible_agent = self.manager_agent if self.manager_agent else self.members[0].agent
|
403
370
|
|
404
|
-
# self._prepare_agent_tools(task)
|
405
|
-
# self._log_task_start(task, responsible_agent)
|
406
|
-
|
407
371
|
if isinstance(task, ConditionalTask):
|
408
|
-
skipped_task_output =
|
372
|
+
skipped_task_output = task._handle_conditional_task(task_outputs, futures, task_index, was_replayed)
|
409
373
|
if skipped_task_output:
|
410
374
|
continue
|
411
375
|
|
376
|
+
# self._prepare_agent_tools(task)
|
377
|
+
# self._log_task_start(task, responsible_agent)
|
378
|
+
|
412
379
|
if task.async_execution:
|
413
380
|
context = create_raw_outputs(tasks=[task, ],task_outputs=([last_sync_output,] if last_sync_output else []))
|
414
|
-
future = task.execute_async(agent=responsible_agent, context=context
|
415
|
-
# tools=responsible_agent.tools
|
416
|
-
)
|
381
|
+
future = task.execute_async(agent=responsible_agent, context=context, tools=responsible_agent.tools)
|
417
382
|
futures.append((task, future, task_index))
|
418
383
|
else:
|
419
|
-
if
|
420
|
-
|
421
|
-
futures.clear()
|
422
|
-
|
423
|
-
context = create_raw_outputs(tasks=[task,], task_outputs=([ last_sync_output,] if last_sync_output else [] ))
|
424
|
-
task_output = task.execute_sync(agent=responsible_agent, context=context
|
425
|
-
# tools=responsible_agent.tools
|
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
|
426
386
|
)
|
427
387
|
if responsible_agent is self.manager_agent:
|
428
388
|
lead_task_output = task_output
|
429
389
|
|
430
390
|
task_outputs.append(task_output)
|
431
391
|
# self._process_task_result(task, task_output)
|
432
|
-
|
392
|
+
task._store_execution_log(task_index, was_replayed)
|
393
|
+
|
394
|
+
|
395
|
+
if futures:
|
396
|
+
task_outputs = self._process_async_tasks(futures, was_replayed)
|
433
397
|
|
434
|
-
# if futures:
|
435
|
-
# task_outputs = self._process_async_tasks(futures, was_replayed)
|
436
398
|
return self._create_team_output(task_outputs, lead_task_output)
|
437
399
|
|
438
400
|
|
@@ -458,10 +420,6 @@ class Team(BaseModel):
|
|
458
420
|
# self._task_output_handler.reset()
|
459
421
|
# self._logging_color = "bold_purple"
|
460
422
|
|
461
|
-
# if inputs is not None:
|
462
|
-
# self._inputs = inputs
|
463
|
-
# self._interpolate_inputs(inputs)
|
464
|
-
|
465
423
|
|
466
424
|
# i18n = I18N(prompt_file=self.prompt_file)
|
467
425
|
|
@@ -469,14 +427,13 @@ class Team(BaseModel):
|
|
469
427
|
agent = member.agent
|
470
428
|
agent.team = self
|
471
429
|
|
472
|
-
|
473
|
-
if not agent.function_calling_llm:
|
430
|
+
if not agent.function_calling_llm and self.function_calling_llm:
|
474
431
|
agent.function_calling_llm = self.function_calling_llm
|
475
432
|
|
476
433
|
# if agent.allow_code_execution:
|
477
434
|
# agent.tools += agent.get_code_execution_tools()
|
478
435
|
|
479
|
-
if not agent.step_callback:
|
436
|
+
if not agent.step_callback and self.step_callback:
|
480
437
|
agent.step_callback = self.step_callback
|
481
438
|
|
482
439
|
if self.process is None:
|
versionhq/tool/model.py
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
from abc import ABC
|
2
2
|
from inspect import signature
|
3
3
|
from typing import Any, Dict, Callable, Type, Optional, get_args, get_origin
|
4
|
-
from pydantic import
|
5
|
-
InstanceOf,
|
6
|
-
BaseModel,
|
7
|
-
ConfigDict,
|
8
|
-
Field,
|
9
|
-
create_model,
|
10
|
-
field_validator,
|
11
|
-
model_validator,
|
12
|
-
)
|
4
|
+
from pydantic import InstanceOf, BaseModel, ConfigDict, Field, create_model, field_validator, model_validator
|
13
5
|
|
14
6
|
from versionhq._utils.cache_handler import CacheHandler
|
15
7
|
|
@@ -135,6 +127,7 @@ class Tool(ABC, BaseModel):
|
|
135
127
|
|
136
128
|
return cls(name=tool_name, func=func, args_schema=args_schema)
|
137
129
|
|
130
|
+
|
138
131
|
def run(self, *args, **kwargs) -> Any:
|
139
132
|
"""
|
140
133
|
Use the tool.
|
versionhq/tool/tool_handler.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
from typing import Any, Optional
|
1
|
+
from typing import Any, Optional
|
2
|
+
from pydantic import InstanceOf
|
2
3
|
from versionhq.tool.model import ToolCalled, InstructorToolCalled, CacheTool
|
3
4
|
from versionhq._utils.cache_handler import CacheHandler
|
4
5
|
|
@@ -23,7 +24,7 @@ class ToolHandler:
|
|
23
24
|
|
24
25
|
def record_last_tool_used(
|
25
26
|
self,
|
26
|
-
last_used_tool:
|
27
|
+
last_used_tool: InstanceOf[ToolCalled] | InstanceOf[InstructorToolCalled],
|
27
28
|
output: str,
|
28
29
|
should_cache: bool = True,
|
29
30
|
) -> Any:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: versionhq
|
3
|
-
Version: 1.1.7.
|
3
|
+
Version: 1.1.7.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
|
@@ -32,6 +32,9 @@ Keywords: orchestration framework,orchestration,ai agent,multi-agent system,RAG,
|
|
32
32
|
Classifier: Programming Language :: Python
|
33
33
|
Classifier: License :: OSI Approved :: MIT License
|
34
34
|
Classifier: Operating System :: OS Independent
|
35
|
+
Classifier: Development Status :: 3 - Alpha
|
36
|
+
Classifier: Intended Audience :: Developers
|
37
|
+
Classifier: Topic :: Software Development :: Build Tools
|
35
38
|
Requires-Python: >=3.12
|
36
39
|
Description-Content-Type: text/markdown
|
37
40
|
License-File: LICENSE
|
@@ -43,12 +46,12 @@ Requires-Dist: typing
|
|
43
46
|
Requires-Dist: json-repair>=0.31.0
|
44
47
|
Requires-Dist: litellm>=1.55.8
|
45
48
|
Requires-Dist: openai>=1.57.0
|
46
|
-
Requires-Dist: composio-openai>=0.6.
|
47
|
-
Requires-Dist: pre-commit>=4.0.1
|
48
|
-
Requires-Dist: gunicorn>=23.0.0
|
49
|
+
Requires-Dist: composio-openai>=0.6.9
|
49
50
|
Requires-Dist: composio>=0.1.0
|
50
51
|
Requires-Dist: setuptools>=75.6.0
|
51
52
|
Requires-Dist: wheel>=0.45.1
|
53
|
+
Requires-Dist: python-dotenv>=1.0.0
|
54
|
+
Requires-Dist: appdirs>=1.4.4
|
52
55
|
|
53
56
|
# Overview
|
54
57
|
|
@@ -56,7 +59,7 @@ Requires-Dist: wheel>=0.45.1
|
|
56
59
|
[](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
|
57
60
|

|
58
61
|

|
59
|
-

|
60
63
|
|
61
64
|
|
62
65
|
An LLM orchestration frameworks for multi-agent systems with RAG to autopilot outbound workflows.
|
@@ -276,8 +279,7 @@ src/
|
|
276
279
|
```
|
277
280
|
uv venv
|
278
281
|
source .venv/bin/activate
|
279
|
-
|
280
|
-
uv pip install -r requirements.txt -v
|
282
|
+
uv pip sync
|
281
283
|
```
|
282
284
|
|
283
285
|
* In case of AssertionError/module mismatch, run Python version control using `.pyenv`
|
@@ -1,14 +1,14 @@
|
|
1
|
-
versionhq/__init__.py,sha256=
|
1
|
+
versionhq/__init__.py,sha256=KTM1HF1lTl5AiAuFQuGwltg4LEZjHnac9mz8h12MtGg,871
|
2
2
|
versionhq/_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
versionhq/_utils/cache_handler.py,sha256=zDQKzIn7vp-M2-uepHFxgJstjfftZS5mzXKL_-4uVvI,370
|
4
4
|
versionhq/_utils/i18n.py,sha256=TwA_PnYfDLA6VqlUDPuybdV9lgi3Frh_ASsb_X8jJo8,1483
|
5
5
|
versionhq/_utils/logger.py,sha256=lqRYH45KHMQ4mwE1woa5xNmngYu4O749AYECsnWWpmA,1851
|
6
|
-
versionhq/_utils/process_config.py,sha256=
|
6
|
+
versionhq/_utils/process_config.py,sha256=UqoWD5IR4VLxEDGxIyVUylw_ppXwk8Wx1ynVuD-pUSg,822
|
7
7
|
versionhq/_utils/rpm_controller.py,sha256=T7waIGeblu5K58erY4lqVLcPsWM7W9UFdU3DG9Dsk0w,2214
|
8
8
|
versionhq/_utils/usage_metrics.py,sha256=c33a_28y8ECUgflsKN3mkNm0fNkWgZmXwybMwIqoKXA,1098
|
9
9
|
versionhq/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
versionhq/agent/model.py,sha256=
|
11
|
-
versionhq/agent/parser.py,sha256=
|
10
|
+
versionhq/agent/model.py,sha256=tv14XkjrgsFsryFWzaw0w2h0X1T0ffipVK9l4kYbpIE,18598
|
11
|
+
versionhq/agent/parser.py,sha256=db5kfk-lR1Ph9-rsTvSeW1NjR6GJ00iaqTNYxJy3N8o,5487
|
12
12
|
versionhq/agent/TEMPLATES/Backstory.py,sha256=cdngBx1GEv7nroR46FEhnysnBJ9mEVL763_9np6Skkc,395
|
13
13
|
versionhq/agent/TEMPLATES/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
versionhq/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -18,22 +18,25 @@ versionhq/clients/customer/model.py,sha256=rQnCv_wdCdrYAsUjyB6X6ULiuWfqcBBoarXcQ
|
|
18
18
|
versionhq/clients/product/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
versionhq/clients/product/model.py,sha256=Us3UnzYlub6ipBislMN-JrvxZx0ocl9PtQJINJ8XtBA,2385
|
20
20
|
versionhq/clients/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
versionhq/clients/workflow/model.py,sha256=
|
21
|
+
versionhq/clients/workflow/model.py,sha256=GI-cSw-7zOFzLC2Xa4YfCowu3MI0JyH33qDFTqbJLmg,5725
|
22
22
|
versionhq/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
23
|
versionhq/llm/llm_vars.py,sha256=YZoXqFBW7XpclUZ14_AAz7WOjoyCXnGcI959GSpX2q0,5343
|
24
|
-
versionhq/llm/model.py,sha256=
|
24
|
+
versionhq/llm/model.py,sha256=mXzSuf1s6MebGT7_yqgNppde0NIlAF8bjIXAp2MZ9Uw,8247
|
25
|
+
versionhq/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
+
versionhq/storage/task_output_storage.py,sha256=RxvF_lSRUuo2Af3zMAuc1aOymx1e6e_5VJ2y757_Hu0,4910
|
25
27
|
versionhq/task/__init__.py,sha256=g4mCATnn1mUXxsfQ5p6IpPawr8O421wVIT8kMKEcxQw,180
|
26
28
|
versionhq/task/formatter.py,sha256=N8Kmk9vtrMtBdgJ8J7RmlKNMdZWSmV8O1bDexmCWgU0,643
|
27
|
-
versionhq/task/
|
29
|
+
versionhq/task/log_handler.py,sha256=KJRrcNZgFSKhlNzvtYFnvtp6xukaF1s7ifX9u4zWrN8,1683
|
30
|
+
versionhq/task/model.py,sha256=bUmERE6AZfs8qh2Hb9LES5BLUlXrkrOUyWxCU27M1ic,19317
|
28
31
|
versionhq/team/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
-
versionhq/team/model.py,sha256=
|
32
|
+
versionhq/team/model.py,sha256=T_71FarXEzAxrTn_8yYVWMwLS9p-UxSovexZtHYnYn0,18066
|
30
33
|
versionhq/team/team_planner.py,sha256=B1UOn_DYVVterUn2CAd80jfO4sViJCCXPJA3abSSugg,2143
|
31
34
|
versionhq/tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
35
|
versionhq/tool/decorator.py,sha256=Y-j4jkoujD5LUvpe8uf3p5Zagk2XVaRKC9rkIE-2geo,1189
|
33
|
-
versionhq/tool/model.py,sha256=
|
34
|
-
versionhq/tool/tool_handler.py,sha256=
|
35
|
-
versionhq-1.1.7.
|
36
|
-
versionhq-1.1.7.
|
37
|
-
versionhq-1.1.7.
|
38
|
-
versionhq-1.1.7.
|
39
|
-
versionhq-1.1.7.
|
36
|
+
versionhq/tool/model.py,sha256=s-y8323ikd5m5U2HG59ATgFW6L7yIFiPLLdOpeXQ8RI,6874
|
37
|
+
versionhq/tool/tool_handler.py,sha256=esUqGp8HoREesai8fmh2klAf04Sjpsacmb03C7F6sNQ,1541
|
38
|
+
versionhq-1.1.7.8.dist-info/LICENSE,sha256=7CCXuMrAjPVsUvZrsBq9DsxI2rLDUSYXR_qj4yO_ZII,1077
|
39
|
+
versionhq-1.1.7.8.dist-info/METADATA,sha256=xsI-HWU0_Rb0OLH_tSDvIk7E8SWP0U-SjTQfoomW6zU,15919
|
40
|
+
versionhq-1.1.7.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
41
|
+
versionhq-1.1.7.8.dist-info/top_level.txt,sha256=DClQwxDWqIUGeRJkA8vBlgeNsYZs4_nJWMonzFt5Wj0,10
|
42
|
+
versionhq-1.1.7.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|