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,912 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Decorator-based metadata injection for Revenium LiteLLM middleware.
|
|
3
|
+
|
|
4
|
+
This module provides convenient decorators for automatically injecting metadata
|
|
5
|
+
into LiteLLM completion calls. Decorators work with both sync and async functions.
|
|
6
|
+
|
|
7
|
+
Available decorators:
|
|
8
|
+
- track_agent: Set agent metadata
|
|
9
|
+
- track_task: Set task_type metadata
|
|
10
|
+
- track_trace: Set trace_id metadata
|
|
11
|
+
- track_organization: Set organization_name metadata
|
|
12
|
+
- track_subscription: Set subscription_id metadata
|
|
13
|
+
- track_product: Set product_name metadata
|
|
14
|
+
- track_subscriber: Set subscriber metadata (id, email, credential)
|
|
15
|
+
- track_quality: Set response_quality_score metadata
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> from revenium_middleware.litellm.client import track_agent, track_task, track_trace
|
|
19
|
+
>>>
|
|
20
|
+
>>> @track_agent("Lead Analyst")
|
|
21
|
+
>>> @track_trace("workflow-123")
|
|
22
|
+
>>> def analyze_market():
|
|
23
|
+
... response = litellm.completion(...)
|
|
24
|
+
... return response
|
|
25
|
+
>>>
|
|
26
|
+
>>> @track_task("research")
|
|
27
|
+
>>> async def research_topic():
|
|
28
|
+
... response = await litellm.acompletion(...)
|
|
29
|
+
... return response
|
|
30
|
+
>>>
|
|
31
|
+
>>> # Dynamic attribute extraction
|
|
32
|
+
>>> @track_agent(name_from_arg="agent_name")
|
|
33
|
+
>>> def process_with_agent(agent_name, data):
|
|
34
|
+
... response = litellm.completion(...)
|
|
35
|
+
... return response
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
import functools
|
|
39
|
+
import inspect
|
|
40
|
+
from typing import Callable, Optional
|
|
41
|
+
from .context import metadata_context
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def track_agent(
|
|
45
|
+
agent: Optional[str] = None,
|
|
46
|
+
*,
|
|
47
|
+
name_from_arg: Optional[str] = None,
|
|
48
|
+
name_from_attr: Optional[str] = None
|
|
49
|
+
) -> Callable:
|
|
50
|
+
"""
|
|
51
|
+
Decorator to automatically inject agent metadata into LiteLLM calls.
|
|
52
|
+
|
|
53
|
+
This decorator sets the 'agent' field in the metadata context for the
|
|
54
|
+
duration of the decorated function. It supports both static agent names
|
|
55
|
+
and dynamic extraction from function arguments or object attributes.
|
|
56
|
+
|
|
57
|
+
Works with both sync and async functions.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
agent: Static agent name to use. Mutually exclusive with name_from_arg
|
|
61
|
+
and name_from_attr.
|
|
62
|
+
name_from_arg: Name of function argument to use as agent name.
|
|
63
|
+
The argument value will be converted to string.
|
|
64
|
+
name_from_attr: Name of object attribute to use as agent name.
|
|
65
|
+
Only works when decorating methods. Uses self.{attr}.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Decorated function that sets agent metadata
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValueError: If multiple or no agent sources are specified
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
>>> # Static agent name
|
|
75
|
+
>>> @track_agent("Lead Analyst")
|
|
76
|
+
>>> def analyze():
|
|
77
|
+
... return litellm.completion(...)
|
|
78
|
+
>>>
|
|
79
|
+
>>> # Dynamic from argument
|
|
80
|
+
>>> @track_agent(name_from_arg="agent_name")
|
|
81
|
+
>>> def process(agent_name, data):
|
|
82
|
+
... return litellm.completion(...)
|
|
83
|
+
>>>
|
|
84
|
+
>>> # Dynamic from object attribute
|
|
85
|
+
>>> class Agent:
|
|
86
|
+
... def __init__(self, name):
|
|
87
|
+
... self.name = name
|
|
88
|
+
...
|
|
89
|
+
... @track_agent(name_from_attr="name")
|
|
90
|
+
... def execute(self):
|
|
91
|
+
... return litellm.completion(...)
|
|
92
|
+
"""
|
|
93
|
+
# Validate arguments
|
|
94
|
+
sources = sum([
|
|
95
|
+
agent is not None,
|
|
96
|
+
name_from_arg is not None,
|
|
97
|
+
name_from_attr is not None
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
if sources == 0:
|
|
101
|
+
raise ValueError("Must specify agent, name_from_arg, or name_from_attr")
|
|
102
|
+
if sources > 1:
|
|
103
|
+
raise ValueError("Can only specify one of: agent, name_from_arg, name_from_attr")
|
|
104
|
+
|
|
105
|
+
def decorator(func: Callable) -> Callable:
|
|
106
|
+
# Check if function is async
|
|
107
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
108
|
+
|
|
109
|
+
if is_async:
|
|
110
|
+
@functools.wraps(func)
|
|
111
|
+
async def async_wrapper(*args, **kwargs):
|
|
112
|
+
# Determine agent name
|
|
113
|
+
agent_name = _get_value(agent, name_from_arg, name_from_attr, args, kwargs, func, "agent")
|
|
114
|
+
|
|
115
|
+
# Set context and call function
|
|
116
|
+
with metadata_context.set(agent=agent_name):
|
|
117
|
+
return await func(*args, **kwargs)
|
|
118
|
+
|
|
119
|
+
return async_wrapper
|
|
120
|
+
else:
|
|
121
|
+
@functools.wraps(func)
|
|
122
|
+
def sync_wrapper(*args, **kwargs):
|
|
123
|
+
# Determine agent name
|
|
124
|
+
agent_name = _get_value(agent, name_from_arg, name_from_attr, args, kwargs, func, "agent")
|
|
125
|
+
|
|
126
|
+
# Set context and call function
|
|
127
|
+
with metadata_context.set(agent=agent_name):
|
|
128
|
+
return func(*args, **kwargs)
|
|
129
|
+
|
|
130
|
+
return sync_wrapper
|
|
131
|
+
|
|
132
|
+
return decorator
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def track_task(
|
|
136
|
+
task_type: Optional[str] = None,
|
|
137
|
+
*,
|
|
138
|
+
type_from_arg: Optional[str] = None,
|
|
139
|
+
type_from_attr: Optional[str] = None
|
|
140
|
+
) -> Callable:
|
|
141
|
+
"""
|
|
142
|
+
Decorator to automatically inject task_type metadata into LiteLLM calls.
|
|
143
|
+
|
|
144
|
+
This decorator sets the 'task_type' field in the metadata context for the
|
|
145
|
+
duration of the decorated function. It supports both static task types
|
|
146
|
+
and dynamic extraction from function arguments or object attributes.
|
|
147
|
+
|
|
148
|
+
Works with both sync and async functions.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
task_type: Static task type to use. Mutually exclusive with type_from_arg
|
|
152
|
+
and type_from_attr.
|
|
153
|
+
type_from_arg: Name of function argument to use as task type.
|
|
154
|
+
The argument value will be converted to string.
|
|
155
|
+
type_from_attr: Name of object attribute to use as task type.
|
|
156
|
+
Only works when decorating methods. Uses self.{attr}.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Decorated function that sets task_type metadata
|
|
160
|
+
|
|
161
|
+
Raises:
|
|
162
|
+
ValueError: If multiple or no task type sources are specified
|
|
163
|
+
|
|
164
|
+
Example:
|
|
165
|
+
>>> # Static task type
|
|
166
|
+
>>> @track_task("research")
|
|
167
|
+
>>> def research():
|
|
168
|
+
... return litellm.completion(...)
|
|
169
|
+
>>>
|
|
170
|
+
>>> # Dynamic from argument
|
|
171
|
+
>>> @track_task(type_from_arg="operation")
|
|
172
|
+
>>> def process(operation, data):
|
|
173
|
+
... return litellm.completion(...)
|
|
174
|
+
>>>
|
|
175
|
+
>>> # Dynamic from object attribute
|
|
176
|
+
>>> class Task:
|
|
177
|
+
... def __init__(self, task_type):
|
|
178
|
+
... self.task_type = task_type
|
|
179
|
+
...
|
|
180
|
+
... @track_task(type_from_attr="task_type")
|
|
181
|
+
... def execute(self):
|
|
182
|
+
... return litellm.completion(...)
|
|
183
|
+
"""
|
|
184
|
+
# Validate arguments
|
|
185
|
+
sources = sum([
|
|
186
|
+
task_type is not None,
|
|
187
|
+
type_from_arg is not None,
|
|
188
|
+
type_from_attr is not None
|
|
189
|
+
])
|
|
190
|
+
|
|
191
|
+
if sources == 0:
|
|
192
|
+
raise ValueError("Must specify task_type, type_from_arg, or type_from_attr")
|
|
193
|
+
if sources > 1:
|
|
194
|
+
raise ValueError("Can only specify one of: task_type, type_from_arg, type_from_attr")
|
|
195
|
+
|
|
196
|
+
def decorator(func: Callable) -> Callable:
|
|
197
|
+
# Check if function is async
|
|
198
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
199
|
+
|
|
200
|
+
if is_async:
|
|
201
|
+
@functools.wraps(func)
|
|
202
|
+
async def async_wrapper(*args, **kwargs):
|
|
203
|
+
# Determine task type
|
|
204
|
+
task_type_value = _get_value(task_type, type_from_arg, type_from_attr, args, kwargs, func, "task_type")
|
|
205
|
+
|
|
206
|
+
# Set context and call function
|
|
207
|
+
with metadata_context.set(task_type=task_type_value):
|
|
208
|
+
return await func(*args, **kwargs)
|
|
209
|
+
|
|
210
|
+
return async_wrapper
|
|
211
|
+
else:
|
|
212
|
+
@functools.wraps(func)
|
|
213
|
+
def sync_wrapper(*args, **kwargs):
|
|
214
|
+
# Determine task type
|
|
215
|
+
task_type_value = _get_value(task_type, type_from_arg, type_from_attr, args, kwargs, func, "task_type")
|
|
216
|
+
|
|
217
|
+
# Set context and call function
|
|
218
|
+
with metadata_context.set(task_type=task_type_value):
|
|
219
|
+
return func(*args, **kwargs)
|
|
220
|
+
|
|
221
|
+
return sync_wrapper
|
|
222
|
+
|
|
223
|
+
return decorator
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _get_value(
|
|
227
|
+
static_value: Optional[str],
|
|
228
|
+
arg_name: Optional[str],
|
|
229
|
+
attr_name: Optional[str],
|
|
230
|
+
args: tuple,
|
|
231
|
+
kwargs: dict,
|
|
232
|
+
func: Callable,
|
|
233
|
+
field_name: str
|
|
234
|
+
) -> str:
|
|
235
|
+
"""
|
|
236
|
+
Generic helper to extract a value from static value, argument, or attribute.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
static_value: Static value
|
|
240
|
+
arg_name: Argument name to extract from
|
|
241
|
+
attr_name: Attribute name to extract from
|
|
242
|
+
args: Positional arguments
|
|
243
|
+
kwargs: Keyword arguments
|
|
244
|
+
func: The decorated function
|
|
245
|
+
field_name: Name of the field (for error messages)
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Value as string
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
ValueError: If argument/attribute not found or invalid
|
|
252
|
+
"""
|
|
253
|
+
if static_value is not None:
|
|
254
|
+
return static_value
|
|
255
|
+
|
|
256
|
+
if arg_name is not None:
|
|
257
|
+
# Try to get from kwargs first
|
|
258
|
+
if arg_name in kwargs:
|
|
259
|
+
return str(kwargs[arg_name])
|
|
260
|
+
|
|
261
|
+
# Try to get from args using function signature
|
|
262
|
+
sig = inspect.signature(func)
|
|
263
|
+
param_names = list(sig.parameters.keys())
|
|
264
|
+
|
|
265
|
+
if arg_name in param_names:
|
|
266
|
+
arg_index = param_names.index(arg_name)
|
|
267
|
+
if arg_index < len(args):
|
|
268
|
+
return str(args[arg_index])
|
|
269
|
+
|
|
270
|
+
raise ValueError(f"Argument '{arg_name}' not found in function call for {field_name}")
|
|
271
|
+
|
|
272
|
+
if attr_name is not None:
|
|
273
|
+
# Get from self (first argument for methods)
|
|
274
|
+
if len(args) == 0:
|
|
275
|
+
raise ValueError(f"Cannot use attr on function without self argument for {field_name}")
|
|
276
|
+
|
|
277
|
+
self_obj = args[0]
|
|
278
|
+
if not hasattr(self_obj, attr_name):
|
|
279
|
+
raise ValueError(f"Object does not have attribute '{attr_name}' for {field_name}")
|
|
280
|
+
|
|
281
|
+
return str(getattr(self_obj, attr_name))
|
|
282
|
+
|
|
283
|
+
raise ValueError(f"No {field_name} source specified")
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def track_trace(
|
|
287
|
+
trace_id: Optional[str] = None,
|
|
288
|
+
*,
|
|
289
|
+
id_from_arg: Optional[str] = None,
|
|
290
|
+
id_from_attr: Optional[str] = None
|
|
291
|
+
) -> Callable:
|
|
292
|
+
"""
|
|
293
|
+
Decorator to automatically inject trace_id metadata into LiteLLM calls.
|
|
294
|
+
|
|
295
|
+
This decorator sets the 'trace_id' field in the metadata context for the
|
|
296
|
+
duration of the decorated function. It supports both static trace IDs
|
|
297
|
+
and dynamic extraction from function arguments or object attributes.
|
|
298
|
+
|
|
299
|
+
Works with both sync and async functions.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
trace_id: Static trace ID to use. Mutually exclusive with id_from_arg
|
|
303
|
+
and id_from_attr.
|
|
304
|
+
id_from_arg: Name of function argument to use as trace ID.
|
|
305
|
+
The argument value will be converted to string.
|
|
306
|
+
id_from_attr: Name of object attribute to use as trace ID.
|
|
307
|
+
Only works when decorating methods. Uses self.{attr}.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Decorated function that sets trace_id metadata
|
|
311
|
+
|
|
312
|
+
Raises:
|
|
313
|
+
ValueError: If multiple or no trace ID sources are specified
|
|
314
|
+
|
|
315
|
+
Example:
|
|
316
|
+
>>> # Static trace ID
|
|
317
|
+
>>> @track_trace("workflow-123")
|
|
318
|
+
>>> def process():
|
|
319
|
+
... return litellm.completion(...)
|
|
320
|
+
>>>
|
|
321
|
+
>>> # Dynamic from argument
|
|
322
|
+
>>> @track_trace(id_from_arg="workflow_id")
|
|
323
|
+
>>> def process(workflow_id, data):
|
|
324
|
+
... return litellm.completion(...)
|
|
325
|
+
"""
|
|
326
|
+
# Validate arguments
|
|
327
|
+
sources = sum([
|
|
328
|
+
trace_id is not None,
|
|
329
|
+
id_from_arg is not None,
|
|
330
|
+
id_from_attr is not None
|
|
331
|
+
])
|
|
332
|
+
|
|
333
|
+
if sources == 0:
|
|
334
|
+
raise ValueError("Must specify trace_id, id_from_arg, or id_from_attr")
|
|
335
|
+
if sources > 1:
|
|
336
|
+
raise ValueError("Can only specify one of: trace_id, id_from_arg, id_from_attr")
|
|
337
|
+
|
|
338
|
+
def decorator(func: Callable) -> Callable:
|
|
339
|
+
# Check if function is async
|
|
340
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
341
|
+
|
|
342
|
+
if is_async:
|
|
343
|
+
@functools.wraps(func)
|
|
344
|
+
async def async_wrapper(*args, **kwargs):
|
|
345
|
+
# Determine trace ID
|
|
346
|
+
trace_id_value = _get_value(trace_id, id_from_arg, id_from_attr, args, kwargs, func, "trace_id")
|
|
347
|
+
|
|
348
|
+
# Set context and call function
|
|
349
|
+
with metadata_context.set(trace_id=trace_id_value):
|
|
350
|
+
return await func(*args, **kwargs)
|
|
351
|
+
|
|
352
|
+
return async_wrapper
|
|
353
|
+
else:
|
|
354
|
+
@functools.wraps(func)
|
|
355
|
+
def sync_wrapper(*args, **kwargs):
|
|
356
|
+
# Determine trace ID
|
|
357
|
+
trace_id_value = _get_value(trace_id, id_from_arg, id_from_attr, args, kwargs, func, "trace_id")
|
|
358
|
+
|
|
359
|
+
# Set context and call function
|
|
360
|
+
with metadata_context.set(trace_id=trace_id_value):
|
|
361
|
+
return func(*args, **kwargs)
|
|
362
|
+
|
|
363
|
+
return sync_wrapper
|
|
364
|
+
|
|
365
|
+
return decorator
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def track_organization(
|
|
369
|
+
organization_name: Optional[str] = None,
|
|
370
|
+
*,
|
|
371
|
+
name_from_arg: Optional[str] = None,
|
|
372
|
+
name_from_attr: Optional[str] = None,
|
|
373
|
+
# Deprecated parameter names for backward compatibility
|
|
374
|
+
organization_id: Optional[str] = None,
|
|
375
|
+
id_from_arg: Optional[str] = None,
|
|
376
|
+
id_from_attr: Optional[str] = None
|
|
377
|
+
) -> Callable:
|
|
378
|
+
"""
|
|
379
|
+
Decorator to automatically inject organization_name metadata into LiteLLM calls.
|
|
380
|
+
|
|
381
|
+
This decorator sets the 'organization_name' field in the metadata context for the
|
|
382
|
+
duration of the decorated function. It supports both static organization names
|
|
383
|
+
and dynamic extraction from function arguments or object attributes.
|
|
384
|
+
|
|
385
|
+
Works with both sync and async functions.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
organization_name: Static organization name to use. Mutually exclusive with
|
|
389
|
+
name_from_arg and name_from_attr.
|
|
390
|
+
name_from_arg: Name of function argument to use as organization name.
|
|
391
|
+
The argument value will be converted to string.
|
|
392
|
+
name_from_attr: Name of object attribute to use as organization name.
|
|
393
|
+
Only works when decorating methods. Uses self.{attr}.
|
|
394
|
+
organization_id: Deprecated. Use organization_name instead.
|
|
395
|
+
id_from_arg: Deprecated. Use name_from_arg instead.
|
|
396
|
+
id_from_attr: Deprecated. Use name_from_attr instead.
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Decorated function that sets organization_name metadata
|
|
400
|
+
|
|
401
|
+
Raises:
|
|
402
|
+
ValueError: If multiple or no organization name sources are specified
|
|
403
|
+
|
|
404
|
+
Example:
|
|
405
|
+
>>> # Static organization name
|
|
406
|
+
>>> @track_organization("AcmeCorp")
|
|
407
|
+
>>> def process():
|
|
408
|
+
... return litellm.completion(...)
|
|
409
|
+
>>>
|
|
410
|
+
>>> # Dynamic from argument
|
|
411
|
+
>>> @track_organization(name_from_arg="org_name")
|
|
412
|
+
>>> def process(org_name, data):
|
|
413
|
+
... return litellm.completion(...)
|
|
414
|
+
"""
|
|
415
|
+
# Support deprecated parameter names (new names take precedence)
|
|
416
|
+
effective_name = organization_name if organization_name is not None else organization_id
|
|
417
|
+
effective_from_arg = name_from_arg if name_from_arg is not None else id_from_arg
|
|
418
|
+
effective_from_attr = name_from_attr if name_from_attr is not None else id_from_attr
|
|
419
|
+
|
|
420
|
+
# Validate arguments
|
|
421
|
+
sources = sum([
|
|
422
|
+
effective_name is not None,
|
|
423
|
+
effective_from_arg is not None,
|
|
424
|
+
effective_from_attr is not None
|
|
425
|
+
])
|
|
426
|
+
|
|
427
|
+
if sources == 0:
|
|
428
|
+
raise ValueError("Must specify organization_name, name_from_arg, or name_from_attr")
|
|
429
|
+
if sources > 1:
|
|
430
|
+
raise ValueError("Can only specify one of: organization_name, name_from_arg, name_from_attr")
|
|
431
|
+
|
|
432
|
+
def decorator(func: Callable) -> Callable:
|
|
433
|
+
# Check if function is async
|
|
434
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
435
|
+
|
|
436
|
+
if is_async:
|
|
437
|
+
@functools.wraps(func)
|
|
438
|
+
async def async_wrapper(*args, **kwargs):
|
|
439
|
+
# Determine organization name
|
|
440
|
+
org_name_value = _get_value(effective_name, effective_from_arg, effective_from_attr, args, kwargs, func, "organization_name")
|
|
441
|
+
|
|
442
|
+
# Set context using organization_name (not organization_id)
|
|
443
|
+
with metadata_context.set(organization_name=org_name_value):
|
|
444
|
+
return await func(*args, **kwargs)
|
|
445
|
+
|
|
446
|
+
return async_wrapper
|
|
447
|
+
else:
|
|
448
|
+
@functools.wraps(func)
|
|
449
|
+
def sync_wrapper(*args, **kwargs):
|
|
450
|
+
# Determine organization name
|
|
451
|
+
org_name_value = _get_value(effective_name, effective_from_arg, effective_from_attr, args, kwargs, func, "organization_name")
|
|
452
|
+
|
|
453
|
+
# Set context using organization_name (not organization_id)
|
|
454
|
+
with metadata_context.set(organization_name=org_name_value):
|
|
455
|
+
return func(*args, **kwargs)
|
|
456
|
+
|
|
457
|
+
return sync_wrapper
|
|
458
|
+
|
|
459
|
+
return decorator
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def track_subscription(
|
|
463
|
+
subscription_id: Optional[str] = None,
|
|
464
|
+
*,
|
|
465
|
+
id_from_arg: Optional[str] = None,
|
|
466
|
+
id_from_attr: Optional[str] = None
|
|
467
|
+
) -> Callable:
|
|
468
|
+
"""
|
|
469
|
+
Decorator to automatically inject subscription_id metadata into LiteLLM calls.
|
|
470
|
+
|
|
471
|
+
This decorator sets the 'subscription_id' field in the metadata context for the
|
|
472
|
+
duration of the decorated function. It supports both static subscription IDs
|
|
473
|
+
and dynamic extraction from function arguments or object attributes.
|
|
474
|
+
|
|
475
|
+
Works with both sync and async functions.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
subscription_id: Static subscription ID to use. Mutually exclusive with
|
|
479
|
+
id_from_arg and id_from_attr.
|
|
480
|
+
id_from_arg: Name of function argument to use as subscription ID.
|
|
481
|
+
The argument value will be converted to string.
|
|
482
|
+
id_from_attr: Name of object attribute to use as subscription ID.
|
|
483
|
+
Only works when decorating methods. Uses self.{attr}.
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
Decorated function that sets subscription_id metadata
|
|
487
|
+
|
|
488
|
+
Raises:
|
|
489
|
+
ValueError: If multiple or no subscription ID sources are specified
|
|
490
|
+
|
|
491
|
+
Example:
|
|
492
|
+
>>> # Static subscription ID
|
|
493
|
+
>>> @track_subscription("sub-123")
|
|
494
|
+
>>> def process():
|
|
495
|
+
... return litellm.completion(...)
|
|
496
|
+
>>>
|
|
497
|
+
>>> # Dynamic from argument
|
|
498
|
+
>>> @track_subscription(id_from_arg="sub_id")
|
|
499
|
+
>>> def process(sub_id, data):
|
|
500
|
+
... return litellm.completion(...)
|
|
501
|
+
"""
|
|
502
|
+
# Validate arguments
|
|
503
|
+
sources = sum([
|
|
504
|
+
subscription_id is not None,
|
|
505
|
+
id_from_arg is not None,
|
|
506
|
+
id_from_attr is not None
|
|
507
|
+
])
|
|
508
|
+
|
|
509
|
+
if sources == 0:
|
|
510
|
+
raise ValueError("Must specify subscription_id, id_from_arg, or id_from_attr")
|
|
511
|
+
if sources > 1:
|
|
512
|
+
raise ValueError("Can only specify one of: subscription_id, id_from_arg, id_from_attr")
|
|
513
|
+
|
|
514
|
+
def decorator(func: Callable) -> Callable:
|
|
515
|
+
# Check if function is async
|
|
516
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
517
|
+
|
|
518
|
+
if is_async:
|
|
519
|
+
@functools.wraps(func)
|
|
520
|
+
async def async_wrapper(*args, **kwargs):
|
|
521
|
+
# Determine subscription ID
|
|
522
|
+
sub_id_value = _get_value(subscription_id, id_from_arg, id_from_attr, args, kwargs, func, "subscription_id")
|
|
523
|
+
|
|
524
|
+
# Set context and call function
|
|
525
|
+
with metadata_context.set(subscription_id=sub_id_value):
|
|
526
|
+
return await func(*args, **kwargs)
|
|
527
|
+
|
|
528
|
+
return async_wrapper
|
|
529
|
+
else:
|
|
530
|
+
@functools.wraps(func)
|
|
531
|
+
def sync_wrapper(*args, **kwargs):
|
|
532
|
+
# Determine subscription ID
|
|
533
|
+
sub_id_value = _get_value(subscription_id, id_from_arg, id_from_attr, args, kwargs, func, "subscription_id")
|
|
534
|
+
|
|
535
|
+
# Set context and call function
|
|
536
|
+
with metadata_context.set(subscription_id=sub_id_value):
|
|
537
|
+
return func(*args, **kwargs)
|
|
538
|
+
|
|
539
|
+
return sync_wrapper
|
|
540
|
+
|
|
541
|
+
return decorator
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def track_product(
|
|
545
|
+
product_name: Optional[str] = None,
|
|
546
|
+
*,
|
|
547
|
+
name_from_arg: Optional[str] = None,
|
|
548
|
+
name_from_attr: Optional[str] = None,
|
|
549
|
+
# Deprecated parameter names for backward compatibility
|
|
550
|
+
product_id: Optional[str] = None,
|
|
551
|
+
id_from_arg: Optional[str] = None,
|
|
552
|
+
id_from_attr: Optional[str] = None
|
|
553
|
+
) -> Callable:
|
|
554
|
+
"""
|
|
555
|
+
Decorator to automatically inject product_name metadata into LiteLLM calls.
|
|
556
|
+
|
|
557
|
+
This decorator sets the 'product_name' field in the metadata context for the
|
|
558
|
+
duration of the decorated function. It supports both static product names
|
|
559
|
+
and dynamic extraction from function arguments or object attributes.
|
|
560
|
+
|
|
561
|
+
Works with both sync and async functions.
|
|
562
|
+
|
|
563
|
+
Args:
|
|
564
|
+
product_name: Static product name to use. Mutually exclusive with
|
|
565
|
+
name_from_arg and name_from_attr.
|
|
566
|
+
name_from_arg: Name of function argument to use as product name.
|
|
567
|
+
The argument value will be converted to string.
|
|
568
|
+
name_from_attr: Name of object attribute to use as product name.
|
|
569
|
+
Only works when decorating methods. Uses self.{attr}.
|
|
570
|
+
product_id: Deprecated. Use product_name instead.
|
|
571
|
+
id_from_arg: Deprecated. Use name_from_arg instead.
|
|
572
|
+
id_from_attr: Deprecated. Use name_from_attr instead.
|
|
573
|
+
|
|
574
|
+
Returns:
|
|
575
|
+
Decorated function that sets product_name metadata
|
|
576
|
+
|
|
577
|
+
Raises:
|
|
578
|
+
ValueError: If multiple or no product name sources are specified
|
|
579
|
+
|
|
580
|
+
Example:
|
|
581
|
+
>>> # Static product name
|
|
582
|
+
>>> @track_product("ai-assistant")
|
|
583
|
+
>>> def process():
|
|
584
|
+
... return litellm.completion(...)
|
|
585
|
+
>>>
|
|
586
|
+
>>> # Dynamic from argument
|
|
587
|
+
>>> @track_product(name_from_arg="prod_name")
|
|
588
|
+
>>> def process(prod_name, data):
|
|
589
|
+
... return litellm.completion(...)
|
|
590
|
+
"""
|
|
591
|
+
# Support deprecated parameter names (new names take precedence)
|
|
592
|
+
effective_name = product_name if product_name is not None else product_id
|
|
593
|
+
effective_from_arg = name_from_arg if name_from_arg is not None else id_from_arg
|
|
594
|
+
effective_from_attr = name_from_attr if name_from_attr is not None else id_from_attr
|
|
595
|
+
|
|
596
|
+
# Validate arguments
|
|
597
|
+
sources = sum([
|
|
598
|
+
effective_name is not None,
|
|
599
|
+
effective_from_arg is not None,
|
|
600
|
+
effective_from_attr is not None
|
|
601
|
+
])
|
|
602
|
+
|
|
603
|
+
if sources == 0:
|
|
604
|
+
raise ValueError("Must specify product_name, name_from_arg, or name_from_attr")
|
|
605
|
+
if sources > 1:
|
|
606
|
+
raise ValueError("Can only specify one of: product_name, name_from_arg, name_from_attr")
|
|
607
|
+
|
|
608
|
+
def decorator(func: Callable) -> Callable:
|
|
609
|
+
# Check if function is async
|
|
610
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
611
|
+
|
|
612
|
+
if is_async:
|
|
613
|
+
@functools.wraps(func)
|
|
614
|
+
async def async_wrapper(*args, **kwargs):
|
|
615
|
+
# Determine product name
|
|
616
|
+
prod_name_value = _get_value(effective_name, effective_from_arg, effective_from_attr, args, kwargs, func, "product_name")
|
|
617
|
+
|
|
618
|
+
# Set context using product_name (not product_id)
|
|
619
|
+
with metadata_context.set(product_name=prod_name_value):
|
|
620
|
+
return await func(*args, **kwargs)
|
|
621
|
+
|
|
622
|
+
return async_wrapper
|
|
623
|
+
else:
|
|
624
|
+
@functools.wraps(func)
|
|
625
|
+
def sync_wrapper(*args, **kwargs):
|
|
626
|
+
# Determine product name
|
|
627
|
+
prod_name_value = _get_value(effective_name, effective_from_arg, effective_from_attr, args, kwargs, func, "product_name")
|
|
628
|
+
|
|
629
|
+
# Set context using product_name (not product_id)
|
|
630
|
+
with metadata_context.set(product_name=prod_name_value):
|
|
631
|
+
return func(*args, **kwargs)
|
|
632
|
+
|
|
633
|
+
return sync_wrapper
|
|
634
|
+
|
|
635
|
+
return decorator
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def _build_subscriber_dict(
|
|
639
|
+
subscriber_id: Optional[str],
|
|
640
|
+
subscriber_email: Optional[str],
|
|
641
|
+
credential_name: Optional[str],
|
|
642
|
+
id_from_arg: Optional[str],
|
|
643
|
+
email_from_arg: Optional[str],
|
|
644
|
+
credential_from_arg: Optional[str],
|
|
645
|
+
args: tuple,
|
|
646
|
+
kwargs: dict,
|
|
647
|
+
func: Callable
|
|
648
|
+
) -> dict:
|
|
649
|
+
"""
|
|
650
|
+
Helper function to build subscriber dictionary from various sources.
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
subscriber_id: Static subscriber ID
|
|
654
|
+
subscriber_email: Static subscriber email
|
|
655
|
+
credential_name: Static credential name
|
|
656
|
+
id_from_arg: Argument name for subscriber ID
|
|
657
|
+
email_from_arg: Argument name for subscriber email
|
|
658
|
+
credential_from_arg: Argument name for credential
|
|
659
|
+
args: Positional arguments
|
|
660
|
+
kwargs: Keyword arguments
|
|
661
|
+
func: The decorated function
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
Dictionary with subscriber information
|
|
665
|
+
"""
|
|
666
|
+
subscriber = {}
|
|
667
|
+
|
|
668
|
+
# Get subscriber ID
|
|
669
|
+
if subscriber_id is not None:
|
|
670
|
+
subscriber['id'] = subscriber_id
|
|
671
|
+
elif id_from_arg is not None:
|
|
672
|
+
try:
|
|
673
|
+
subscriber['id'] = _get_value(None, id_from_arg, None, args, kwargs, func, "subscriber_id")
|
|
674
|
+
except ValueError:
|
|
675
|
+
pass # Optional field, skip if not found
|
|
676
|
+
|
|
677
|
+
# Get subscriber email
|
|
678
|
+
if subscriber_email is not None:
|
|
679
|
+
subscriber['email'] = subscriber_email
|
|
680
|
+
elif email_from_arg is not None:
|
|
681
|
+
try:
|
|
682
|
+
subscriber['email'] = _get_value(None, email_from_arg, None, args, kwargs, func, "subscriber_email")
|
|
683
|
+
except ValueError:
|
|
684
|
+
pass # Optional field, skip if not found
|
|
685
|
+
|
|
686
|
+
# Get credential name
|
|
687
|
+
if credential_name is not None:
|
|
688
|
+
subscriber['credential'] = {'name': credential_name}
|
|
689
|
+
elif credential_from_arg is not None:
|
|
690
|
+
try:
|
|
691
|
+
cred_name = _get_value(None, credential_from_arg, None, args, kwargs, func, "credential_name")
|
|
692
|
+
subscriber['credential'] = {'name': cred_name}
|
|
693
|
+
except ValueError:
|
|
694
|
+
pass # Optional field, skip if not found
|
|
695
|
+
|
|
696
|
+
return subscriber
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def track_subscriber(
|
|
700
|
+
subscriber_id: Optional[str] = None,
|
|
701
|
+
subscriber_email: Optional[str] = None,
|
|
702
|
+
credential_name: Optional[str] = None,
|
|
703
|
+
*,
|
|
704
|
+
id_from_arg: Optional[str] = None,
|
|
705
|
+
email_from_arg: Optional[str] = None,
|
|
706
|
+
credential_from_arg: Optional[str] = None
|
|
707
|
+
) -> Callable:
|
|
708
|
+
"""
|
|
709
|
+
Decorator to automatically inject subscriber metadata into LiteLLM calls.
|
|
710
|
+
|
|
711
|
+
This decorator sets the 'subscriber' field (with id, email, and credential)
|
|
712
|
+
in the metadata context for the duration of the decorated function.
|
|
713
|
+
|
|
714
|
+
Works with both sync and async functions.
|
|
715
|
+
|
|
716
|
+
Args:
|
|
717
|
+
subscriber_id: Static subscriber ID to use.
|
|
718
|
+
subscriber_email: Static subscriber email to use.
|
|
719
|
+
credential_name: Static credential name to use.
|
|
720
|
+
id_from_arg: Name of function argument to use as subscriber ID.
|
|
721
|
+
email_from_arg: Name of function argument to use as subscriber email.
|
|
722
|
+
credential_from_arg: Name of function argument to use as credential name.
|
|
723
|
+
|
|
724
|
+
Returns:
|
|
725
|
+
Decorated function that sets subscriber metadata
|
|
726
|
+
|
|
727
|
+
Example:
|
|
728
|
+
>>> # Static subscriber info
|
|
729
|
+
>>> @track_subscriber(subscriber_id="user-123", subscriber_email="user@example.com")
|
|
730
|
+
>>> def process():
|
|
731
|
+
... return litellm.completion(...)
|
|
732
|
+
>>>
|
|
733
|
+
>>> # Dynamic from arguments
|
|
734
|
+
>>> @track_subscriber(id_from_arg="user_id", email_from_arg="user_email")
|
|
735
|
+
>>> def process(user_id, user_email, data):
|
|
736
|
+
... return litellm.completion(...)
|
|
737
|
+
"""
|
|
738
|
+
def decorator(func: Callable) -> Callable:
|
|
739
|
+
# Check if function is async
|
|
740
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
741
|
+
|
|
742
|
+
if is_async:
|
|
743
|
+
@functools.wraps(func)
|
|
744
|
+
async def async_wrapper(*args, **kwargs):
|
|
745
|
+
# Build subscriber dict
|
|
746
|
+
subscriber = _build_subscriber_dict(
|
|
747
|
+
subscriber_id, subscriber_email, credential_name,
|
|
748
|
+
id_from_arg, email_from_arg, credential_from_arg,
|
|
749
|
+
args, kwargs, func
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
# Set context and call function
|
|
753
|
+
if subscriber:
|
|
754
|
+
with metadata_context.set(subscriber=subscriber):
|
|
755
|
+
return await func(*args, **kwargs)
|
|
756
|
+
else:
|
|
757
|
+
return await func(*args, **kwargs)
|
|
758
|
+
|
|
759
|
+
return async_wrapper
|
|
760
|
+
else:
|
|
761
|
+
@functools.wraps(func)
|
|
762
|
+
def sync_wrapper(*args, **kwargs):
|
|
763
|
+
# Build subscriber dict
|
|
764
|
+
subscriber = _build_subscriber_dict(
|
|
765
|
+
subscriber_id, subscriber_email, credential_name,
|
|
766
|
+
id_from_arg, email_from_arg, credential_from_arg,
|
|
767
|
+
args, kwargs, func
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
# Set context and call function
|
|
771
|
+
if subscriber:
|
|
772
|
+
with metadata_context.set(subscriber=subscriber):
|
|
773
|
+
return func(*args, **kwargs)
|
|
774
|
+
else:
|
|
775
|
+
return func(*args, **kwargs)
|
|
776
|
+
|
|
777
|
+
return sync_wrapper
|
|
778
|
+
|
|
779
|
+
return decorator
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
def track_quality(
|
|
783
|
+
quality_score: Optional[float] = None,
|
|
784
|
+
*,
|
|
785
|
+
score_from_arg: Optional[str] = None,
|
|
786
|
+
score_from_attr: Optional[str] = None
|
|
787
|
+
) -> Callable:
|
|
788
|
+
"""
|
|
789
|
+
Decorator to automatically inject response_quality_score metadata into LiteLLM calls.
|
|
790
|
+
|
|
791
|
+
This decorator sets the 'response_quality_score' field in the metadata context
|
|
792
|
+
for the duration of the decorated function. It supports both static quality scores
|
|
793
|
+
and dynamic extraction from function arguments or object attributes.
|
|
794
|
+
|
|
795
|
+
Works with both sync and async functions.
|
|
796
|
+
|
|
797
|
+
Args:
|
|
798
|
+
quality_score: Static quality score to use (0.0-1.0). Mutually exclusive with
|
|
799
|
+
score_from_arg and score_from_attr.
|
|
800
|
+
score_from_arg: Name of function argument to use as quality score.
|
|
801
|
+
The argument value will be converted to float.
|
|
802
|
+
score_from_attr: Name of object attribute to use as quality score.
|
|
803
|
+
Only works when decorating methods. Uses self.{attr}.
|
|
804
|
+
|
|
805
|
+
Returns:
|
|
806
|
+
Decorated function that sets response_quality_score metadata
|
|
807
|
+
|
|
808
|
+
Raises:
|
|
809
|
+
ValueError: If multiple or no quality score sources are specified
|
|
810
|
+
|
|
811
|
+
Example:
|
|
812
|
+
>>> # Static quality score
|
|
813
|
+
>>> @track_quality(0.95)
|
|
814
|
+
>>> def process():
|
|
815
|
+
... return litellm.completion(...)
|
|
816
|
+
>>>
|
|
817
|
+
>>> # Dynamic from argument
|
|
818
|
+
>>> @track_quality(score_from_arg="min_quality")
|
|
819
|
+
>>> def process(min_quality, data):
|
|
820
|
+
... return litellm.completion(...)
|
|
821
|
+
"""
|
|
822
|
+
# Validate arguments
|
|
823
|
+
sources = sum([
|
|
824
|
+
quality_score is not None,
|
|
825
|
+
score_from_arg is not None,
|
|
826
|
+
score_from_attr is not None
|
|
827
|
+
])
|
|
828
|
+
|
|
829
|
+
if sources == 0:
|
|
830
|
+
raise ValueError("Must specify quality_score, score_from_arg, or score_from_attr")
|
|
831
|
+
if sources > 1:
|
|
832
|
+
raise ValueError("Can only specify one of: quality_score, score_from_arg, score_from_attr")
|
|
833
|
+
|
|
834
|
+
def decorator(func: Callable) -> Callable:
|
|
835
|
+
# Check if function is async
|
|
836
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
837
|
+
|
|
838
|
+
if is_async:
|
|
839
|
+
@functools.wraps(func)
|
|
840
|
+
async def async_wrapper(*args, **kwargs):
|
|
841
|
+
# Determine quality score
|
|
842
|
+
score_value = _get_quality_score(quality_score, score_from_arg, score_from_attr, args, kwargs, func)
|
|
843
|
+
|
|
844
|
+
# Set context and call function
|
|
845
|
+
with metadata_context.set(response_quality_score=score_value):
|
|
846
|
+
return await func(*args, **kwargs)
|
|
847
|
+
|
|
848
|
+
return async_wrapper
|
|
849
|
+
else:
|
|
850
|
+
@functools.wraps(func)
|
|
851
|
+
def sync_wrapper(*args, **kwargs):
|
|
852
|
+
# Determine quality score
|
|
853
|
+
score_value = _get_quality_score(quality_score, score_from_arg, score_from_attr, args, kwargs, func)
|
|
854
|
+
|
|
855
|
+
# Set context and call function
|
|
856
|
+
with metadata_context.set(response_quality_score=score_value):
|
|
857
|
+
return func(*args, **kwargs)
|
|
858
|
+
|
|
859
|
+
return sync_wrapper
|
|
860
|
+
|
|
861
|
+
return decorator
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
def _get_quality_score(
|
|
865
|
+
static_score: Optional[float],
|
|
866
|
+
arg_name: Optional[str],
|
|
867
|
+
attr_name: Optional[str],
|
|
868
|
+
args: tuple,
|
|
869
|
+
kwargs: dict,
|
|
870
|
+
func: Callable
|
|
871
|
+
) -> float:
|
|
872
|
+
"""
|
|
873
|
+
Extract quality score from static value, argument, or attribute.
|
|
874
|
+
|
|
875
|
+
Args:
|
|
876
|
+
static_score: Static quality score
|
|
877
|
+
arg_name: Argument name to extract from
|
|
878
|
+
attr_name: Attribute name to extract from
|
|
879
|
+
args: Positional arguments
|
|
880
|
+
kwargs: Keyword arguments
|
|
881
|
+
func: The decorated function
|
|
882
|
+
|
|
883
|
+
Returns:
|
|
884
|
+
Quality score as float
|
|
885
|
+
|
|
886
|
+
Raises:
|
|
887
|
+
ValueError: If argument/attribute not found or invalid
|
|
888
|
+
"""
|
|
889
|
+
# Use the generic _get_value function and convert to float
|
|
890
|
+
value_str = _get_value(
|
|
891
|
+
str(static_score) if static_score is not None else None,
|
|
892
|
+
arg_name,
|
|
893
|
+
attr_name,
|
|
894
|
+
args,
|
|
895
|
+
kwargs,
|
|
896
|
+
func,
|
|
897
|
+
"quality_score"
|
|
898
|
+
)
|
|
899
|
+
return float(value_str)
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
__all__ = [
|
|
903
|
+
'track_agent',
|
|
904
|
+
'track_task',
|
|
905
|
+
'track_trace',
|
|
906
|
+
'track_organization',
|
|
907
|
+
'track_subscription',
|
|
908
|
+
'track_product',
|
|
909
|
+
'track_subscriber',
|
|
910
|
+
'track_quality',
|
|
911
|
+
]
|
|
912
|
+
|