cloudmesh-ai-common 7.0.4__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 (28) hide show
  1. cloudmesh_ai_common-7.0.4/PKG-INFO +20 -0
  2. cloudmesh_ai_common-7.0.4/README.md +104 -0
  3. cloudmesh_ai_common-7.0.4/VERSION +1 -0
  4. cloudmesh_ai_common-7.0.4/pyproject.toml +55 -0
  5. cloudmesh_ai_common-7.0.4/setup.cfg +4 -0
  6. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/__init__.py +48 -0
  7. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/aggregation.py +130 -0
  8. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/io.py +101 -0
  9. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/logging.py +264 -0
  10. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/stopwatch.py +407 -0
  11. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/sys.py +589 -0
  12. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/telemetry.py +408 -0
  13. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/time.py +35 -0
  14. cloudmesh_ai_common-7.0.4/src/cloudmesh/ai/common/user.py +87 -0
  15. cloudmesh_ai_common-7.0.4/src/cloudmesh_ai_common.egg-info/PKG-INFO +20 -0
  16. cloudmesh_ai_common-7.0.4/src/cloudmesh_ai_common.egg-info/SOURCES.txt +26 -0
  17. cloudmesh_ai_common-7.0.4/src/cloudmesh_ai_common.egg-info/dependency_links.txt +1 -0
  18. cloudmesh_ai_common-7.0.4/src/cloudmesh_ai_common.egg-info/requires.txt +15 -0
  19. cloudmesh_ai_common-7.0.4/src/cloudmesh_ai_common.egg-info/top_level.txt +1 -0
  20. cloudmesh_ai_common-7.0.4/tests/test_gpu_detection.py +72 -0
  21. cloudmesh_ai_common-7.0.4/tests/test_io.py +85 -0
  22. cloudmesh_ai_common-7.0.4/tests/test_logging.py +31 -0
  23. cloudmesh_ai_common-7.0.4/tests/test_shell.py +21 -0
  24. cloudmesh_ai_common-7.0.4/tests/test_stopwatch_threadsafe.py +84 -0
  25. cloudmesh_ai_common-7.0.4/tests/test_sys.py +75 -0
  26. cloudmesh_ai_common-7.0.4/tests/test_telemetry_integration.py +125 -0
  27. cloudmesh_ai_common-7.0.4/tests/test_time.py +14 -0
  28. cloudmesh_ai_common-7.0.4/tests/test_user.py +66 -0
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: cloudmesh-ai-common
3
+ Version: 7.0.4
4
+ Summary: CMC: Cloudmesh Command for AI
5
+ License: Apache-2.0
6
+ Keywords: cmc,extension,cli,click,cloudmesh
7
+ Requires-Dist: click
8
+ Requires-Dist: rich
9
+ Requires-Dist: shellingham
10
+ Requires-Dist: psutil
11
+ Requires-Dist: PyYAML
12
+ Requires-Dist: humanize
13
+ Requires-Dist: tzlocal
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
16
+ Requires-Dist: pip-tools; extra == "dev"
17
+ Requires-Dist: twine; extra == "dev"
18
+ Requires-Dist: ruff; extra == "dev"
19
+ Requires-Dist: mypy; extra == "dev"
20
+ Requires-Dist: pytest-asyncio; extra == "dev"
@@ -0,0 +1,104 @@
1
+ # Cloudmesh AI Common
2
+
3
+ `cloudmesh-ai-common` provides a set of shared utilities for the Cloudmesh AI ecosystem, focusing on system introspection, structured telemetry, and standardized logging.
4
+
5
+ ## Features
6
+
7
+ ### 1. Enhanced System Introspection (`cloudmesh.ai.common.sys`)
8
+ The system utility provides deep insights into the host hardware and environment.
9
+
10
+ - **Hardware Detection**:
11
+ - **GPU**: Automatic detection for NVIDIA, AMD, Apple Silicon, and Intel GPUs.
12
+ - **Topology**: Detection of NUMA nodes and high-speed network interfaces (InfiniBand/RoCE).
13
+ - **CPU**: Detailed processor descriptions across macOS, Windows, and Linux.
14
+ - **Thermal Monitoring**: Standardized CPU temperature retrieval across Linux and macOS to detect thermal throttling.
15
+ - **Container Awareness**: Automatic detection of Docker and Kubernetes environments, including extraction of K8s namespace metadata.
16
+ - **Real-time Metrics**:
17
+ - Live CPU utilization (overall and per-core).
18
+ - Detailed memory and swap usage.
19
+ - Disk I/O statistics and usage.
20
+ - **System Info**: The `systeminfo()` function collects a comprehensive snapshot of the environment. Use `realtime=True` to include live performance metrics.
21
+
22
+ ### 2. Advanced Telemetry System (`cloudmesh.ai.common.telemetry`)
23
+ A pluggable telemetry framework for emitting structured performance and status data from AI commands.
24
+
25
+ - **Pluggable Backends**:
26
+ - `JSONFileBackend`: Emits records in JSONL format for easy ingestion by log aggregators.
27
+ - `SQLiteBackend`: Stores telemetry in a structured SQLite database for complex querying.
28
+ - `TextBackend`: Provides human-readable summaries for developers and operators.
29
+ - **Automatic Tracking**: The `track()` context manager automatically handles start/complete/fail events and calculates task duration.
30
+ - **Async Support**: The `AsyncTelemetry` class allows non-blocking telemetry emission using `asyncio`, ensuring that I/O operations do not interfere with compute-intensive AI tasks.
31
+ - **Standardized Events**: Built-in helpers for `start()`, `complete()`, and `fail()` events.
32
+
33
+ ### 3. Telemetry Aggregation (`cloudmesh.ai.common.aggregation`)
34
+ The `TelemetryAggregator` utility allows for the analysis of emitted telemetry data.
35
+
36
+ - **Multi-source Loading**: Load data from either JSONL files or SQLite databases.
37
+ - **Statistical Summaries**: Calculate success rates, status distributions, and command frequency.
38
+ - **Metric Analysis**: Aggregate specific KPIs to find average, minimum, and maximum values across multiple runs.
39
+
40
+ ### 4. Standardized Logging (`cloudmesh.ai.common.logging`)
41
+ Provides a consistent logging interface across all AI components to ensure uniform log formatting and traceability.
42
+
43
+ ## Usage Examples
44
+
45
+ ### System Info
46
+ ```python
47
+ from cloudmesh.ai.common import sys as ai_sys
48
+
49
+ # Get static and real-time system info
50
+ info = ai_sys.systeminfo(realtime=True)
51
+ print(f"CPU: {info['cpu']}")
52
+ print(f"GPU Present: {info['gpu.present']}")
53
+ print(f"CPU Temp: {info.get('cpu.temp')}")
54
+ print(f"Container: {info.get('container.type', 'none')}")
55
+ ```
56
+
57
+ ### Synchronous Telemetry with Context Manager
58
+ ```python
59
+ from cloudmesh.ai.common.telemetry import Telemetry, SQLiteBackend, TextBackend
60
+
61
+ telemetry = Telemetry(
62
+ "my-ai-command",
63
+ backends=[SQLiteBackend("metrics.db"), TextBackend()]
64
+ )
65
+
66
+ # Use the track context manager for automatic timing and error handling
67
+ with telemetry.track(message="Running inference", metrics={"model": "llama-3"}):
68
+ # ... perform work ...
69
+ # If an exception occurs here, it is automatically logged as a 'failed' event
70
+ pass
71
+ ```
72
+
73
+ ### Asynchronous Telemetry
74
+ ```python
75
+ import asyncio
76
+ from cloudmesh.ai.common.telemetry import AsyncTelemetry, JSONFileBackend
77
+
78
+ async def run_command():
79
+ telemetry = AsyncTelemetry(
80
+ "async-ai-task",
81
+ backends=[JSONFileBackend("async_metrics.jsonl")]
82
+ )
83
+
84
+ async with telemetry.track(message="Processing async request"):
85
+ # ... perform async work ...
86
+ await asyncio.sleep(0.1)
87
+
88
+ asyncio.run(run_command())
89
+ ```
90
+
91
+ ### Telemetry Aggregation
92
+ ```python
93
+ from cloudmesh.ai.common.aggregation import TelemetryAggregator
94
+
95
+ # Analyze data from a SQLite database
96
+ agg = TelemetryAggregator("metrics.db")
97
+
98
+ # Get high-level summary
99
+ summary = agg.get_summary()
100
+ print(f"Success Rate: {summary['success_rate']}")
101
+
102
+ # Aggregate a specific metric across all runs
103
+ latency_stats = agg.aggregate_metric("duration_sec")
104
+ print(f"Average Duration: {latency_stats['avg']}s")
@@ -0,0 +1 @@
1
+ 7.0.4
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cloudmesh-ai-common"
7
+ dynamic = ["version"]
8
+ description = "CMC: Cloudmesh Command for AI"
9
+ license = {text = "Apache-2.0"}
10
+ keywords = ["cmc", "extension", "cli", "click", "cloudmesh"]
11
+ dependencies = [
12
+ "click",
13
+ "rich",
14
+ "shellingham",
15
+ "psutil",
16
+ "PyYAML",
17
+ "humanize",
18
+ "tzlocal",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ dev = [
23
+ "pytest",
24
+ "pip-tools",
25
+ "twine",
26
+ "ruff",
27
+ "mypy",
28
+ "pytest-asyncio",
29
+ ]
30
+
31
+ [tool.setuptools.dynamic]
32
+ version = {file = ["VERSION"]}
33
+
34
+ [tool.setuptools]
35
+ # Tells setuptools to look inside the 'src' directory for packages
36
+ package-dir = {"" = "src"}
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["src"]
40
+ include = ["cloudmesh*"]
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py310"
45
+
46
+ [tool.ruff.lint]
47
+ select = ["E", "F", "W", "I", "N", "UP"]
48
+ ignore = ["E501"]
49
+
50
+ [tool.mypy]
51
+ python_version = "3.10"
52
+ strict = false
53
+ ignore_missing_imports = true
54
+ warn_return_any = true
55
+ warn_unused_configs = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,48 @@
1
+ """Common utilities for cloudmesh-ai components.
2
+
3
+ This package provides shared functionality for logging, telemetry, system
4
+ information gathering, and other common helper utilities used across
5
+ the cloudmesh-ai ecosystem.
6
+ """
7
+
8
+ from .logging import (
9
+ set_context_id,
10
+ get_context_id,
11
+ ContextFilter,
12
+ JsonFormatter,
13
+ load_logging_config,
14
+ get_log_dir,
15
+ ensure_log_dir,
16
+ get_log_file_path,
17
+ get_logger,
18
+ progress,
19
+ )
20
+ from .aggregation import TelemetryAggregator
21
+ from .telemetry import (
22
+ TelemetryBackend,
23
+ JSONFileBackend,
24
+ SQLiteBackend,
25
+ TextBackend,
26
+ Telemetry,
27
+ AsyncTelemetry,
28
+ )
29
+ from .sys import (
30
+ os_is_windows,
31
+ os_is_mac,
32
+ os_is_linux,
33
+ os_is_pi,
34
+ has_window_manager,
35
+ sys_user,
36
+ get_platform,
37
+ get_cpu_description,
38
+ get_gpu_info,
39
+ get_thermal_info,
40
+ get_numa_info,
41
+ get_container_info,
42
+ get_network_info,
43
+ get_disk_read_speed,
44
+ get_cpu_metrics,
45
+ get_memory_metrics,
46
+ get_disk_metrics,
47
+ systeminfo,
48
+ )
@@ -0,0 +1,130 @@
1
+ """
2
+ Telemetry aggregation utility for cloudmesh-ai.
3
+ Provides tools to analyze and summarize telemetry data from various backends.
4
+ """
5
+
6
+ import json
7
+ import sqlite3
8
+ from pathlib import Path
9
+ from typing import Dict, Any, List, Optional, Union
10
+ from collections import defaultdict
11
+
12
+ class TelemetryAggregator:
13
+ """
14
+ Analyzes telemetry records to provide summaries and statistics.
15
+ Supports loading data from both JSONL files and SQLite databases.
16
+ """
17
+
18
+ def __init__(self, source: Union[str, Path]):
19
+ """
20
+ Initialize the TelemetryAggregator.
21
+
22
+ Args:
23
+ source: Path to the telemetry source (JSONL file or SQLite .db file).
24
+ """
25
+ self.source = Path(source).expanduser()
26
+ self.records: List[Dict[str, Any]] = []
27
+ self._load_data()
28
+
29
+ def _load_data(self) -> None:
30
+ """Detects source type and loads records into memory.
31
+ """
32
+ if self.source.suffix == ".db":
33
+ self._load_from_sqlite()
34
+ else:
35
+ self._load_from_jsonl()
36
+
37
+ def _load_from_jsonl(self) -> None:
38
+ """Loads records from a JSONL file.
39
+ """
40
+ try:
41
+ if not self.source.exists():
42
+ return
43
+ with open(self.source, "r") as f:
44
+ for line in f:
45
+ if line.strip():
46
+ self.records.append(json.loads(line))
47
+ except Exception as e:
48
+ print(f"Error loading JSONL telemetry: {e}")
49
+
50
+ def _load_from_sqlite(self) -> None:
51
+ """Loads records from a SQLite database.
52
+ """
53
+ try:
54
+ with sqlite3.connect(self.source) as conn:
55
+ conn.row_factory = sqlite3.Row
56
+ cursor = conn.execute("SELECT * FROM telemetry")
57
+ for row in cursor:
58
+ record = dict(row)
59
+ # Convert JSON strings back to dicts
60
+ record["metrics"] = json.loads(record["metrics"]) if isinstance(record["metrics"], str) else record["metrics"]
61
+ record["system"] = json.loads(record["system"]) if isinstance(record["system"], str) else record["system"]
62
+ self.records.append(record)
63
+ except Exception as e:
64
+ print(f"Error loading SQLite telemetry: {e}")
65
+
66
+ def get_summary(self) -> Dict[str, Any]:
67
+ """
68
+ Calculates a high-level summary of the telemetry data.
69
+
70
+ Returns:
71
+ A dictionary containing total records, success rate,
72
+ status distribution, and command distribution.
73
+ """
74
+ if not self.records:
75
+ return {"error": "No records found"}
76
+
77
+ total = len(self.records)
78
+ status_counts = defaultdict(int)
79
+ command_counts = defaultdict(int)
80
+
81
+ for r in self.records:
82
+ status_counts[r.get("status", "unknown")] += 1
83
+ command_counts[r.get("command", "unknown")] += 1
84
+
85
+ success_count = status_counts.get("completed", 0)
86
+
87
+ return {
88
+ "total_records": total,
89
+ "success_rate": f"{(success_count / total) * 100:.2f}%",
90
+ "status_distribution": dict(status_counts),
91
+ "command_distribution": dict(command_counts),
92
+ }
93
+
94
+ def aggregate_metric(self, metric_name: str) -> Dict[str, Any]:
95
+ """
96
+ Calculates average, min, and max for a specific metric across all records.
97
+
98
+ Args:
99
+ metric_name: The key of the metric to aggregate from the 'metrics' dictionary.
100
+
101
+ Returns:
102
+ A dictionary containing the count, average, minimum, and maximum values.
103
+ """
104
+ values = []
105
+ for r in self.records:
106
+ metrics = r.get("metrics", {})
107
+ if metric_name in metrics:
108
+ val = metrics[metric_name]
109
+ if isinstance(val, (int, float)):
110
+ values.append(val)
111
+
112
+ if not values:
113
+ return {"metric": metric_name, "status": "no data"}
114
+
115
+ return {
116
+ "metric": metric_name,
117
+ "count": len(values),
118
+ "avg": sum(values) / len(values),
119
+ "min": min(values),
120
+ "max": max(values),
121
+ }
122
+
123
+ if __name__ == "__main__":
124
+ # Simple test if run directly
125
+ import sys
126
+ if len(sys.argv) > 1:
127
+ agg = TelemetryAggregator(sys.argv[1])
128
+ print(json.dumps(agg.get_summary(), indent=4))
129
+ else:
130
+ print("Usage: python aggregation.py <telemetry_file_or_db>")
@@ -0,0 +1,101 @@
1
+ """
2
+ I/O utility functions for cloudmesh-ai.
3
+ Provides helpers for path expansion, YAML handling, and benchmark file creation.
4
+ """
5
+
6
+ import os
7
+ import yaml
8
+ from pathlib import Path
9
+ from typing import Any, Dict, Optional
10
+
11
+ def path_expand(text: str, slashreplace: bool = True) -> str:
12
+ """Expands a path string by resolving '~', environment variables, and relative links.
13
+
14
+ Args:
15
+ text: The path to be expanded (e.g., "~/$PROJECT/./file.txt").
16
+ slashreplace: If True, returns backslashes on Windows. Defaults to True.
17
+
18
+ Returns:
19
+ The fully expanded and absolute path.
20
+ """
21
+ if not text:
22
+ return ""
23
+
24
+ # 1. Expand ~ and Environment Variables
25
+ expanded = os.path.expandvars(os.path.expanduser(text))
26
+
27
+ # 2. Convert to a Path object and make it absolute
28
+ # .resolve() handles the "./" and "../" logic correctly
29
+ path_obj = Path(expanded).resolve()
30
+
31
+ # 3. Handle string conversion and slash preference
32
+ if slashreplace and os.name == 'nt':
33
+ # On Windows, this automatically uses backslashes
34
+ return str(path_obj)
35
+
36
+ # .as_posix() forces forward slashes (/) regardless of OS
37
+ return path_obj.as_posix()
38
+
39
+ def load_yaml(path: Path) -> Optional[Dict[str, Any]]:
40
+ """Safely loads a YAML file from the given path.
41
+
42
+ Args:
43
+ path: Path to the YAML file.
44
+
45
+ Returns:
46
+ The loaded YAML data as a dictionary, or None if the file does not exist
47
+ or an error occurs.
48
+ """
49
+ try:
50
+ if not path.exists():
51
+ return None
52
+ with open(path, 'r') as f:
53
+ return yaml.safe_load(f)
54
+ except (yaml.YAMLError, OSError):
55
+ return None
56
+
57
+ def dump_yaml(path: Path, data: Dict[str, Any]) -> None:
58
+ """Safely writes a dictionary to a YAML file, ensuring the directory exists.
59
+
60
+ Args:
61
+ path: Path where the YAML file should be written.
62
+ data: The dictionary to write to the file.
63
+
64
+ """
65
+ path.parent.mkdir(parents=True, exist_ok=True)
66
+ with open(path, 'w') as f:
67
+ yaml.dump(data, f, default_flow_style=False)
68
+
69
+ def create_benchmark_yaml(path: str, n: int) -> None:
70
+ """Creates a Cloudmesh service YAML test file with specified number of services.
71
+
72
+ Args:
73
+ path: Path where the benchmark YAML file should be created.
74
+ n: Number of services to include in the benchmark file.
75
+
76
+ """
77
+ cm = {"cloudmesh": {}}
78
+ for i in range(0, n):
79
+ cm["cloudmesh"][f"service{i}"] = {"attribute": f"service{i}"}
80
+
81
+ location = path_expand(path)
82
+ with open(location, "w") as yaml_file:
83
+ yaml.dump(cm, yaml_file, default_flow_style=False)
84
+
85
+ def create_benchmark_file(path: str, n: int) -> int:
86
+ """Creates a file of a given size in binary megabytes.
87
+
88
+ Args:
89
+ path: Path where the benchmark file should be created.
90
+ n: Size of the file in megabytes.
91
+
92
+ Returns:
93
+ The actual size of the created file in megabytes.
94
+ """
95
+ location = path_expand(path)
96
+ size = 1048576 * n # size in bytes
97
+ with open(location, "wb") as f:
98
+ f.write(os.urandom(size))
99
+
100
+ s = os.path.getsize(location)
101
+ return int(s / 1048576.0)