mca-sdk 0.0.0__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.
Files changed (68) hide show
  1. mca_sdk/FOR_AI_ASSISTANTS.md +202 -0
  2. mca_sdk/__init__.py +76 -0
  3. mca_sdk/autolog/__init__.py +185 -0
  4. mca_sdk/autolog/_base.py +381 -0
  5. mca_sdk/autolog/_lightgbm.py +111 -0
  6. mca_sdk/autolog/_sklearn.py +93 -0
  7. mca_sdk/autolog/_xgboost.py +111 -0
  8. mca_sdk/buffering/__init__.py +15 -0
  9. mca_sdk/buffering/exporters.py +1691 -0
  10. mca_sdk/buffering/queue.py +1275 -0
  11. mca_sdk/buffering/queued_item.py +37 -0
  12. mca_sdk/buffering/retry.py +282 -0
  13. mca_sdk/cli/__init__.py +10 -0
  14. mca_sdk/cli/_bootstrap.py +172 -0
  15. mca_sdk/cli/init.py +683 -0
  16. mca_sdk/cli/instrument.py +339 -0
  17. mca_sdk/cli/run.py +339 -0
  18. mca_sdk/config/__init__.py +5 -0
  19. mca_sdk/config/settings.py +1898 -0
  20. mca_sdk/core/__init__.py +15 -0
  21. mca_sdk/core/client.py +3150 -0
  22. mca_sdk/core/gcp_auth.py +460 -0
  23. mca_sdk/core/providers.py +772 -0
  24. mca_sdk/exporters/__init__.py +9 -0
  25. mca_sdk/exporters/gcp_logging.py +562 -0
  26. mca_sdk/exporters/gcp_trace_exporter.py +865 -0
  27. mca_sdk/integrations/__init__.py +45 -0
  28. mca_sdk/integrations/bridge.py +345 -0
  29. mca_sdk/integrations/decorators.py +523 -0
  30. mca_sdk/integrations/genai.py +284 -0
  31. mca_sdk/integrations/litellm_callback.py +689 -0
  32. mca_sdk/integrations/state.py +197 -0
  33. mca_sdk/py.typed +2 -0
  34. mca_sdk/registry/__init__.py +22 -0
  35. mca_sdk/registry/client.py +1048 -0
  36. mca_sdk/registry/models.py +170 -0
  37. mca_sdk/registry/telemetry.py +72 -0
  38. mca_sdk/resilience/__init__.py +10 -0
  39. mca_sdk/resilience/circuit_breaker.py +321 -0
  40. mca_sdk/resilience/dead_letter_queue.py +611 -0
  41. mca_sdk/security/__init__.py +47 -0
  42. mca_sdk/security/_testing.py +11 -0
  43. mca_sdk/security/certificate.py +261 -0
  44. mca_sdk/security/certificates.py +541 -0
  45. mca_sdk/security/encryption.py +664 -0
  46. mca_sdk/security/migration.py +205 -0
  47. mca_sdk/utils/__init__.py +5 -0
  48. mca_sdk/utils/exceptions.py +215 -0
  49. mca_sdk/utils/gcp_validators.py +68 -0
  50. mca_sdk/utils/phi_utils.py +84 -0
  51. mca_sdk/utils/routing.py +162 -0
  52. mca_sdk/utils/serialization.py +407 -0
  53. mca_sdk/utils/version_check.py +90 -0
  54. mca_sdk/validation/__init__.py +241 -0
  55. mca_sdk/validation/attributes.py +416 -0
  56. mca_sdk/validation/naming_conventions.py +94 -0
  57. mca_sdk/validation/result.py +33 -0
  58. mca_sdk/validation/rules.py +117 -0
  59. mca_sdk/validation/schema.py +476 -0
  60. mca_sdk/validation/telemetry_attributes.py +390 -0
  61. mca_sdk/validation/validator.py +140 -0
  62. mca_sdk/version.py +17 -0
  63. mca_sdk-0.0.0.dist-info/METADATA +1597 -0
  64. mca_sdk-0.0.0.dist-info/RECORD +68 -0
  65. mca_sdk-0.0.0.dist-info/WHEEL +5 -0
  66. mca_sdk-0.0.0.dist-info/entry_points.txt +4 -0
  67. mca_sdk-0.0.0.dist-info/licenses/LICENSE +17 -0
  68. mca_sdk-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,202 @@
