revenium-python-sdk 0.1.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.
- revenium_middleware/__init__.py +184 -0
- revenium_middleware/_core/__init__.py +65 -0
- revenium_middleware/_core/config.py +165 -0
- revenium_middleware/_core/context.py +109 -0
- revenium_middleware/_core/decorators.py +202 -0
- revenium_middleware/_core/metering.py +207 -0
- revenium_middleware/_core/prompt_extraction.py +55 -0
- revenium_middleware/_core/subscriber.py +51 -0
- revenium_middleware/_core/trace_fields.py +265 -0
- revenium_middleware/anthropic/__init__.py +108 -0
- revenium_middleware/anthropic/bedrock_adapter.py +753 -0
- revenium_middleware/anthropic/config.py +29 -0
- revenium_middleware/anthropic/middleware.py +1070 -0
- revenium_middleware/anthropic/prompt_extractor.py +178 -0
- revenium_middleware/anthropic/provider.py +141 -0
- revenium_middleware/anthropic/summary_printer.py +286 -0
- revenium_middleware/anthropic/trace_fields.py +158 -0
- revenium_middleware/google/__init__.py +114 -0
- revenium_middleware/google/common/__init__.py +127 -0
- revenium_middleware/google/common/exceptions.py +137 -0
- revenium_middleware/google/common/protocols.py +192 -0
- revenium_middleware/google/common/summary_printer.py +271 -0
- revenium_middleware/google/common/trace_fields.py +205 -0
- revenium_middleware/google/common/types.py +208 -0
- revenium_middleware/google/common/utils.py +1111 -0
- revenium_middleware/google/config.py +64 -0
- revenium_middleware/google/google_ai/__init__.py +53 -0
- revenium_middleware/google/google_ai/middleware.py +667 -0
- revenium_middleware/google/google_ai/provider.py +135 -0
- revenium_middleware/google/prompt_extractor.py +396 -0
- revenium_middleware/google/vertex_ai/__init__.py +56 -0
- revenium_middleware/google/vertex_ai/middleware.py +1162 -0
- revenium_middleware/google/vertex_ai/provider.py +99 -0
- revenium_middleware/litellm/__init__.py +25 -0
- revenium_middleware/litellm/client/__init__.py +81 -0
- revenium_middleware/litellm/client/config.py +53 -0
- revenium_middleware/litellm/client/context.py +198 -0
- revenium_middleware/litellm/client/decorators.py +912 -0
- revenium_middleware/litellm/client/hooks.py +192 -0
- revenium_middleware/litellm/client/integrations/__init__.py +26 -0
- revenium_middleware/litellm/client/integrations/crewai.py +446 -0
- revenium_middleware/litellm/client/middleware.py +321 -0
- revenium_middleware/litellm/client/summary_printer.py +314 -0
- revenium_middleware/litellm/client/trace_fields.py +51 -0
- revenium_middleware/litellm/client/validation.py +207 -0
- revenium_middleware/litellm/proxy/__init__.py +25 -0
- revenium_middleware/litellm/proxy/middleware.py +217 -0
- revenium_middleware/ollama/__init__.py +28 -0
- revenium_middleware/ollama/middleware.py +569 -0
- revenium_middleware/ollama/trace_fields.py +63 -0
- revenium_middleware/openai/__init__.py +23 -0
- revenium_middleware/openai/azure_config.py +169 -0
- revenium_middleware/openai/azure_model_resolver.py +219 -0
- revenium_middleware/openai/config.py +45 -0
- revenium_middleware/openai/exceptions.py +115 -0
- revenium_middleware/openai/langchain/__init__.py +114 -0
- revenium_middleware/openai/langchain/_utils.py +129 -0
- revenium_middleware/openai/langchain/unified_handler.py +526 -0
- revenium_middleware/openai/middleware.py +1451 -0
- revenium_middleware/openai/prompt_extractor.py +173 -0
- revenium_middleware/openai/provider.py +170 -0
- revenium_middleware/openai/summary_printer.py +292 -0
- revenium_middleware/openai/trace_fields.py +98 -0
- revenium_middleware/perplexity/__init__.py +97 -0
- revenium_middleware/perplexity/middleware.py +379 -0
- revenium_middleware/perplexity/perplexity_sdk.py +256 -0
- revenium_middleware/perplexity/provider.py +84 -0
- revenium_middleware/perplexity/trace_fields.py +25 -0
- revenium_python_sdk-0.1.0.dist-info/METADATA +252 -0
- revenium_python_sdk-0.1.0.dist-info/RECORD +73 -0
- revenium_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- revenium_python_sdk-0.1.0.dist-info/licenses/LICENSE +21 -0
- revenium_python_sdk-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared trace visualization field capture and validation.
|
|
3
|
+
|
|
4
|
+
This module provides the canonical implementations of trace field functions
|
|
5
|
+
shared across all provider middlewares. Provider-specific trace_fields modules
|
|
6
|
+
import from here and re-export, keeping only provider-specific functions.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Optional, Dict, Any
|
|
13
|
+
|
|
14
|
+
from .config import Config
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# Re-export environment variable names from core config
|
|
19
|
+
ENV_REVENIUM_ENVIRONMENT = Config.ENV_REVENIUM_ENVIRONMENT
|
|
20
|
+
ENV_ENVIRONMENT = Config.ENV_ENVIRONMENT
|
|
21
|
+
ENV_DEPLOYMENT_ENV = Config.ENV_DEPLOYMENT_ENV
|
|
22
|
+
|
|
23
|
+
ENV_REVENIUM_REGION = Config.ENV_REVENIUM_REGION
|
|
24
|
+
ENV_AWS_REGION = "AWS_REGION"
|
|
25
|
+
ENV_AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"
|
|
26
|
+
ENV_AZURE_REGION = "AZURE_REGION"
|
|
27
|
+
ENV_GCP_REGION = "GCP_REGION"
|
|
28
|
+
ENV_GOOGLE_CLOUD_REGION = "GOOGLE_CLOUD_REGION"
|
|
29
|
+
|
|
30
|
+
ENV_REVENIUM_CREDENTIAL_ALIAS = Config.ENV_REVENIUM_CREDENTIAL_ALIAS
|
|
31
|
+
ENV_REVENIUM_TRACE_TYPE = Config.ENV_REVENIUM_TRACE_TYPE
|
|
32
|
+
ENV_REVENIUM_TRACE_NAME = Config.ENV_REVENIUM_TRACE_NAME
|
|
33
|
+
ENV_REVENIUM_PARENT_TRANSACTION_ID = Config.ENV_REVENIUM_PARENT_TRANSACTION_ID
|
|
34
|
+
ENV_REVENIUM_TRANSACTION_NAME = Config.ENV_REVENIUM_TRANSACTION_NAME
|
|
35
|
+
ENV_REVENIUM_RETRY_NUMBER = Config.ENV_REVENIUM_RETRY_NUMBER
|
|
36
|
+
|
|
37
|
+
# Validation constants
|
|
38
|
+
TRACE_TYPE_MAX_LENGTH = 128
|
|
39
|
+
TRACE_NAME_MAX_LENGTH = 256
|
|
40
|
+
TRACE_TYPE_PATTERN = re.compile(r'^[a-zA-Z0-9_-]+$')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_environment() -> Optional[str]:
|
|
44
|
+
"""
|
|
45
|
+
Get deployment environment from environment variables.
|
|
46
|
+
|
|
47
|
+
Checks in order:
|
|
48
|
+
1. REVENIUM_ENVIRONMENT
|
|
49
|
+
2. ENVIRONMENT
|
|
50
|
+
3. DEPLOYMENT_ENV
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Environment name (e.g., 'production', 'staging') or None
|
|
54
|
+
"""
|
|
55
|
+
return (
|
|
56
|
+
os.getenv(ENV_REVENIUM_ENVIRONMENT) or
|
|
57
|
+
os.getenv(ENV_ENVIRONMENT) or
|
|
58
|
+
os.getenv(ENV_DEPLOYMENT_ENV)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_region() -> Optional[str]:
|
|
63
|
+
"""
|
|
64
|
+
Get cloud region from environment variables.
|
|
65
|
+
|
|
66
|
+
Checks in order:
|
|
67
|
+
1. REVENIUM_REGION
|
|
68
|
+
2. AWS_REGION or AWS_DEFAULT_REGION
|
|
69
|
+
3. AZURE_REGION
|
|
70
|
+
4. GCP_REGION or GOOGLE_CLOUD_REGION
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Region name (e.g., 'us-east-1', 'eastus') or None
|
|
74
|
+
"""
|
|
75
|
+
# Try Revenium-specific env var first
|
|
76
|
+
region = os.getenv(ENV_REVENIUM_REGION)
|
|
77
|
+
if region:
|
|
78
|
+
return region
|
|
79
|
+
|
|
80
|
+
# Try AWS region
|
|
81
|
+
region = os.getenv(ENV_AWS_REGION) or os.getenv(ENV_AWS_DEFAULT_REGION)
|
|
82
|
+
if region:
|
|
83
|
+
return region
|
|
84
|
+
|
|
85
|
+
# Try Azure region
|
|
86
|
+
region = os.getenv(ENV_AZURE_REGION)
|
|
87
|
+
if region:
|
|
88
|
+
return region
|
|
89
|
+
|
|
90
|
+
# Try GCP region
|
|
91
|
+
region = os.getenv(ENV_GCP_REGION) or os.getenv(ENV_GOOGLE_CLOUD_REGION)
|
|
92
|
+
if region:
|
|
93
|
+
return region
|
|
94
|
+
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get_credential_alias() -> Optional[str]:
|
|
99
|
+
"""
|
|
100
|
+
Get credential alias from environment variables.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Credential alias (e.g., 'prod-api-key', 'staging-key') or None
|
|
104
|
+
"""
|
|
105
|
+
return os.getenv(ENV_REVENIUM_CREDENTIAL_ALIAS)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_trace_type() -> Optional[str]:
|
|
109
|
+
"""
|
|
110
|
+
Get and validate trace type from environment variables.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Validated trace type or None if invalid/not set
|
|
114
|
+
"""
|
|
115
|
+
trace_type = os.getenv(ENV_REVENIUM_TRACE_TYPE)
|
|
116
|
+
if trace_type:
|
|
117
|
+
return validate_trace_type(trace_type)
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_trace_name() -> Optional[str]:
|
|
122
|
+
"""
|
|
123
|
+
Get and validate trace name from environment variables.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Validated trace name (truncated if needed) or None if not set
|
|
127
|
+
"""
|
|
128
|
+
trace_name = os.getenv(ENV_REVENIUM_TRACE_NAME)
|
|
129
|
+
if trace_name:
|
|
130
|
+
return validate_trace_name(trace_name)
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_parent_transaction_id() -> Optional[str]:
|
|
135
|
+
"""
|
|
136
|
+
Get parent transaction ID from environment variables.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Parent transaction ID or None
|
|
140
|
+
"""
|
|
141
|
+
return os.getenv(ENV_REVENIUM_PARENT_TRANSACTION_ID)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_transaction_name(usage_metadata: Optional[Dict[str, Any]] = None) -> Optional[str]:
|
|
145
|
+
"""
|
|
146
|
+
Get transaction name with fallback to task_type.
|
|
147
|
+
|
|
148
|
+
Checks in order:
|
|
149
|
+
1. REVENIUM_TRANSACTION_NAME env var
|
|
150
|
+
2. transactionName from usage_metadata
|
|
151
|
+
3. task_type from usage_metadata (fallback)
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
usage_metadata: Optional metadata dictionary
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Transaction name or None
|
|
158
|
+
"""
|
|
159
|
+
# First priority: env var
|
|
160
|
+
transaction_name = os.getenv(ENV_REVENIUM_TRANSACTION_NAME)
|
|
161
|
+
if transaction_name:
|
|
162
|
+
return transaction_name
|
|
163
|
+
|
|
164
|
+
# Second priority: usage_metadata
|
|
165
|
+
if usage_metadata:
|
|
166
|
+
transaction_name = (
|
|
167
|
+
usage_metadata.get('transactionName') or
|
|
168
|
+
usage_metadata.get('transaction_name')
|
|
169
|
+
)
|
|
170
|
+
if transaction_name:
|
|
171
|
+
return transaction_name
|
|
172
|
+
|
|
173
|
+
# Third priority: fallback to task_type
|
|
174
|
+
task_type = (
|
|
175
|
+
usage_metadata.get('task_type') or
|
|
176
|
+
usage_metadata.get('taskType')
|
|
177
|
+
)
|
|
178
|
+
if task_type:
|
|
179
|
+
return task_type
|
|
180
|
+
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get_retry_number() -> int:
|
|
185
|
+
"""
|
|
186
|
+
Get retry number from environment variables.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Retry number (0 for first attempt, 1+ for retries)
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
return int(os.getenv(ENV_REVENIUM_RETRY_NUMBER, '0'))
|
|
193
|
+
except ValueError:
|
|
194
|
+
logger.warning(
|
|
195
|
+
"Invalid REVENIUM_RETRY_NUMBER value, defaulting to 0"
|
|
196
|
+
)
|
|
197
|
+
return 0
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def validate_trace_type(trace_type: str) -> Optional[str]:
|
|
201
|
+
"""
|
|
202
|
+
Validate trace type format and length.
|
|
203
|
+
|
|
204
|
+
Rules:
|
|
205
|
+
- Only alphanumeric characters, hyphens, and underscores
|
|
206
|
+
- Maximum 128 characters
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
trace_type: Trace type to validate
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
Valid trace type or None if invalid
|
|
213
|
+
"""
|
|
214
|
+
if not trace_type:
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
# Check length
|
|
218
|
+
if len(trace_type) > TRACE_TYPE_MAX_LENGTH:
|
|
219
|
+
logger.warning(
|
|
220
|
+
f"traceType exceeds maximum length of "
|
|
221
|
+
f"{TRACE_TYPE_MAX_LENGTH} characters: '{trace_type}'. "
|
|
222
|
+
f"Field will be omitted."
|
|
223
|
+
)
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
# Check format
|
|
227
|
+
if not TRACE_TYPE_PATTERN.match(trace_type):
|
|
228
|
+
logger.warning(
|
|
229
|
+
f"traceType contains invalid characters "
|
|
230
|
+
f"(only alphanumeric, hyphens, and underscores allowed): "
|
|
231
|
+
f"'{trace_type}'. Field will be omitted."
|
|
232
|
+
)
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
return trace_type
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def validate_trace_name(trace_name: str) -> Optional[str]:
|
|
239
|
+
"""
|
|
240
|
+
Validate trace name length and truncate if needed.
|
|
241
|
+
|
|
242
|
+
Rules:
|
|
243
|
+
- Maximum 256 characters
|
|
244
|
+
- Truncates with warning if too long
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
trace_name: Trace name to validate
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Valid trace name (truncated if needed) or None if empty
|
|
251
|
+
"""
|
|
252
|
+
if not trace_name:
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
# Check length and truncate if needed
|
|
256
|
+
if len(trace_name) > TRACE_NAME_MAX_LENGTH:
|
|
257
|
+
logger.warning(
|
|
258
|
+
f"traceName exceeds maximum length of "
|
|
259
|
+
f"{TRACE_NAME_MAX_LENGTH} characters. "
|
|
260
|
+
f"Truncating from {len(trace_name)} to "
|
|
261
|
+
f"{TRACE_NAME_MAX_LENGTH} characters."
|
|
262
|
+
)
|
|
263
|
+
return trace_name[:TRACE_NAME_MAX_LENGTH]
|
|
264
|
+
|
|
265
|
+
return trace_name
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Revenium Middleware for Anthropic Python SDK
|
|
3
|
+
|
|
4
|
+
This library automatically hooks anthropic.resources.messages.create using wrapt,
|
|
5
|
+
and logs token usage after each request. You can customize or extend this logging
|
|
6
|
+
logic later to add user or organization metadata for metering purposes.
|
|
7
|
+
|
|
8
|
+
Now supports AWS Bedrock integration for routing Anthropic Claude models
|
|
9
|
+
through Amazon Bedrock while maintaining the same metering functionality.
|
|
10
|
+
|
|
11
|
+
Auto-initialization: The middleware is automatically initialized on import for
|
|
12
|
+
a simple "just works" experience. For explicit control, use the exported
|
|
13
|
+
initialize() function.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger("revenium_middleware.anthropic.init")
|
|
20
|
+
|
|
21
|
+
# Import provider detection (no SDK dependency)
|
|
22
|
+
from .provider import Provider, detect_provider, get_provider_metadata, is_bedrock_provider
|
|
23
|
+
|
|
24
|
+
# Conditionally import middleware (requires anthropic SDK)
|
|
25
|
+
try:
|
|
26
|
+
import anthropic # noqa: F401
|
|
27
|
+
from . import middleware
|
|
28
|
+
from .middleware import create_wrapper, usage_context
|
|
29
|
+
except ImportError:
|
|
30
|
+
logger.debug("Anthropic SDK (anthropic) not available, middleware not loaded")
|
|
31
|
+
middleware = None # type: ignore
|
|
32
|
+
create_wrapper = None # type: ignore
|
|
33
|
+
usage_context = None # type: ignore
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def initialize():
|
|
37
|
+
"""
|
|
38
|
+
Explicitly initialize the Revenium middleware.
|
|
39
|
+
|
|
40
|
+
This function can be called manually for explicit control over initialization,
|
|
41
|
+
or it's called automatically on import with graceful fallback.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
bool: True if initialization succeeded, False otherwise
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
# Check if required environment variables are set
|
|
48
|
+
api_key = os.getenv("REVENIUM_METERING_API_KEY")
|
|
49
|
+
if not api_key:
|
|
50
|
+
logger.debug("REVENIUM_METERING_API_KEY not set - middleware will not meter requests")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
# The middleware is automatically activated by importing create_wrapper
|
|
54
|
+
# which uses @wrapt.patch_function_wrapper decorator
|
|
55
|
+
logger.debug("Revenium middleware initialized successfully")
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logger.debug(f"Failed to initialize Revenium middleware: {e}")
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def is_initialized():
|
|
64
|
+
"""
|
|
65
|
+
Check if the middleware has been initialized.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
bool: True if middleware is active and ready to meter requests
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
# Check if environment is properly configured
|
|
72
|
+
api_key = os.getenv("REVENIUM_METERING_API_KEY")
|
|
73
|
+
base_url = os.getenv("REVENIUM_METERING_BASE_URL")
|
|
74
|
+
|
|
75
|
+
return bool(api_key and base_url)
|
|
76
|
+
except Exception:
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Auto-initialize with graceful fallback
|
|
81
|
+
try:
|
|
82
|
+
_auto_init_success = initialize()
|
|
83
|
+
if _auto_init_success:
|
|
84
|
+
logger.debug("Auto-initialization successful")
|
|
85
|
+
else:
|
|
86
|
+
logger.debug("Auto-initialization skipped - manual configuration may be needed")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
# Log debug message but don't throw
|
|
89
|
+
# Allow manual configuration later
|
|
90
|
+
logger.debug(f"Auto-initialization failed: {e}")
|
|
91
|
+
_auto_init_success = False
|
|
92
|
+
|
|
93
|
+
# Export functions for explicit control
|
|
94
|
+
__all__ = [
|
|
95
|
+
# Core middleware components
|
|
96
|
+
"create_wrapper",
|
|
97
|
+
"usage_context",
|
|
98
|
+
|
|
99
|
+
# Provider detection
|
|
100
|
+
"Provider",
|
|
101
|
+
"detect_provider",
|
|
102
|
+
"get_provider_metadata",
|
|
103
|
+
"is_bedrock_provider",
|
|
104
|
+
|
|
105
|
+
# Initialization control
|
|
106
|
+
"initialize",
|
|
107
|
+
"is_initialized",
|
|
108
|
+
]
|