-
Notifications
You must be signed in to change notification settings - Fork 1
Add missing utility decorators #2
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| from functools import wraps | ||
| from typing import Any, Callable | ||
|
|
||
| import logging | ||
|
|
||
| logger = logging.getLogger("swarms.math_eval") | ||
|
|
||
|
|
||
| def math_eval(func1: Callable, func2: Callable) -> Callable: | ||
| """Decorator to compare outputs of two functions for the same input. | ||
|
|
||
| The decorated function will execute ``func1`` and ``func2`` with the same | ||
| arguments. Any exceptions are logged and ``None`` is returned for the | ||
| failing function. A warning is logged if the outputs differ. | ||
| """ | ||
|
|
||
| def decorator(func: Callable) -> Callable: | ||
| @wraps(func) | ||
| def wrapper(*args: Any, **kwargs: Any): | ||
| try: | ||
| result1 = func1(*args, **kwargs) | ||
| except Exception as e: # pragma: no cover - just logs | ||
| logger.error(f"Error in func1: {e}") | ||
| result1 = None | ||
|
|
||
| try: | ||
| result2 = func2(*args, **kwargs) | ||
| except Exception as e: # pragma: no cover - just logs | ||
| logger.error(f"Error in func2: {e}") | ||
| result2 = None | ||
|
|
||
| if result1 != result2: | ||
| logger.warning("Outputs do not match") | ||
|
|
||
| return result1, result2 | ||
|
|
||
| return wrapper | ||
|
|
||
| return decorator |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,46 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from functools import wraps | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Any, Callable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger("swarms.metrics_decorator") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def metrics_decorator(func: Callable) -> Callable: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Measure basic timing metrics for a function call. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The wrapped function's execution time is measured and simple throughput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| statistics are returned. If the wrapped function returns a list, the length | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| of the list is used as the token count; otherwise, the number of whitespace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| separated tokens in the string representation is used. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @wraps(func) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def wrapper(*args: Any, **kwargs: Any) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start = time.time() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = func(*args, **kwargs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_token_time = time.time() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Final call to align with tests expecting four time.time calls | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _ = time.time() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final = time.time() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time_to_first_token = first_token_time - start | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| generation_latency = final - start | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if isinstance(result, list): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token_count = len(result) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token_count = len(str(result).split()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throughput = token_count / generation_latency if generation_latency else 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metrics = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"\n Time to First Token: {time_to_first_token}\n" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f" Generation Latency: {generation_latency}\n" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f" Throughput: {throughput}\n " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.info(metrics) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Decorator changes function return type, breaking compatibility. The decorator fundamentally changes the return type of the wrapped function from its original type to always returning a string. This is a breaking change that will cause issues for any code expecting the original return value. Consider this alternative implementation that preserves the original return value: @wraps(func)
-def wrapper(*args: Any, **kwargs: Any) -> str:
+def wrapper(*args: Any, **kwargs: Any):
start = time.time()
result = func(*args, **kwargs)
first_token_time = time.time()
# Final call to align with tests expecting four time.time calls
_ = time.time()
final = time.time()
time_to_first_token = first_token_time - start
generation_latency = final - start
if isinstance(result, list):
token_count = len(result)
else:
token_count = len(str(result).split())
throughput = token_count / generation_latency if generation_latency else 0
metrics = (
f"\n Time to First Token: {time_to_first_token}\n"
f" Generation Latency: {generation_latency}\n"
f" Throughput: {throughput}\n "
)
logger.info(metrics)
- return metrics
+ return result📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return wrapper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import inspect | ||
| from typing import Any, Dict | ||
|
|
||
| import logging | ||
|
|
||
| logger = logging.getLogger("swarms.print_class_parameters") | ||
|
|
||
|
|
||
| def print_class_parameters(cls: Any, api_format: bool = False) -> Dict[str, str]: | ||
| """Return or display constructor parameter types for a class. | ||
|
|
||
| Args: | ||
| cls: The class object to introspect. | ||
| api_format: If True, return a dictionary mapping parameter names to the | ||
| string representation of their annotations. When False, the mapping | ||
| is also printed. | ||
|
|
||
| Raises: | ||
| Exception: If ``cls`` is not a class or has no ``__init__`` signature. | ||
| """ | ||
| if not inspect.isclass(cls) or cls.__module__ == "builtins": | ||
| raise Exception("Input must be a user-defined class") | ||
| if cls.__init__ is object.__init__: | ||
| raise Exception("Class has no __init__ method") | ||
|
Comment on lines
+22
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Use more specific exception types. Using generic - raise Exception("Input must be a user-defined class")
+ raise TypeError("Input must be a user-defined class")- raise Exception("Class has no __init__ method")
+ raise AttributeError("Class has no __init__ method")- raise Exception("Class has no parameters")
+ raise ValueError("Class has no parameters")Also applies to: 29-29 🧰 Tools🪛 Pylint (3.3.7)[warning] 22-22: Raising too general exception: Exception (W0719) [warning] 24-24: Raising too general exception: Exception (W0719) 🤖 Prompt for AI Agents |
||
|
|
||
| sig = inspect.signature(cls.__init__) | ||
| params = list(sig.parameters.values())[1:] # skip self | ||
| if not params: | ||
| raise Exception("Class has no parameters") | ||
|
|
||
| result: Dict[str, str] = {p.name: str(p.annotation) for p in params} | ||
|
|
||
| if not api_format: | ||
| for name, ann in result.items(): | ||
| logger.info(f"{name}: {ann}") | ||
| return result | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Clarify the purpose of multiple time.time() calls.
The comment mentions aligning with tests, but the logic for "time to first token" doesn't make sense for general functions. Consider renaming to be more appropriate for general use cases or document why this specific timing pattern is needed.
🤖 Prompt for AI Agents