revenium-python-sdk 0.3.0__tar.gz → 0.5.0__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.
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/PKG-INFO +157 -4
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/README.md +152 -3
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/pyproject.toml +6 -1
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/__init__.py +19 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/config.py +9 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/context.py +44 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/exceptions.py +42 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/fields.py +24 -1
- revenium_python_sdk-0.5.0/revenium_middleware/_core/outcomes.py +424 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/trace_fields.py +58 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/ai.py +8 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_completion_params.py +3 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/agentic_outcomes.py +58 -104
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/bedrock_transport.py +1 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/middleware.py +9 -2
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/trace_fields.py +3 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/_metering.py +2 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/trace_fields.py +6 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/trace_fields.py +4 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/utils.py +5 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/__init__.py +35 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/_metadata.py +10 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/anthropic_driver.py +87 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/litellm_driver.py +527 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/ollama_driver.py +87 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/openai_driver.py +133 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/openai_embedding_driver.py +121 -0
- revenium_python_sdk-0.5.0/revenium_middleware/griptape/universal_driver.py +527 -0
- revenium_python_sdk-0.5.0/revenium_middleware/job_context.py +508 -0
- revenium_python_sdk-0.5.0/revenium_middleware/job_history.py +127 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/__init__.py +2 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/decorators.py +110 -1
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/integrations/crewai.py +151 -24
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/middleware.py +5 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/trace_fields.py +3 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/ollama/middleware.py +5 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/ollama/trace_fields.py +3 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/middleware.py +14 -3
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/trace_fields.py +3 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/middleware.py +4 -1
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/perplexity_sdk.py +4 -1
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/trace_fields.py +3 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/PKG-INFO +157 -4
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/SOURCES.txt +11 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/requires.txt +9 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/tests/test_metering.py +30 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/LICENSE +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/decorators.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/enforcement.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/log_sanitize.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/metering.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/metering_buffer.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/metering_submission.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/patch_registry.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/prompt_extraction.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/subscriber.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/LICENSE +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_base_client.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_client.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_compat.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_constants.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_exceptions.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_files.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_models.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_qs.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_resource.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_response.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_streaming.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_types.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_logs.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_proxy.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_reflection.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_resources_proxy.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_streams.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_sync.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_transform.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_typing.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_utils.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_version.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/context.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/decorator.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/py.typed +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/apis.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/events.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_audio_params.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_image_params.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_video_params.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/api_meter_request_params.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/api_meter_response_params.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/event_create_params.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/metering_response_resource.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/bedrock_adapter.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/config.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/prompt_extractor.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/provider.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/stream_create.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/config.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/middleware.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/exceptions.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/protocols.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/types.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/config.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/google_ai/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/google_ai/middleware.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/google_ai/provider.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/prompt_extractor.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/vertex_ai/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/vertex_ai/middleware.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/vertex_ai/provider.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/config.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/context.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/hooks.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/integrations/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/validation.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/proxy/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/proxy/middleware.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/ollama/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/azure_config.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/azure_model_resolver.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/config.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/exceptions.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/langchain/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/langchain/_utils.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/langchain/unified_handler.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/prompt_extractor.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/provider.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/provider.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/webhooks/__init__.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/webhooks/_verify.py +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/dependency_links.txt +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/top_level.txt +0 -0
- {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: revenium-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: The official Revenium Python SDK — unified AI metering middleware for OpenAI, Anthropic, Google, Ollama, LiteLLM, Perplexity, and fal.ai.
|
|
5
5
|
Author-email: Revenium <support@revenium.io>
|
|
6
6
|
License: MIT
|
|
@@ -36,6 +36,9 @@ Provides-Extra: langchain
|
|
|
36
36
|
Requires-Dist: langchain>=0.1.16; extra == "langchain"
|
|
37
37
|
Requires-Dist: langchain-openai>=0.1.0; extra == "langchain"
|
|
38
38
|
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
|
|
39
|
+
Provides-Extra: griptape
|
|
40
|
+
Requires-Dist: griptape<2.0.0,>=1.0.0; python_version >= "3.10" and extra == "griptape"
|
|
41
|
+
Requires-Dist: requests>=2.25.0; extra == "griptape"
|
|
39
42
|
Provides-Extra: perplexity
|
|
40
43
|
Requires-Dist: wrapt>=1.14.0; extra == "perplexity"
|
|
41
44
|
Requires-Dist: python-dotenv>=0.19.0; extra == "perplexity"
|
|
@@ -89,6 +92,7 @@ Requires-Dist: python-dotenv>=0.19.0; extra == "dev"
|
|
|
89
92
|
Requires-Dist: anthropic>=0.30.0; extra == "dev"
|
|
90
93
|
Requires-Dist: boto3; extra == "dev"
|
|
91
94
|
Requires-Dist: ollama>=0.3.0; extra == "dev"
|
|
95
|
+
Requires-Dist: griptape<2.0.0,>=1.0.0; python_version >= "3.10" and extra == "dev"
|
|
92
96
|
Dynamic: license-file
|
|
93
97
|
|
|
94
98
|
# Revenium Python SDK
|
|
@@ -132,6 +136,7 @@ The official Revenium Python SDK — unified AI metering middleware for deeply a
|
|
|
132
136
|
| Perplexity (Native SDK) | `perplexity-native` | `pip install revenium-python-sdk[perplexity-native]` |
|
|
133
137
|
| fal.ai | `fal` | `pip install revenium-python-sdk[fal]` |
|
|
134
138
|
| LangChain | `langchain` | `pip install revenium-python-sdk[langchain]` |
|
|
139
|
+
| Griptape | `griptape` | `pip install revenium-python-sdk[griptape]` |
|
|
135
140
|
|
|
136
141
|
## Feature Matrix
|
|
137
142
|
|
|
@@ -147,6 +152,7 @@ The official Revenium Python SDK — unified AI metering middleware for deeply a
|
|
|
147
152
|
| Terminal Summary | Yes | Yes | Yes | Yes | Yes | - | - |
|
|
148
153
|
| Azure / Bedrock | Azure | Bedrock | Vertex AI | - | All | - | - |
|
|
149
154
|
| LangChain Integration | Yes | - | - | - | - | - | - |
|
|
155
|
+
| Griptape Integration | Yes | Yes | - | Yes | Yes | - | - |
|
|
150
156
|
| CrewAI Integration | - | - | - | - | Yes | - | - |
|
|
151
157
|
| Proxy Mode | - | - | - | - | Yes | - | - |
|
|
152
158
|
|
|
@@ -212,6 +218,70 @@ print(response.choices[0].message.content)
|
|
|
212
218
|
|
|
213
219
|
Emit per-agent terminal outcomes (`CONVERTED`, `DEFLECTED`, `ESCALATED`) alongside completion and tool-event records, so dashboards show business value next to AI cost.
|
|
214
220
|
|
|
221
|
+
> **You need a write-scope key (`rev_sk_`) to use the agentic outcomes API.** Metering keys (`rev_mk_`) can only meter completions and tool events — they cannot report, amend, or read job outcomes, and the SDK rejects them client-side before any HTTP request is made. Key resolution: explicit `api_key=` > `REVENIUM_OUTCOME_API_KEY` > `REVENIUM_METERING_API_KEY`.
|
|
222
|
+
|
|
223
|
+
### JobContext
|
|
224
|
+
|
|
225
|
+
`JobContext` is the recommended high-level API: every AI call made inside the block is automatically metered against the job (all provider middlewares pick the job fields up from context), and the job's business outcome is reported when the work is done.
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from revenium_middleware import JobContext
|
|
229
|
+
|
|
230
|
+
with JobContext("loan-app-12345", type="loan_processing", version="2.1") as job:
|
|
231
|
+
response = client.chat.completions.create(...) # metered against the job automatically
|
|
232
|
+
job.report_outcome(
|
|
233
|
+
execution_status="SUCCESS", # SUCCESS | FAILED | CANCELLED
|
|
234
|
+
outcome_type="CONVERTED",
|
|
235
|
+
outcome_value=500.0,
|
|
236
|
+
outcome_currency="USD",
|
|
237
|
+
)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- **Async:** `async with JobContext(...) as job:` works identically.
|
|
241
|
+
- **Auto-FAILED:** if an unhandled exception escapes the block before an outcome was reported, the context automatically reports `execution_status="FAILED"` (error message and class in metadata) and always re-raises the original exception.
|
|
242
|
+
- **Blocking:** outcome calls are synchronous HTTP requests with retries; tune how long they may block with the `retry_attempts`, `retry_initial_seconds`, and `retry_max_seconds` arguments, accepted by the `JobContext` constructor, `JobContext.attach()`, `get_outcome_history()`, and the CrewAI wrapper's `report_job_outcome`/`amend_job_outcome`.
|
|
243
|
+
- **Team resolution:** explicit `team_id=` > `REVENIUM_TEAM_ID` > automatic resolution from the API key; `OutcomeReportingError` is raised if none of these yields a team.
|
|
244
|
+
- **Nesting:** a nested `JobContext` is a different job (replace, not merge); exiting the inner context restores the outer job's fields.
|
|
245
|
+
|
|
246
|
+
To tag AI calls with job fields without a context manager, use per-call `usage_metadata={"agentic_job_id": ...}`, the `@track_job` decorator (LiteLLM), or the process-wide `REVENIUM_AGENTIC_JOB_*` environment variables — see [Optional Environment Variables](#optional-environment-variables).
|
|
247
|
+
|
|
248
|
+
### Amending an Outcome
|
|
249
|
+
|
|
250
|
+
Outcomes are amendable: when the business result changes after the fact, amend the recorded outcome instead of re-reporting it. `JobContext.attach()` returns a lightweight handle to an existing job — it is not entered as a context manager and does not touch AI-call scoping — so amendments work from a different process than the one that ran the job.
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
from revenium_middleware import JobContext, get_outcome_history
|
|
254
|
+
|
|
255
|
+
# Two weeks after the agent converted the lead at $500,
|
|
256
|
+
# the customer expands to the annual plan.
|
|
257
|
+
job = JobContext.attach("sales-lead-8842")
|
|
258
|
+
job.amend_outcome(
|
|
259
|
+
reason="Customer expanded to the annual plan after the initial conversion",
|
|
260
|
+
outcome_value=750.0,
|
|
261
|
+
)
|
|
262
|
+
job.close()
|
|
263
|
+
|
|
264
|
+
history = get_outcome_history("sales-lead-8842")
|
|
265
|
+
# List[JobOutcomeAmendment], ordered by amendment_sequence (1 = the initial report)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
`amend_outcome()` takes a mandatory non-blank `reason` plus the same optional fields as `report_outcome()` (`execution_status`, `outcome_type`, `outcome_value`, `outcome_currency`, `metadata`, `reported_by`), and returns the updated job as a dict.
|
|
269
|
+
|
|
270
|
+
### Outcome Exceptions
|
|
271
|
+
|
|
272
|
+
All outcome exceptions are importable from `revenium_middleware` and share the `OutcomeReportingError` base, so `except OutcomeReportingError:` catches the whole family:
|
|
273
|
+
|
|
274
|
+
| Exception | Raised when | What to do |
|
|
275
|
+
|-----------|-------------|------------|
|
|
276
|
+
| `OutcomeReportingError` | Base class — configuration failures (no API key available, unresolvable `team_id`) | Fix the key / team configuration |
|
|
277
|
+
| `OutcomeAlreadyReportedError` | Re-reporting a job that already has an outcome (backend 409) | Amend with `amend_outcome()` instead; the exception carries `reported_at` and `amendment_count` |
|
|
278
|
+
| `OutcomeNotReportedError` | Amending a job that has no outcome yet (backend 422) | Call `report_outcome()` first |
|
|
279
|
+
| `OutcomeAmendConflictError` | A concurrent amendment changed the outcome (backend 409, optimistic lock) | Refetch with `get_outcome_history()` and retry — the SDK does not auto-retry |
|
|
280
|
+
|
|
281
|
+
### Low-Level Client
|
|
282
|
+
|
|
283
|
+
For manual control over every metric (one `emit_completion` per LLM call, one `emit_tool_event` per tool/step), use `AgenticOutcomeClient` directly:
|
|
284
|
+
|
|
215
285
|
```python
|
|
216
286
|
from revenium_middleware.agentic_outcomes import AgenticOutcomeClient, AgenticOutcomeSettings
|
|
217
287
|
|
|
@@ -678,8 +748,9 @@ LiteLLM provides additional tracking decorators beyond the standard `@revenium_m
|
|
|
678
748
|
| `@track_product()` | Track product-specific usage |
|
|
679
749
|
| `@track_subscriber()` | Identify end users |
|
|
680
750
|
| `@track_quality()` | Track response quality scores |
|
|
751
|
+
| `@track_job()` | Inject agentic job fields for cost/ROI correlation, e.g. `@track_job(job_id="loan-app-12345", type="loan_processing")` |
|
|
681
752
|
|
|
682
|
-
|
|
753
|
+
The tracking decorators above support static values, extraction from function arguments (`name_from_arg`), or extraction from object attributes (`name_from_attr`); `@track_job` supports static values and argument extraction (`job_id_from_arg`, `type_from_arg`) but has no attribute variant.
|
|
683
754
|
|
|
684
755
|
#### CrewAI Integration
|
|
685
756
|
|
|
@@ -689,6 +760,35 @@ pip install "revenium-python-sdk[litellm]" crewai
|
|
|
689
760
|
|
|
690
761
|
Pre-built wrapper for tracking CrewAI agent executions. **Note:** CrewAI requires Python 3.12 or earlier.
|
|
691
762
|
|
|
763
|
+
**Job outcome tracking:** pass the `agentic_job_*` kwargs to tie every LLM call in the crew to one agentic job, then report (or later amend) the job's business outcome. Requires a write-scope key (`rev_sk_`) — see [Agentic Outcomes](#agentic-outcomes-outcome-based-metering).
|
|
764
|
+
|
|
765
|
+
```python
|
|
766
|
+
from revenium_middleware.litellm.client.integrations.crewai import ReveniumCrewWrapper
|
|
767
|
+
|
|
768
|
+
crew = ReveniumCrewWrapper(
|
|
769
|
+
agents=[support_agent],
|
|
770
|
+
tasks=[triage_task],
|
|
771
|
+
organization_id="AcmeCorp",
|
|
772
|
+
subscription_id="82764738",
|
|
773
|
+
product_id="Platinum",
|
|
774
|
+
agentic_job_id="support-ticket-456",
|
|
775
|
+
agentic_job_name="Support Ticket Triage",
|
|
776
|
+
agentic_job_type="customer_support",
|
|
777
|
+
agentic_job_version="2.0",
|
|
778
|
+
)
|
|
779
|
+
result = crew.kickoff()
|
|
780
|
+
|
|
781
|
+
crew.report_job_outcome(
|
|
782
|
+
execution_status="SUCCESS",
|
|
783
|
+
outcome_type="DEFLECTED",
|
|
784
|
+
outcome_value=25.0,
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
# Later, if the business result changes:
|
|
788
|
+
# crew.amend_job_outcome(reason="Ticket reopened and escalated to a human agent",
|
|
789
|
+
# outcome_type="ESCALATED", outcome_value=0.0)
|
|
790
|
+
```
|
|
791
|
+
|
|
692
792
|
**LiteLLM environment variables:**
|
|
693
793
|
- `LITELLM_PROXY_URL` - Your LiteLLM proxy URL
|
|
694
794
|
- `LITELLM_API_KEY` - Your LiteLLM proxy API key
|
|
@@ -865,6 +965,49 @@ Credentials come from the standard environment variables (`REVENIUM_METERING_API
|
|
|
865
965
|
|
|
866
966
|
---
|
|
867
967
|
|
|
968
|
+
### Griptape
|
|
969
|
+
|
|
970
|
+
Metered prompt and embedding drivers for [Griptape](https://github.com/griptape-ai/griptape) applications. Requires Python 3.10+.
|
|
971
|
+
|
|
972
|
+
```bash
|
|
973
|
+
pip install "revenium-python-sdk[griptape,openai]" # OpenAI
|
|
974
|
+
pip install "revenium-python-sdk[griptape,anthropic]" # Anthropic
|
|
975
|
+
pip install "revenium-python-sdk[griptape,ollama]" # Ollama
|
|
976
|
+
pip install "revenium-python-sdk[griptape,litellm,litellm-proxy]" # 100+ providers via LiteLLM
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
`ReveniumDriver` auto-detects the provider from the model name (`gpt-*` → OpenAI, `claude-*` → Anthropic, `llama`/`mistral`/... → Ollama, anything else → LiteLLM) and wraps the matching Griptape prompt driver with Revenium metering:
|
|
980
|
+
|
|
981
|
+
```python
|
|
982
|
+
import os
|
|
983
|
+
from griptape.structures import Agent
|
|
984
|
+
from revenium_middleware.griptape import ReveniumDriver
|
|
985
|
+
|
|
986
|
+
os.environ["REVENIUM_METERING_API_KEY"] = "your_revenium_key"
|
|
987
|
+
|
|
988
|
+
agent = Agent(prompt_driver=ReveniumDriver(
|
|
989
|
+
model="gpt-4o-mini",
|
|
990
|
+
usage_metadata={"task_type": "demo"},
|
|
991
|
+
))
|
|
992
|
+
agent.run("Hello!")
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
Force a provider with `force_provider="litellm"`, or wrap an existing driver with `ReveniumDriver(base_driver=...)`.
|
|
996
|
+
|
|
997
|
+
**Embeddings:**
|
|
998
|
+
|
|
999
|
+
```python
|
|
1000
|
+
from revenium_middleware.griptape import ReveniumEmbeddingDriver
|
|
1001
|
+
|
|
1002
|
+
driver = ReveniumEmbeddingDriver(model="text-embedding-3-large")
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
**Provider-specific drivers:** for direct control, use `ReveniumOpenAiDriver`, `ReveniumAnthropicDriver`, `ReveniumOllamaDriver`, `ReveniumLiteLLMDriver` or `ReveniumOpenAiEmbeddingDriver` — each subclasses the corresponding Griptape driver and accepts a `usage_metadata` dict (see [Metadata Fields](#metadata-fields)).
|
|
1006
|
+
|
|
1007
|
+
**Migrating from `revenium-griptape`:** the standalone package is deprecated — install the `griptape` extra and change `from revenium_griptape import ReveniumDriver` to `from revenium_middleware.griptape import ReveniumDriver`. All driver class names are unchanged. One behaviour difference: the old package called `load_dotenv()` automatically at import time; the SDK never mutates your environment on import, so if you keep credentials in a `.env` file, call `load_dotenv()` yourself before creating a driver.
|
|
1008
|
+
|
|
1009
|
+
---
|
|
1010
|
+
|
|
868
1011
|
## Metadata Fields
|
|
869
1012
|
|
|
870
1013
|
Add business context to any API call by passing a `usage_metadata` dictionary. All fields are optional.
|
|
@@ -931,6 +1074,7 @@ Enhanced observability fields for tracking AI operations across environments, re
|
|
|
931
1074
|
| `parent_transaction_id` | `REVENIUM_PARENT_TRANSACTION_ID` | Parent transaction ID | Link child operations to parents across microservices |
|
|
932
1075
|
| `transaction_name` | `REVENIUM_TRANSACTION_NAME` | Human-friendly operation name | Label operations (e.g., `"Generate Response"`, `"Analyze Sentiment"`) |
|
|
933
1076
|
| `retry_number` | `REVENIUM_RETRY_NUMBER` | Retry attempt number (0 = first attempt) | Track retry attempts for failed operations |
|
|
1077
|
+
| `ticket_id` | `REVENIUM_TICKET_ID` | External ticket or issue ID (e.g., Jira, Linear) (max 256 chars) | Attribute AI costs to individual tickets or issues |
|
|
934
1078
|
|
|
935
1079
|
**Note:** `operation_type` (e.g., `CHAT`, `EMBED`, `TOOL_CALL`) and `operation_subtype` (e.g., `function_call`, `streaming`) are automatically detected by the middleware and cannot be overridden.
|
|
936
1080
|
|
|
@@ -958,7 +1102,8 @@ response = client.chat.completions.create(
|
|
|
958
1102
|
"trace_type": "customer-support",
|
|
959
1103
|
"trace_name": "Support Chat Session",
|
|
960
1104
|
"transaction_name": "Generate Response",
|
|
961
|
-
"parent_transaction_id": "parent-txn-123"
|
|
1105
|
+
"parent_transaction_id": "parent-txn-123",
|
|
1106
|
+
"ticket_id": "JIRA-123"
|
|
962
1107
|
}
|
|
963
1108
|
)
|
|
964
1109
|
```
|
|
@@ -1362,7 +1507,7 @@ print(get_buffer_stats())
|
|
|
1362
1507
|
| `REVENIUM_LOG_LEVEL` | `INFO` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
|
|
1363
1508
|
| `REVENIUM_CAPTURE_PROMPTS` | `false` | Enable prompt capture |
|
|
1364
1509
|
| `REVENIUM_SELECTIVE_METERING` | `false` | Only meter `@revenium_meter` decorated functions |
|
|
1365
|
-
| `REVENIUM_TEAM_ID` | - | Team ID for cost lookups |
|
|
1510
|
+
| `REVENIUM_TEAM_ID` | - | Team ID for cost lookups and outcome reporting (JobContext team resolution) |
|
|
1366
1511
|
| `REVENIUM_ENVIRONMENT` | - | Deployment environment (auto-detects from `ENVIRONMENT`, `DEPLOYMENT_ENV`) |
|
|
1367
1512
|
| `REVENIUM_REGION` | - | Cloud region (auto-detects from `AWS_REGION`, `AZURE_REGION`, `GCP_REGION`) |
|
|
1368
1513
|
| `REVENIUM_CREDENTIAL_ALIAS` | - | Human-readable API key name |
|
|
@@ -1371,10 +1516,18 @@ print(get_buffer_stats())
|
|
|
1371
1516
|
| `REVENIUM_PARENT_TRANSACTION_ID` | - | Parent transaction ID for distributed tracing |
|
|
1372
1517
|
| `REVENIUM_TRANSACTION_NAME` | - | Human-friendly operation name |
|
|
1373
1518
|
| `REVENIUM_RETRY_NUMBER` | - | Retry attempt number |
|
|
1519
|
+
| `REVENIUM_AGENTIC_JOB_ID` | - | Agentic job instance ID attached to all completions in the process (triggers backend job auto-creation) |
|
|
1520
|
+
| `REVENIUM_AGENTIC_JOB_NAME` | - | Human-readable agentic job name |
|
|
1521
|
+
| `REVENIUM_AGENTIC_JOB_TYPE` | - | Agentic job type category |
|
|
1522
|
+
| `REVENIUM_AGENTIC_JOB_VERSION` | - | Agentic job version |
|
|
1523
|
+
| `REVENIUM_OUTCOME_API_KEY` | - | Write-scope key (`rev_sk_`) for the agentic outcomes API (report/amend/history); falls back to `REVENIUM_METERING_API_KEY` |
|
|
1524
|
+
| `REVENIUM_PROFITSTREAM_BASE_URL` | `https://api.revenium.io` | Agentic outcomes API base URL |
|
|
1374
1525
|
| `REVENIUM_BEDROCK_DISABLE` | - | Set to `1` to disable Bedrock auto-detection |
|
|
1375
1526
|
| `REVENIUM_BUFFER_MAX_SIZE` | `1000` | Store-and-forward buffer capacity (oldest events evicted when full) |
|
|
1376
1527
|
| `REVENIUM_BUFFER_FLUSH_INTERVAL` | `30` | Seconds between automatic replay attempts for buffered events |
|
|
1377
1528
|
|
|
1529
|
+
Per-call `usage_metadata` values take precedence over the `REVENIUM_AGENTIC_JOB_*` environment variables, and the LiteLLM proxy path sources job fields from `x-revenium-*` headers only — these process-level env fallbacks do not apply to proxied traffic.
|
|
1530
|
+
|
|
1378
1531
|
### Provider-Specific Environment Variables
|
|
1379
1532
|
|
|
1380
1533
|
| Variable | Provider | Description |
|
|
@@ -39,6 +39,7 @@ The official Revenium Python SDK — unified AI metering middleware for deeply a
|
|
|
39
39
|
| Perplexity (Native SDK) | `perplexity-native` | `pip install revenium-python-sdk[perplexity-native]` |
|
|
40
40
|
| fal.ai | `fal` | `pip install revenium-python-sdk[fal]` |
|
|
41
41
|
| LangChain | `langchain` | `pip install revenium-python-sdk[langchain]` |
|
|
42
|
+
| Griptape | `griptape` | `pip install revenium-python-sdk[griptape]` |
|
|
42
43
|
|
|
43
44
|
## Feature Matrix
|
|
44
45
|
|
|
@@ -54,6 +55,7 @@ The official Revenium Python SDK — unified AI metering middleware for deeply a
|
|
|
54
55
|
| Terminal Summary | Yes | Yes | Yes | Yes | Yes | - | - |
|
|
55
56
|
| Azure / Bedrock | Azure | Bedrock | Vertex AI | - | All | - | - |
|
|
56
57
|
| LangChain Integration | Yes | - | - | - | - | - | - |
|
|
58
|
+
| Griptape Integration | Yes | Yes | - | Yes | Yes | - | - |
|
|
57
59
|
| CrewAI Integration | - | - | - | - | Yes | - | - |
|
|
58
60
|
| Proxy Mode | - | - | - | - | Yes | - | - |
|
|
59
61
|
|
|
@@ -119,6 +121,70 @@ print(response.choices[0].message.content)
|
|
|
119
121
|
|
|
120
122
|
Emit per-agent terminal outcomes (`CONVERTED`, `DEFLECTED`, `ESCALATED`) alongside completion and tool-event records, so dashboards show business value next to AI cost.
|
|
121
123
|
|
|
124
|
+
> **You need a write-scope key (`rev_sk_`) to use the agentic outcomes API.** Metering keys (`rev_mk_`) can only meter completions and tool events — they cannot report, amend, or read job outcomes, and the SDK rejects them client-side before any HTTP request is made. Key resolution: explicit `api_key=` > `REVENIUM_OUTCOME_API_KEY` > `REVENIUM_METERING_API_KEY`.
|
|
125
|
+
|
|
126
|
+
### JobContext
|
|
127
|
+
|
|
128
|
+
`JobContext` is the recommended high-level API: every AI call made inside the block is automatically metered against the job (all provider middlewares pick the job fields up from context), and the job's business outcome is reported when the work is done.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from revenium_middleware import JobContext
|
|
132
|
+
|
|
133
|
+
with JobContext("loan-app-12345", type="loan_processing", version="2.1") as job:
|
|
134
|
+
response = client.chat.completions.create(...) # metered against the job automatically
|
|
135
|
+
job.report_outcome(
|
|
136
|
+
execution_status="SUCCESS", # SUCCESS | FAILED | CANCELLED
|
|
137
|
+
outcome_type="CONVERTED",
|
|
138
|
+
outcome_value=500.0,
|
|
139
|
+
outcome_currency="USD",
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- **Async:** `async with JobContext(...) as job:` works identically.
|
|
144
|
+
- **Auto-FAILED:** if an unhandled exception escapes the block before an outcome was reported, the context automatically reports `execution_status="FAILED"` (error message and class in metadata) and always re-raises the original exception.
|
|
145
|
+
- **Blocking:** outcome calls are synchronous HTTP requests with retries; tune how long they may block with the `retry_attempts`, `retry_initial_seconds`, and `retry_max_seconds` arguments, accepted by the `JobContext` constructor, `JobContext.attach()`, `get_outcome_history()`, and the CrewAI wrapper's `report_job_outcome`/`amend_job_outcome`.
|
|
146
|
+
- **Team resolution:** explicit `team_id=` > `REVENIUM_TEAM_ID` > automatic resolution from the API key; `OutcomeReportingError` is raised if none of these yields a team.
|
|
147
|
+
- **Nesting:** a nested `JobContext` is a different job (replace, not merge); exiting the inner context restores the outer job's fields.
|
|
148
|
+
|
|
149
|
+
To tag AI calls with job fields without a context manager, use per-call `usage_metadata={"agentic_job_id": ...}`, the `@track_job` decorator (LiteLLM), or the process-wide `REVENIUM_AGENTIC_JOB_*` environment variables — see [Optional Environment Variables](#optional-environment-variables).
|
|
150
|
+
|
|
151
|
+
### Amending an Outcome
|
|
152
|
+
|
|
153
|
+
Outcomes are amendable: when the business result changes after the fact, amend the recorded outcome instead of re-reporting it. `JobContext.attach()` returns a lightweight handle to an existing job — it is not entered as a context manager and does not touch AI-call scoping — so amendments work from a different process than the one that ran the job.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from revenium_middleware import JobContext, get_outcome_history
|
|
157
|
+
|
|
158
|
+
# Two weeks after the agent converted the lead at $500,
|
|
159
|
+
# the customer expands to the annual plan.
|
|
160
|
+
job = JobContext.attach("sales-lead-8842")
|
|
161
|
+
job.amend_outcome(
|
|
162
|
+
reason="Customer expanded to the annual plan after the initial conversion",
|
|
163
|
+
outcome_value=750.0,
|
|
164
|
+
)
|
|
165
|
+
job.close()
|
|
166
|
+
|
|
167
|
+
history = get_outcome_history("sales-lead-8842")
|
|
168
|
+
# List[JobOutcomeAmendment], ordered by amendment_sequence (1 = the initial report)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`amend_outcome()` takes a mandatory non-blank `reason` plus the same optional fields as `report_outcome()` (`execution_status`, `outcome_type`, `outcome_value`, `outcome_currency`, `metadata`, `reported_by`), and returns the updated job as a dict.
|
|
172
|
+
|
|
173
|
+
### Outcome Exceptions
|
|
174
|
+
|
|
175
|
+
All outcome exceptions are importable from `revenium_middleware` and share the `OutcomeReportingError` base, so `except OutcomeReportingError:` catches the whole family:
|
|
176
|
+
|
|
177
|
+
| Exception | Raised when | What to do |
|
|
178
|
+
|-----------|-------------|------------|
|
|
179
|
+
| `OutcomeReportingError` | Base class — configuration failures (no API key available, unresolvable `team_id`) | Fix the key / team configuration |
|
|
180
|
+
| `OutcomeAlreadyReportedError` | Re-reporting a job that already has an outcome (backend 409) | Amend with `amend_outcome()` instead; the exception carries `reported_at` and `amendment_count` |
|
|
181
|
+
| `OutcomeNotReportedError` | Amending a job that has no outcome yet (backend 422) | Call `report_outcome()` first |
|
|
182
|
+
| `OutcomeAmendConflictError` | A concurrent amendment changed the outcome (backend 409, optimistic lock) | Refetch with `get_outcome_history()` and retry — the SDK does not auto-retry |
|
|
183
|
+
|
|
184
|
+
### Low-Level Client
|
|
185
|
+
|
|
186
|
+
For manual control over every metric (one `emit_completion` per LLM call, one `emit_tool_event` per tool/step), use `AgenticOutcomeClient` directly:
|
|
187
|
+
|
|
122
188
|
```python
|
|
123
189
|
from revenium_middleware.agentic_outcomes import AgenticOutcomeClient, AgenticOutcomeSettings
|
|
124
190
|
|
|
@@ -585,8 +651,9 @@ LiteLLM provides additional tracking decorators beyond the standard `@revenium_m
|
|
|
585
651
|
| `@track_product()` | Track product-specific usage |
|
|
586
652
|
| `@track_subscriber()` | Identify end users |
|
|
587
653
|
| `@track_quality()` | Track response quality scores |
|
|
654
|
+
| `@track_job()` | Inject agentic job fields for cost/ROI correlation, e.g. `@track_job(job_id="loan-app-12345", type="loan_processing")` |
|
|
588
655
|
|
|
589
|
-
|
|
656
|
+
The tracking decorators above support static values, extraction from function arguments (`name_from_arg`), or extraction from object attributes (`name_from_attr`); `@track_job` supports static values and argument extraction (`job_id_from_arg`, `type_from_arg`) but has no attribute variant.
|
|
590
657
|
|
|
591
658
|
#### CrewAI Integration
|
|
592
659
|
|
|
@@ -596,6 +663,35 @@ pip install "revenium-python-sdk[litellm]" crewai
|
|
|
596
663
|
|
|
597
664
|
Pre-built wrapper for tracking CrewAI agent executions. **Note:** CrewAI requires Python 3.12 or earlier.
|
|
598
665
|
|
|
666
|
+
**Job outcome tracking:** pass the `agentic_job_*` kwargs to tie every LLM call in the crew to one agentic job, then report (or later amend) the job's business outcome. Requires a write-scope key (`rev_sk_`) — see [Agentic Outcomes](#agentic-outcomes-outcome-based-metering).
|
|
667
|
+
|
|
668
|
+
```python
|
|
669
|
+
from revenium_middleware.litellm.client.integrations.crewai import ReveniumCrewWrapper
|
|
670
|
+
|
|
671
|
+
crew = ReveniumCrewWrapper(
|
|
672
|
+
agents=[support_agent],
|
|
673
|
+
tasks=[triage_task],
|
|
674
|
+
organization_id="AcmeCorp",
|
|
675
|
+
subscription_id="82764738",
|
|
676
|
+
product_id="Platinum",
|
|
677
|
+
agentic_job_id="support-ticket-456",
|
|
678
|
+
agentic_job_name="Support Ticket Triage",
|
|
679
|
+
agentic_job_type="customer_support",
|
|
680
|
+
agentic_job_version="2.0",
|
|
681
|
+
)
|
|
682
|
+
result = crew.kickoff()
|
|
683
|
+
|
|
684
|
+
crew.report_job_outcome(
|
|
685
|
+
execution_status="SUCCESS",
|
|
686
|
+
outcome_type="DEFLECTED",
|
|
687
|
+
outcome_value=25.0,
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
# Later, if the business result changes:
|
|
691
|
+
# crew.amend_job_outcome(reason="Ticket reopened and escalated to a human agent",
|
|
692
|
+
# outcome_type="ESCALATED", outcome_value=0.0)
|
|
693
|
+
```
|
|
694
|
+
|
|
599
695
|
**LiteLLM environment variables:**
|
|
600
696
|
- `LITELLM_PROXY_URL` - Your LiteLLM proxy URL
|
|
601
697
|
- `LITELLM_API_KEY` - Your LiteLLM proxy API key
|
|
@@ -772,6 +868,49 @@ Credentials come from the standard environment variables (`REVENIUM_METERING_API
|
|
|
772
868
|
|
|
773
869
|
---
|
|
774
870
|
|
|
871
|
+
### Griptape
|
|
872
|
+
|
|
873
|
+
Metered prompt and embedding drivers for [Griptape](https://github.com/griptape-ai/griptape) applications. Requires Python 3.10+.
|
|
874
|
+
|
|
875
|
+
```bash
|
|
876
|
+
pip install "revenium-python-sdk[griptape,openai]" # OpenAI
|
|
877
|
+
pip install "revenium-python-sdk[griptape,anthropic]" # Anthropic
|
|
878
|
+
pip install "revenium-python-sdk[griptape,ollama]" # Ollama
|
|
879
|
+
pip install "revenium-python-sdk[griptape,litellm,litellm-proxy]" # 100+ providers via LiteLLM
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
`ReveniumDriver` auto-detects the provider from the model name (`gpt-*` → OpenAI, `claude-*` → Anthropic, `llama`/`mistral`/... → Ollama, anything else → LiteLLM) and wraps the matching Griptape prompt driver with Revenium metering:
|
|
883
|
+
|
|
884
|
+
```python
|
|
885
|
+
import os
|
|
886
|
+
from griptape.structures import Agent
|
|
887
|
+
from revenium_middleware.griptape import ReveniumDriver
|
|
888
|
+
|
|
889
|
+
os.environ["REVENIUM_METERING_API_KEY"] = "your_revenium_key"
|
|
890
|
+
|
|
891
|
+
agent = Agent(prompt_driver=ReveniumDriver(
|
|
892
|
+
model="gpt-4o-mini",
|
|
893
|
+
usage_metadata={"task_type": "demo"},
|
|
894
|
+
))
|
|
895
|
+
agent.run("Hello!")
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
Force a provider with `force_provider="litellm"`, or wrap an existing driver with `ReveniumDriver(base_driver=...)`.
|
|
899
|
+
|
|
900
|
+
**Embeddings:**
|
|
901
|
+
|
|
902
|
+
```python
|
|
903
|
+
from revenium_middleware.griptape import ReveniumEmbeddingDriver
|
|
904
|
+
|
|
905
|
+
driver = ReveniumEmbeddingDriver(model="text-embedding-3-large")
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
**Provider-specific drivers:** for direct control, use `ReveniumOpenAiDriver`, `ReveniumAnthropicDriver`, `ReveniumOllamaDriver`, `ReveniumLiteLLMDriver` or `ReveniumOpenAiEmbeddingDriver` — each subclasses the corresponding Griptape driver and accepts a `usage_metadata` dict (see [Metadata Fields](#metadata-fields)).
|
|
909
|
+
|
|
910
|
+
**Migrating from `revenium-griptape`:** the standalone package is deprecated — install the `griptape` extra and change `from revenium_griptape import ReveniumDriver` to `from revenium_middleware.griptape import ReveniumDriver`. All driver class names are unchanged. One behaviour difference: the old package called `load_dotenv()` automatically at import time; the SDK never mutates your environment on import, so if you keep credentials in a `.env` file, call `load_dotenv()` yourself before creating a driver.
|
|
911
|
+
|
|
912
|
+
---
|
|
913
|
+
|
|
775
914
|
## Metadata Fields
|
|
776
915
|
|
|
777
916
|
Add business context to any API call by passing a `usage_metadata` dictionary. All fields are optional.
|
|
@@ -838,6 +977,7 @@ Enhanced observability fields for tracking AI operations across environments, re
|
|
|
838
977
|
| `parent_transaction_id` | `REVENIUM_PARENT_TRANSACTION_ID` | Parent transaction ID | Link child operations to parents across microservices |
|
|
839
978
|
| `transaction_name` | `REVENIUM_TRANSACTION_NAME` | Human-friendly operation name | Label operations (e.g., `"Generate Response"`, `"Analyze Sentiment"`) |
|
|
840
979
|
| `retry_number` | `REVENIUM_RETRY_NUMBER` | Retry attempt number (0 = first attempt) | Track retry attempts for failed operations |
|
|
980
|
+
| `ticket_id` | `REVENIUM_TICKET_ID` | External ticket or issue ID (e.g., Jira, Linear) (max 256 chars) | Attribute AI costs to individual tickets or issues |
|
|
841
981
|
|
|
842
982
|
**Note:** `operation_type` (e.g., `CHAT`, `EMBED`, `TOOL_CALL`) and `operation_subtype` (e.g., `function_call`, `streaming`) are automatically detected by the middleware and cannot be overridden.
|
|
843
983
|
|
|
@@ -865,7 +1005,8 @@ response = client.chat.completions.create(
|
|
|
865
1005
|
"trace_type": "customer-support",
|
|
866
1006
|
"trace_name": "Support Chat Session",
|
|
867
1007
|
"transaction_name": "Generate Response",
|
|
868
|
-
"parent_transaction_id": "parent-txn-123"
|
|
1008
|
+
"parent_transaction_id": "parent-txn-123",
|
|
1009
|
+
"ticket_id": "JIRA-123"
|
|
869
1010
|
}
|
|
870
1011
|
)
|
|
871
1012
|
```
|
|
@@ -1269,7 +1410,7 @@ print(get_buffer_stats())
|
|
|
1269
1410
|
| `REVENIUM_LOG_LEVEL` | `INFO` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
|
|
1270
1411
|
| `REVENIUM_CAPTURE_PROMPTS` | `false` | Enable prompt capture |
|
|
1271
1412
|
| `REVENIUM_SELECTIVE_METERING` | `false` | Only meter `@revenium_meter` decorated functions |
|
|
1272
|
-
| `REVENIUM_TEAM_ID` | - | Team ID for cost lookups |
|
|
1413
|
+
| `REVENIUM_TEAM_ID` | - | Team ID for cost lookups and outcome reporting (JobContext team resolution) |
|
|
1273
1414
|
| `REVENIUM_ENVIRONMENT` | - | Deployment environment (auto-detects from `ENVIRONMENT`, `DEPLOYMENT_ENV`) |
|
|
1274
1415
|
| `REVENIUM_REGION` | - | Cloud region (auto-detects from `AWS_REGION`, `AZURE_REGION`, `GCP_REGION`) |
|
|
1275
1416
|
| `REVENIUM_CREDENTIAL_ALIAS` | - | Human-readable API key name |
|
|
@@ -1278,10 +1419,18 @@ print(get_buffer_stats())
|
|
|
1278
1419
|
| `REVENIUM_PARENT_TRANSACTION_ID` | - | Parent transaction ID for distributed tracing |
|
|
1279
1420
|
| `REVENIUM_TRANSACTION_NAME` | - | Human-friendly operation name |
|
|
1280
1421
|
| `REVENIUM_RETRY_NUMBER` | - | Retry attempt number |
|
|
1422
|
+
| `REVENIUM_AGENTIC_JOB_ID` | - | Agentic job instance ID attached to all completions in the process (triggers backend job auto-creation) |
|
|
1423
|
+
| `REVENIUM_AGENTIC_JOB_NAME` | - | Human-readable agentic job name |
|
|
1424
|
+
| `REVENIUM_AGENTIC_JOB_TYPE` | - | Agentic job type category |
|
|
1425
|
+
| `REVENIUM_AGENTIC_JOB_VERSION` | - | Agentic job version |
|
|
1426
|
+
| `REVENIUM_OUTCOME_API_KEY` | - | Write-scope key (`rev_sk_`) for the agentic outcomes API (report/amend/history); falls back to `REVENIUM_METERING_API_KEY` |
|
|
1427
|
+
| `REVENIUM_PROFITSTREAM_BASE_URL` | `https://api.revenium.io` | Agentic outcomes API base URL |
|
|
1281
1428
|
| `REVENIUM_BEDROCK_DISABLE` | - | Set to `1` to disable Bedrock auto-detection |
|
|
1282
1429
|
| `REVENIUM_BUFFER_MAX_SIZE` | `1000` | Store-and-forward buffer capacity (oldest events evicted when full) |
|
|
1283
1430
|
| `REVENIUM_BUFFER_FLUSH_INTERVAL` | `30` | Seconds between automatic replay attempts for buffered events |
|
|
1284
1431
|
|
|
1432
|
+
Per-call `usage_metadata` values take precedence over the `REVENIUM_AGENTIC_JOB_*` environment variables, and the LiteLLM proxy path sources job fields from `x-revenium-*` headers only — these process-level env fallbacks do not apply to proxied traffic.
|
|
1433
|
+
|
|
1285
1434
|
### Provider-Specific Environment Variables
|
|
1286
1435
|
|
|
1287
1436
|
| Variable | Provider | Description |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "revenium-python-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "The official Revenium Python SDK — unified AI metering middleware for OpenAI, Anthropic, Google, Ollama, LiteLLM, Perplexity, and fal.ai."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -51,6 +51,10 @@ langchain = [
|
|
|
51
51
|
"langchain-openai>=0.1.0",
|
|
52
52
|
"langchain-core>=0.1.0",
|
|
53
53
|
]
|
|
54
|
+
griptape = [
|
|
55
|
+
"griptape>=1.0.0,<2.0.0; python_version >= '3.10'",
|
|
56
|
+
"requests>=2.25.0",
|
|
57
|
+
]
|
|
54
58
|
perplexity = [
|
|
55
59
|
"wrapt>=1.14.0",
|
|
56
60
|
"python-dotenv>=0.19.0",
|
|
@@ -116,6 +120,7 @@ dev = [
|
|
|
116
120
|
"anthropic>=0.30.0",
|
|
117
121
|
"boto3",
|
|
118
122
|
"ollama>=0.3.0",
|
|
123
|
+
"griptape>=1.0.0,<2.0.0; python_version >= '3.10'",
|
|
119
124
|
]
|
|
120
125
|
|
|
121
126
|
[tool.pytest.ini_options]
|
|
@@ -166,6 +166,16 @@ from ._metering import meter_tool, report_tool_call, configure # noqa: E402
|
|
|
166
166
|
# Agentic-outcome client (used by examples/agentic_outcomes/ pack)
|
|
167
167
|
from .agentic_outcomes import AgenticOutcomeClient, AgenticOutcomeSettings # noqa: E402
|
|
168
168
|
|
|
169
|
+
# Public job-context surface (BACK-777)
|
|
170
|
+
from .job_context import JobContext # noqa: E402
|
|
171
|
+
from .job_history import JobOutcomeAmendment, get_outcome_history # noqa: E402
|
|
172
|
+
from ._core.exceptions import ( # noqa: E402
|
|
173
|
+
OutcomeAlreadyReportedError,
|
|
174
|
+
OutcomeAmendConflictError,
|
|
175
|
+
OutcomeNotReportedError,
|
|
176
|
+
OutcomeReportingError,
|
|
177
|
+
)
|
|
178
|
+
|
|
169
179
|
__all__ = [
|
|
170
180
|
# Metering exports
|
|
171
181
|
"client",
|
|
@@ -199,4 +209,13 @@ __all__ = [
|
|
|
199
209
|
# Agentic-outcome exports
|
|
200
210
|
"AgenticOutcomeClient",
|
|
201
211
|
"AgenticOutcomeSettings",
|
|
212
|
+
# Job-context exports (BACK-777)
|
|
213
|
+
"JobContext",
|
|
214
|
+
"OutcomeReportingError",
|
|
215
|
+
"OutcomeAlreadyReportedError",
|
|
216
|
+
# Outcome amendment / history exports (BACK-777 Phase 3)
|
|
217
|
+
"get_outcome_history",
|
|
218
|
+
"JobOutcomeAmendment",
|
|
219
|
+
"OutcomeNotReportedError",
|
|
220
|
+
"OutcomeAmendConflictError",
|
|
202
221
|
]
|
|
@@ -56,6 +56,15 @@ class Config:
|
|
|
56
56
|
ENV_REVENIUM_PARENT_TRANSACTION_ID: str = "REVENIUM_PARENT_TRANSACTION_ID"
|
|
57
57
|
ENV_REVENIUM_TRANSACTION_NAME: str = "REVENIUM_TRANSACTION_NAME"
|
|
58
58
|
ENV_REVENIUM_RETRY_NUMBER: str = "REVENIUM_RETRY_NUMBER"
|
|
59
|
+
ENV_REVENIUM_TICKET_ID: str = "REVENIUM_TICKET_ID"
|
|
60
|
+
|
|
61
|
+
# Agentic job field environment variables (BACK-777 fallbacks)
|
|
62
|
+
ENV_REVENIUM_AGENTIC_JOB_ID: str = "REVENIUM_AGENTIC_JOB_ID"
|
|
63
|
+
ENV_REVENIUM_AGENTIC_JOB_NAME: str = "REVENIUM_AGENTIC_JOB_NAME"
|
|
64
|
+
ENV_REVENIUM_AGENTIC_JOB_TYPE: str = "REVENIUM_AGENTIC_JOB_TYPE"
|
|
65
|
+
ENV_REVENIUM_AGENTIC_JOB_VERSION: str = "REVENIUM_AGENTIC_JOB_VERSION"
|
|
66
|
+
ENV_REVENIUM_OUTCOME_API_KEY: str = "REVENIUM_OUTCOME_API_KEY"
|
|
67
|
+
ENV_REVENIUM_PROFITSTREAM_BASE_URL: str = "REVENIUM_PROFITSTREAM_BASE_URL"
|
|
59
68
|
|
|
60
69
|
# Prompt capture settings
|
|
61
70
|
ENV_REVENIUM_CAPTURE_PROMPTS: str = "REVENIUM_CAPTURE_PROMPTS"
|
{revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/context.py
RENAMED
|
@@ -30,6 +30,12 @@ _idempotency_key_context: contextvars.ContextVar[Optional[str]] = contextvars.Co
|
|
|
30
30
|
'revenium_idempotency_key', default=None
|
|
31
31
|
)
|
|
32
32
|
|
|
33
|
+
# Context variable holding agentic job fields (wire-name keys) for the current scope.
|
|
34
|
+
# This is the seam the public JobContext (BACK-777 Phase 2) builds on.
|
|
35
|
+
_agentic_job_context: contextvars.ContextVar[Optional[Dict[str, Any]]] = contextvars.ContextVar(
|
|
36
|
+
'revenium_agentic_job', default=None
|
|
37
|
+
)
|
|
38
|
+
|
|
33
39
|
|
|
34
40
|
def is_inside_decorated_function() -> bool:
|
|
35
41
|
"""
|
|
@@ -172,3 +178,41 @@ def idempotency_key(key: str) -> Iterator[None]:
|
|
|
172
178
|
finally:
|
|
173
179
|
_idempotency_key_context.reset(token)
|
|
174
180
|
|
|
181
|
+
|
|
182
|
+
def get_agentic_job_fields() -> Optional[Dict[str, Any]]:
|
|
183
|
+
"""Return the agentic job fields set on the current context, or None.
|
|
184
|
+
|
|
185
|
+
Keys are wire names (``agenticJobId``, ``agenticJobName``, ``agenticJobType``,
|
|
186
|
+
``agenticJobVersion``) — the same keys ``extract_agentic_job_fields`` emits,
|
|
187
|
+
so the fallback merge in ``_core/fields.py`` is key-aligned.
|
|
188
|
+
"""
|
|
189
|
+
return _agentic_job_context.get()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def set_agentic_job_fields(
|
|
193
|
+
job_id: Optional[str] = None,
|
|
194
|
+
name: Optional[str] = None,
|
|
195
|
+
type: Optional[str] = None,
|
|
196
|
+
version: Optional[str] = None,
|
|
197
|
+
) -> contextvars.Token:
|
|
198
|
+
"""Set agentic job fields on the current context. Returns a Token for reset().
|
|
199
|
+
|
|
200
|
+
Maps snake-case arguments to wire-name keys, omitting Nones. Callers own the
|
|
201
|
+
token and must reset it (``_agentic_job_context.reset(token)``) when the scope
|
|
202
|
+
ends — the JobContext context manager in Phase 2 does this on exit.
|
|
203
|
+
|
|
204
|
+
Raises:
|
|
205
|
+
ValueError: if no field is provided.
|
|
206
|
+
"""
|
|
207
|
+
fields: Dict[str, Any] = {}
|
|
208
|
+
if job_id is not None:
|
|
209
|
+
fields["agenticJobId"] = job_id
|
|
210
|
+
if name is not None:
|
|
211
|
+
fields["agenticJobName"] = name
|
|
212
|
+
if type is not None:
|
|
213
|
+
fields["agenticJobType"] = type
|
|
214
|
+
if version is not None:
|
|
215
|
+
fields["agenticJobVersion"] = version
|
|
216
|
+
if not fields:
|
|
217
|
+
raise ValueError("set_agentic_job_fields requires at least one field")
|
|
218
|
+
return _agentic_job_context.set(fields)
|
{revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/exceptions.py
RENAMED
|
@@ -41,3 +41,45 @@ class BudgetExceededError(Exception):
|
|
|
41
41
|
# `except ReveniumCostLimitExceeded:` continues to catch the new exception
|
|
42
42
|
# unchanged. Plan to remove in a future major release.
|
|
43
43
|
ReveniumCostLimitExceeded = BudgetExceededError
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class OutcomeReportingError(Exception):
|
|
47
|
+
"""Raised when an agentic job outcome cannot be reported.
|
|
48
|
+
|
|
49
|
+
Base of the outcome exception family (BACK-777). Configuration failures
|
|
50
|
+
(unresolvable team_id, missing API key) raise this class directly;
|
|
51
|
+
backend-state conditions raise the subclasses.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class OutcomeAlreadyReportedError(OutcomeReportingError):
|
|
56
|
+
"""The job already has an outcome (backend 409 with amendment guidance).
|
|
57
|
+
|
|
58
|
+
Callers can inspect ``reported_at`` / ``amendment_count`` and decide to
|
|
59
|
+
amend (``amend_outcome``, BACK-777 Phase 3).
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
message: str,
|
|
65
|
+
reported_at: Optional[str] = None,
|
|
66
|
+
amendment_count: Optional[int] = None,
|
|
67
|
+
):
|
|
68
|
+
super().__init__(message)
|
|
69
|
+
self.reported_at = reported_at
|
|
70
|
+
self.amendment_count = amendment_count
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class OutcomeNotReportedError(OutcomeReportingError):
|
|
74
|
+
"""Amendment attempted on a job with no outcome yet (backend 422).
|
|
75
|
+
|
|
76
|
+
Report the initial outcome with ``report_outcome`` first.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class OutcomeAmendConflictError(OutcomeReportingError):
|
|
81
|
+
"""Concurrent amendment changed the outcome row (backend 409, optimistic lock).
|
|
82
|
+
|
|
83
|
+
Retryable by the caller: refetch the current state (``get_outcome_history``)
|
|
84
|
+
and re-issue the amendment. The SDK does not auto-retry.
|
|
85
|
+
"""
|