versionhq 1.1.4.4__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 ADDED
@@ -0,0 +1,33 @@
1
+ import warnings
2
+
3
+ warnings.filterwarnings(
4
+ "ignore",
5
+ message="Pydantic serializer warnings:",
6
+ category=UserWarning,
7
+ module="pydantic.main",
8
+ )
9
+
10
+ from versionhq.agent.model import Agent
11
+ from versionhq.clients.customer.model import Customer
12
+ from versionhq.clients.product.model import Product, ProductProvider
13
+ from versionhq.clients.workflow.model import MessagingWorkflow
14
+ from versionhq.llm.model import LLM
15
+ from versionhq.task.model import Task, TaskOutput
16
+ from versionhq.team.model import Team, TeamOutput
17
+ from versionhq.tool.model import Tool
18
+
19
+
20
+ __version__ = "1.1.4"
21
+ __all__ = [
22
+ "Agent",
23
+ "Customer",
24
+ "Product",
25
+ "ProductProvider",
26
+ "MessagingWorkflow",
27
+ "LLM",
28
+ "Task",
29
+ "TaskOutput",
30
+ "Team",
31
+ "TeamOutput",
32
+ "Tool",
33
+ ]
File without changes
@@ -0,0 +1,13 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ from pydantic import BaseModel, PrivateAttr
4
+
5
+
6
+ class CacheHandler(BaseModel):
7
+ _cache: Dict[str, Any] = PrivateAttr(default_factory=dict)
8
+
9
+ def add(self, tool, input, output):
10
+ self._cache[f"{tool}-{input}"] = output
11
+
12
+ def read(self, tool, input) -> Optional[str]:
13
+ return self._cache.get(f"{tool}-{input}")
@@ -0,0 +1,48 @@
1
+ import json
2
+ import os
3
+ from typing import Dict, Optional
4
+
5
+ from pydantic import BaseModel, Field, PrivateAttr, model_validator
6
+
7
+
8
+ class I18N(BaseModel):
9
+ _prompts: Dict[str, Dict[str, str]] = PrivateAttr()
10
+ prompt_file: Optional[str] = Field(
11
+ default=None, description="Path to the prompt_file file to load"
12
+ )
13
+
14
+ @model_validator(mode="after")
15
+ def load_prompts(self) -> "I18N":
16
+ """Load prompts from a JSON file."""
17
+ try:
18
+ if self.prompt_file:
19
+ with open(self.prompt_file, "r", encoding="utf-8") as f:
20
+ self._prompts = json.load(f)
21
+ else:
22
+ dir_path = os.path.dirname(os.path.realpath(__file__))
23
+ prompts_path = os.path.join(dir_path, "../translations/en.json")
24
+
25
+ with open(prompts_path, "r", encoding="utf-8") as f:
26
+ self._prompts = json.load(f)
27
+ except:
28
+ return
29
+
30
+ if not self._prompts:
31
+ self._prompts = {}
32
+
33
+ return self
34
+
35
+ def slice(self, slice: str) -> str:
36
+ return self.retrieve("slices", slice)
37
+
38
+ def errors(self, error: str) -> str:
39
+ return self.retrieve("errors", error)
40
+
41
+ def tools(self, error: str) -> str:
42
+ return self.retrieve("tools", error)
43
+
44
+ def retrieve(self, kind, key) -> str:
45
+ try:
46
+ return self._prompts[kind][key]
47
+ except Exception as _:
48
+ raise Exception(f"Prompt for '{kind}':'{key}' not found.")
@@ -0,0 +1,57 @@
1
+ from datetime import datetime
2
+ from typing import Optional
3
+
4
+ from pydantic import BaseModel, Field, PrivateAttr
5
+
6
+
7
+ class Printer:
8
+ def print(self, content: str, color: Optional[str] = None):
9
+ if color == "purple":
10
+ self._print_purple(content)
11
+ elif color == "red":
12
+ self._print_red(content)
13
+ elif color == "bold_green":
14
+ self._print_bold_green(content)
15
+ elif color == "bold_purple":
16
+ self._print_bold_purple(content)
17
+ elif color == "bold_blue":
18
+ self._print_bold_blue(content)
19
+ elif color == "yellow":
20
+ self._print_yellow(content)
21
+ elif color == "bold_yellow":
22
+ self._print_bold_yellow(content)
23
+ else:
24
+ print(content)
25
+
26
+ def _print_bold_purple(self, content):
27
+ print("\033[1m\033[95m {}\033[00m".format(content))
28
+
29
+ def _print_bold_green(self, content):
30
+ print("\033[1m\033[92m {}\033[00m".format(content))
31
+
32
+ def _print_purple(self, content):
33
+ print("\033[95m {}\033[00m".format(content))
34
+
35
+ def _print_red(self, content):
36
+ print("\033[91m {}\033[00m".format(content))
37
+
38
+ def _print_bold_blue(self, content):
39
+ print("\033[1m\033[94m {}\033[00m".format(content))
40
+
41
+ def _print_yellow(self, content):
42
+ print("\033[93m {}\033[00m".format(content))
43
+
44
+ def _print_bold_yellow(self, content):
45
+ print("\033[1m\033[93m {}\033[00m".format(content))
46
+
47
+
48
+ class Logger(BaseModel):
49
+ verbose: bool = Field(default=False)
50
+ _printer: Printer = PrivateAttr(default_factory=Printer)
51
+
52
+ def log(self, level, message, color="bold_yellow"):
53
+ if self.verbose:
54
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
55
+ self._printer.print(
56
+ f"\n[{timestamp}][{level.upper()}]: {message}", color=color
57
+ )
@@ -0,0 +1,28 @@
1
+ from typing import Any, Dict, Type
2
+ from pydantic import BaseModel
3
+
4
+
5
+ def process_config(
6
+ values_to_update: Dict[str, Any], model_class: Type[BaseModel]
7
+ ) -> Dict[str, Any]:
8
+ """
9
+ Process the config dictionary and update the values accordingly.
10
+ Refer to the Pydantic model class for field validation.
11
+ """
12
+
13
+ if hasattr(values_to_update, "config"):
14
+ config = values_to_update.pop("config", {})
15
+ else:
16
+ return values_to_update
17
+
18
+ # copy values from config to the model's attributes if the attribute isn't already set.
19
+ for key, value in config.items():
20
+ if key not in model_class.model_fields or values_to_update.get(key) is not None:
21
+ continue
22
+
23
+ if isinstance(value, dict) and isinstance(values_to_update.get(key), dict):
24
+ values_to_update[key].update(value)
25
+ else:
26
+ values_to_update[key] = value
27
+
28
+ return values_to_update
@@ -0,0 +1,73 @@
1
+ import threading
2
+ import time
3
+ from typing import Optional
4
+
5
+ from pydantic import BaseModel, Field, PrivateAttr, model_validator
6
+
7
+ from versionhq._utils.logger import Logger
8
+
9
+
10
+ class RPMController(BaseModel):
11
+ max_rpm: Optional[int] = Field(default=None)
12
+ logger: Logger = Field(default_factory=lambda: Logger(verbose=False))
13
+ _current_rpm: int = PrivateAttr(default=0)
14
+ _timer: Optional[threading.Timer] = PrivateAttr(default=None)
15
+ _lock: Optional[threading.Lock] = PrivateAttr(default=None)
16
+ _shutdown_flag: bool = PrivateAttr(default=False)
17
+
18
+ @model_validator(mode="after")
19
+ def reset_counter(self):
20
+ if self.max_rpm is not None:
21
+ if not self._shutdown_flag:
22
+ self._lock = threading.Lock()
23
+ self._reset_request_count()
24
+ return self
25
+
26
+ def check_or_wait(self):
27
+ if self.max_rpm is None:
28
+ return True
29
+
30
+ def _check_and_increment():
31
+ if self.max_rpm is not None and self._current_rpm < self.max_rpm:
32
+ self._current_rpm += 1
33
+ return True
34
+ elif self.max_rpm is not None:
35
+ self.logger.log(
36
+ "info", "Max RPM reached, waiting for next minute to start."
37
+ )
38
+ self._wait_for_next_minute()
39
+ self._current_rpm = 1
40
+ return True
41
+ return True
42
+
43
+ if self._lock:
44
+ with self._lock:
45
+ return _check_and_increment()
46
+ else:
47
+ return _check_and_increment()
48
+
49
+ def stop_rpm_counter(self):
50
+ if self._timer:
51
+ self._timer.cancel()
52
+ self._timer = None
53
+
54
+ def _wait_for_next_minute(self):
55
+ time.sleep(60)
56
+ self._current_rpm = 0
57
+
58
+ def _reset_request_count(self):
59
+ def _reset():
60
+ self._current_rpm = 0
61
+ if not self._shutdown_flag:
62
+ self._timer = threading.Timer(60.0, self._reset_request_count)
63
+ self._timer.start()
64
+
65
+ if self._lock:
66
+ with self._lock:
67
+ _reset()
68
+ else:
69
+ _reset()
70
+
71
+ if self._timer:
72
+ self._shutdown_flag = True
73
+ self._timer.cancel()
@@ -0,0 +1,31 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class UsageMetrics(BaseModel):
5
+ """
6
+ Model to track usage metrics for the agent/team's execution.
7
+ """
8
+
9
+ total_tokens: int = Field(default=0, description="Total number of tokens used")
10
+ prompt_tokens: int = Field(
11
+ default=0, description="Number of tokens used in prompts"
12
+ )
13
+ cached_prompt_tokens: int = Field(
14
+ default=0, description="Number of cached prompt tokens used"
15
+ )
16
+ completion_tokens: int = Field(
17
+ default=0, description="Number of tokens used in completions"
18
+ )
19
+ successful_requests: int = Field(
20
+ default=0, description="Number of successful requests made"
21
+ )
22
+
23
+ def add_usage_metrics(self, usage_metrics: "UsageMetrics"):
24
+ """
25
+ Add the usage metrics from another UsageMetrics object.
26
+ """
27
+ self.total_tokens += usage_metrics.total_tokens
28
+ self.prompt_tokens += usage_metrics.prompt_tokens
29
+ self.cached_prompt_tokens += usage_metrics.cached_prompt_tokens
30
+ self.completion_tokens += usage_metrics.completion_tokens
31
+ self.successful_requests += usage_metrics.successful_requests
File without changes