1
+ # Guide for AI Coding Assistants
2
+
3
+ **Purpose:** Help data scientists integrate MCA SDK into their code.
4
+
5
+ **Location:** This file is included when they `pip install mca-sdk`. You can find it at:
6
+ ```python
7
+ import mca_sdk
8
+ import os
9
+ guide_path = os.path.join(os.path.dirname(mca_sdk.__file__), 'FOR_AI_ASSISTANTS.md')
10
+ ```
11
+
12
+ ---
13
+
14
+ ## What the Data Scientist Does
15
+
16
+ ```bash
17
+ pip install mca-sdk
18
+ ```
19
+
20
+ Then asks you: "Help me add MCA SDK to instrument my model."
21
+
22
+ ---
23
+
24
+ ## Your Job (Simple 3-Step Process)
25
+
26
+ ### 1. Look at Their Code
27
+
28
+ Find their prediction function:
29
+ ```python
30
+ def predict(features):
31
+ return model.predict(features)
32
+ ```
33
+
34
+ ### 2. Apply the Pattern
35
+
36
+ Read the working examples in `sdk-examples/`:
37
+ - `internal-model/instrumented_model.py` - Decorator pattern for ML models
38
+ - `litellm-genai/genai_chatbot.py` - LiteLLM callback for GenAI
39
+ - `internal-agentic/agentic_assistant.py` - Goal tracking for agents
40
+ - `vendor-model/vendor_bridge.py` - Bridge pattern for external APIs
41
+
42
+ Pick the pattern that matches their code and apply it.
43
+
44
+ ### 3. Help with Environment Setup
45
+
46
+ Create `.env` file:
47
+ ```bash
48
+ MCA_SERVICE_NAME="their-model-name"
49
+ MCA_MODEL_ID="mdl-001"
50
+ MCA_TEAM_NAME="their-team"
51
+ MCA_COLLECTOR_ENDPOINT="http://localhost:4318"
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Example: Internal ML Model
57
+
58
+ **Their code:**
59
+ ```python
60
+ from sklearn.ensemble import RandomForestClassifier
61
+
62
+ model = RandomForestClassifier()
63
+
64
+ def predict_readmission(patient_features):
65
+ prediction = model.predict(patient_features)
66
+ return {'risk': prediction[0]}
67
+ ```
68
+
69
+ **You add:**
70
+ ```python
71
+ from sklearn.ensemble import RandomForestClassifier
72
+ from mca_sdk import MCAClient
73
+ import atexit
74
+
75
+ # Initialize SDK
76
+ client = MCAClient(
77
+ service_name="readmission-predictor",
78
+ model_id="mdl-001",
79
+ team_name="clinical-ai"
80
+ )
81
+ atexit.register(client.shutdown)
82
+
83
+ model = RandomForestClassifier()
84
+
85
+ # Add decorator
86
+ @client.predict()
87
+ def predict_readmission(patient_features):
88
+ prediction = model.predict(patient_features)
89
+ return {'risk': prediction[0]}
90
+ ```
91
+
92
+ **Done.** That's it.
93
+
94
+ ---
95
+
96
+ ## Example: GenAI/LLM Model
97
+
98
+ **Their code:**
99
+ ```python
100
+ import litellm
101
+
102
+ def summarize_note(clinical_note):
103
+ response = litellm.completion(
104
+ model="gpt-4",
105
+ messages=[{"role": "user", "content": clinical_note}]
106
+ )
107
+ return response.choices[0].message.content
108
+ ```
109
+
110
+ **You add:**
111
+ ```python
112
+ import litellm
113
+ from mca_sdk import MCAClient
114
+ from mca_sdk.integrations import MCALiteLLMCallback
115
+ import atexit
116
+
117
+ # Initialize SDK
118
+ client = MCAClient(
119
+ service_name="clinical-summarizer",
120
+ model_id="mdl-002",
121
+ team_name="genai-team"
122
+ )
123
+ atexit.register(client.shutdown)
124
+
125
+ # Attach callback - automatically instruments all LLM calls
126
+ callback = MCALiteLLMCallback(client)
127
+ litellm.callbacks.append(callback)
128
+
129
+ def summarize_note(clinical_note):
130
+ response = litellm.completion(
131
+ model="gpt-4",
132
+ messages=[{"role": "user", "content": clinical_note}]
133
+ )
134
+ return response.choices[0].message.content
135
+ ```
136
+
137
+ **Done.** All LLM calls now automatically instrumented.
138
+
139
+ ---
140
+
141
+ ## Critical Reminders
142
+
143
+ ### Always Warn About PHI
144
+
145
+ ```
146
+ ⚠️ IMPORTANT: Remove PHI before using SDK:
147
+
148
+ ❌ BAD:
149
+ client.record_prediction(patient_name="John Doe", mrn="12345")
150
+
151
+ ✅ GOOD:
152
+ client.record_prediction(patient_id=hash("12345"), risk_score=0.85)
153
+ ```
154
+
155
+ ### Testing
156
+
157
+ ```bash
158
+ # Start collector
159
+ docker-compose up -d otel-collector
160
+
161
+ # Run their code
162
+ python model.py
163
+
164
+ # Check metrics
165
+ open http://localhost:9090
166
+ ```
167
+
168
+ ---
169
+
170
+ ## When They Ask Questions
171
+
172
+ **"What environment variables do I need?"**
173
+ → Show them the 4 required: `MCA_SERVICE_NAME`, `MCA_MODEL_ID`, `MCA_TEAM_NAME`, `MCA_COLLECTOR_ENDPOINT`
174
+
175
+ **"How do I know it's working?"**
176
+ → `docker logs -f otel-collector` should show "exporting metrics"
177
+
178
+ **"Will this slow down my model?"**
179
+ → No, SDK uses async export with 10-second batching
180
+
181
+ **"Do I need the Model Registry?"**
182
+ → No, it's optional. SDK works with just environment variables.
183
+
184
+ ---
185
+
186
+ ## That's It
187
+
188
+ Don't overcomplicate. The SDK is designed to be simple:
189
+ 1. One import: `from mca_sdk import MCAClient`
190
+ 2. One decorator: `@client.predict()`
191
+ 3. Done
192
+
193
+ Read the examples in `sdk-examples/` for patterns. Apply them to the data scientist's code. Help with `.env` setup. That's the entire workflow.
194
+
195
+ ---
196
+
197
+ ## Reference
198
+
199
+ - Examples: `sdk-examples/`
200
+ - README: `mca-prototype/README.md`
201
+ - API docs: `docs/api-contracts.md`
202
+ - Full guides: `docs/integration-guides/`
mca_sdk/__init__.py ADDED
@@ -0,0 +1,76 @@
1
+ """MCA SDK - Model Collector Agent SDK for OpenTelemetry instrumentation.
2
+
3
+ This SDK simplifies model instrumentation by providing schema validation,
4
+ buffering, and resilience features while reducing boilerplate code from
5
+ 170+ lines to ~10 lines.
6
+
7
+ Example:
8
+ >>> from mca_sdk import MCAClient
9
+ >>>
10
+ >>> client = MCAClient(
11
+ ... service_name="readmission-model",
12
+ ... model_id="mdl-001",
13
+ ... team_name="clinical-ai"
14
+ ... )
15
+ >>>
16
+ >>> def predict(data):
17
+ ... with client.trace("model.predict"):
18
+ ... result = model.predict(data)
19
+ ... client.record_prediction(data, result, latency=0.15)
20
+ ... return result
21
+ >>>
22
+ >>> client.shutdown()
23
+ """
24
+
25
+ from .version import __version__
26
+
27
+ from .core import MCAClient
28
+ from .config import MCAConfig
29
+ from .registry import RegistryClient, ModelConfig, DeploymentConfig
30
+ from .security.encryption import QueueEncryption, EncryptionManager
31
+ from .security.migration import migrate_queue_to_encrypted, verify_encrypted_queue
32
+ from .autolog import autolog
33
+ from .utils.exceptions import (
34
+ MCASDKError,
35
+ ConfigurationError,
36
+ RegistryError,
37
+ RegistryConnectionError,
38
+ RegistryConfigNotFoundError,
39
+ RegistryAuthError,
40
+ ValidationError,
41
+ BufferingError,
42
+ SecurityError,
43
+ CertificateError,
44
+ CertificateLoadError,
45
+ CertificateValidationError,
46
+ CertificateExpiredError,
47
+ CertificateNotLoadedError,
48
+ )
49
+
50
+ __all__ = [
51
+ "MCAClient",
52
+ "MCAConfig",
53
+ "RegistryClient",
54
+ "ModelConfig",
55
+ "DeploymentConfig",
56
+ "QueueEncryption",
57
+ "EncryptionManager",
58
+ "migrate_queue_to_encrypted",
59
+ "verify_encrypted_queue",
60
+ "autolog",
61
+ "MCASDKError",
62
+ "ConfigurationError",
63
+ "RegistryError",
64
+ "RegistryConnectionError",
65
+ "RegistryConfigNotFoundError",
66
+ "RegistryAuthError",
67
+ "ValidationError",
68
+ "BufferingError",
69
+ "SecurityError",
70
+ "CertificateError",
71
+ "CertificateLoadError",
72
+ "CertificateValidationError",
73
+ "CertificateExpiredError",
74
+ "CertificateNotLoadedError",
75
+ "__version__",
76
+ ]
@@ -0,0 +1,185 @@
1
+ """Auto-instrumentation for popular ML frameworks.
2
+
3
+ This module provides automatic instrumentation (monkey-patching) for popular
4
+ ML frameworks like scikit-learn, XGBoost, and LightGBM, enabling zero-code
5
+ observability for model predictions.
6
+ """
7
+
8
+ import logging
9
+ import sys
10
+ from typing import List, Optional
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def autolog(
16
+ frameworks: Optional[List[str]] = None,
17
+ exclude: Optional[List[str]] = None,
18
+ capture_input: bool = False,
19
+ capture_output: bool = False,
20
+ max_payload_size: int = 10000,
21
+ ) -> None:
22
+ """Enable automatic instrumentation for ML frameworks.
23
+
24
+ Automatically patches popular ML frameworks to emit OpenTelemetry spans
25
+ and metrics when making predictions. This provides zero-code instrumentation
26
+ for models using standard framework APIs.
27
+
28
+ IMPORTANT: MCAClient must be initialized BEFORE calling autolog().
29
+ The autolog() function uses MCAClient.get_instance() to access the active client.
30
+
31
+ Args:
32
+ frameworks: List of specific frameworks to instrument (e.g., ["sklearn", "xgboost"]).
33
+ If None, automatically detects and instruments all available frameworks.
34
+ exclude: List of frameworks to exclude from instrumentation (e.g., ["lightgbm"]).
35
+ capture_input: Whether to capture input data in spans (default: False for security).
36
+ capture_output: Whether to capture output data in spans (default: False for security).
37
+ max_payload_size: Maximum size in bytes for captured input/output data (default: 10000).
38
+ Prevents memory bloat from large batch predictions.
39
+
40
+ Raises:
41
+ RuntimeError: If MCAClient has not been initialized before calling autolog().
42
+ ValueError: If invalid framework names provided in frameworks or exclude parameters.
43
+
44
+ Examples:
45
+ Enable autolog for all available frameworks:
46
+ >>> from mca_sdk import MCAClient, autolog
47
+ >>> client = MCAClient(service_name="ml-model", model_id="mdl-001", team_name="ds")
48
+ >>> autolog() # Automatically instruments sklearn, xgboost, lightgbm if installed
49
+
50
+ Enable autolog for specific frameworks only:
51
+ >>> autolog(frameworks=["sklearn", "xgboost"])
52
+
53
+ Exclude specific frameworks:
54
+ >>> autolog(exclude=["lightgbm"])
55
+
56
+ Capture input/output data with size limits (use with caution - PHI risks):
57
+ >>> autolog(capture_input=True, capture_output=True, max_payload_size=5000)
58
+
59
+ Supported Frameworks:
60
+ - sklearn: scikit-learn estimators (predict, predict_proba, fit)
61
+ - xgboost: XGBoost Booster and sklearn API (predict)
62
+ - lightgbm: LightGBM Booster (predict)
63
+
64
+ Note:
65
+ TensorFlow and PyTorch are not currently supported but are planned for future releases.
66
+ """
67
+ from ..core.client import MCAClient
68
+
69
+ # Validate framework names
70
+ SUPPORTED_FRAMEWORKS = {"sklearn", "xgboost", "lightgbm"}
71
+
72
+ if frameworks is not None:
73
+ invalid_frameworks = set(frameworks) - SUPPORTED_FRAMEWORKS
74
+ if invalid_frameworks:
75
+ raise ValueError(
76
+ f"Invalid framework names: {invalid_frameworks}. "
77
+ f"Supported frameworks: {SUPPORTED_FRAMEWORKS}"
78
+ )
79
+
80
+ if exclude is not None:
81
+ invalid_exclude = set(exclude) - SUPPORTED_FRAMEWORKS
82
+ if invalid_exclude:
83
+ raise ValueError(
84
+ f"Invalid framework names in exclude: {invalid_exclude}. "
85
+ f"Supported frameworks: {SUPPORTED_FRAMEWORKS}"
86
+ )
87
+
88
+ # Check that MCAClient has been initialized
89
+ client_instance = MCAClient.get_instance()
90
+ if client_instance is None:
91
+ raise RuntimeError(
92
+ "MCAClient must be initialized before calling autolog(). "
93
+ "Example: client = MCAClient(service_name='...', model_id='...', team_name='...')"
94
+ )
95
+
96
+ # Determine which frameworks to patch
97
+ if frameworks is None:
98
+ # Auto-detect all available frameworks
99
+ frameworks_to_patch = _detect_installed_frameworks()
100
+ else:
101
+ # Use explicitly requested frameworks
102
+ frameworks_to_patch = frameworks
103
+
104
+ # Remove excluded frameworks
105
+ if exclude:
106
+ frameworks_to_patch = [f for f in frameworks_to_patch if f not in exclude]
107
+
108
+ if not frameworks_to_patch:
109
+ logger.warning("No ML frameworks detected or all frameworks excluded. Autolog is a no-op.")
110
+ return
111
+
112
+ logger.info(f"Enabling autolog for frameworks: {frameworks_to_patch}")
113
+
114
+ # Patch each framework
115
+ for framework_name in frameworks_to_patch:
116
+ try:
117
+ _patch_framework(framework_name, capture_input, capture_output, max_payload_size)
118
+ logger.info(f"Successfully patched {framework_name}")
119
+ except Exception as e:
120
+ # Graceful failure: log warning but continue
121
+ logger.warning(
122
+ f"Failed to patch {framework_name}: {e}. Continuing without instrumentation for this framework."
123
+ )
124
+
125
+
126
+ def _detect_installed_frameworks() -> List[str]:
127
+ """Detect which ML frameworks are currently imported.
128
+
129
+ Returns:
130
+ List of framework names that are available in sys.modules.
131
+ """
132
+ available = []
133
+
134
+ # Check for sklearn (may be imported as 'sklearn' or 'sklearn.base')
135
+ if "sklearn" in sys.modules or "sklearn.base" in sys.modules:
136
+ available.append("sklearn")
137
+
138
+ # Check for xgboost
139
+ if "xgboost" in sys.modules:
140
+ available.append("xgboost")
141
+
142
+ # Check for lightgbm
143
+ if "lightgbm" in sys.modules:
144
+ available.append("lightgbm")
145
+
146
+ return available
147
+
148
+
149
+ def _patch_framework(
150
+ framework_name: str,
151
+ capture_input: bool,
152
+ capture_output: bool,
153
+ max_payload_size: int,
154
+ ) -> None:
155
+ """Patch a specific ML framework.
156
+
157
+ Args:
158
+ framework_name: Name of the framework to patch ("sklearn", "xgboost", "lightgbm").
159
+ capture_input: Whether to capture input data.
160
+ capture_output: Whether to capture output data.
161
+ max_payload_size: Maximum size in bytes for captured data.
162
+
163
+ Raises:
164
+ ValueError: If framework_name is not recognized.
165
+ ImportError: If framework module cannot be imported.
166
+ """
167
+ if framework_name == "sklearn":
168
+ from ._sklearn import patch_sklearn
169
+
170
+ patch_sklearn(capture_input, capture_output, max_payload_size)
171
+ elif framework_name == "xgboost":
172
+ from ._xgboost import patch_xgboost
173
+
174
+ patch_xgboost(capture_input, capture_output, max_payload_size)
175
+ elif framework_name == "lightgbm":
176
+ from ._lightgbm import patch_lightgbm
177
+
178
+ patch_lightgbm(capture_input, capture_output, max_payload_size)
179
+ else:
180
+ raise ValueError(
181
+ f"Unknown framework: {framework_name}. Supported: sklearn, xgboost, lightgbm"
182
+ )
183
+
184
+
185
+ __all__ = ["autolog"]