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.
Files changed (144) hide show
  1. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/PKG-INFO +157 -4
  2. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/README.md +152 -3
  3. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/pyproject.toml +6 -1
  4. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/__init__.py +19 -0
  5. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/config.py +9 -0
  6. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/context.py +44 -0
  7. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/exceptions.py +42 -0
  8. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/fields.py +24 -1
  9. revenium_python_sdk-0.5.0/revenium_middleware/_core/outcomes.py +424 -0
  10. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/trace_fields.py +58 -0
  11. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/ai.py +8 -0
  12. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_completion_params.py +3 -0
  13. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/agentic_outcomes.py +58 -104
  14. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/bedrock_transport.py +1 -0
  15. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/middleware.py +9 -2
  16. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/trace_fields.py +3 -0
  17. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/_metering.py +2 -0
  18. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/trace_fields.py +6 -0
  19. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/trace_fields.py +4 -0
  20. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/utils.py +5 -0
  21. revenium_python_sdk-0.5.0/revenium_middleware/griptape/__init__.py +35 -0
  22. revenium_python_sdk-0.5.0/revenium_middleware/griptape/_metadata.py +10 -0
  23. revenium_python_sdk-0.5.0/revenium_middleware/griptape/anthropic_driver.py +87 -0
  24. revenium_python_sdk-0.5.0/revenium_middleware/griptape/litellm_driver.py +527 -0
  25. revenium_python_sdk-0.5.0/revenium_middleware/griptape/ollama_driver.py +87 -0
  26. revenium_python_sdk-0.5.0/revenium_middleware/griptape/openai_driver.py +133 -0
  27. revenium_python_sdk-0.5.0/revenium_middleware/griptape/openai_embedding_driver.py +121 -0
  28. revenium_python_sdk-0.5.0/revenium_middleware/griptape/universal_driver.py +527 -0
  29. revenium_python_sdk-0.5.0/revenium_middleware/job_context.py +508 -0
  30. revenium_python_sdk-0.5.0/revenium_middleware/job_history.py +127 -0
  31. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/__init__.py +2 -0
  32. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/decorators.py +110 -1
  33. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/integrations/crewai.py +151 -24
  34. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/middleware.py +5 -0
  35. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/trace_fields.py +3 -0
  36. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/ollama/middleware.py +5 -0
  37. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/ollama/trace_fields.py +3 -0
  38. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/middleware.py +14 -3
  39. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/trace_fields.py +3 -0
  40. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/middleware.py +4 -1
  41. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/perplexity_sdk.py +4 -1
  42. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/trace_fields.py +3 -0
  43. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/PKG-INFO +157 -4
  44. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/SOURCES.txt +11 -0
  45. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/requires.txt +9 -0
  46. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/tests/test_metering.py +30 -0
  47. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/LICENSE +0 -0
  48. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/__init__.py +0 -0
  49. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/decorators.py +0 -0
  50. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/enforcement.py +0 -0
  51. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/log_sanitize.py +0 -0
  52. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/metering.py +0 -0
  53. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/metering_buffer.py +0 -0
  54. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/metering_submission.py +0 -0
  55. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/patch_registry.py +0 -0
  56. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/prompt_extraction.py +0 -0
  57. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_core/subscriber.py +0 -0
  58. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/LICENSE +0 -0
  59. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/__init__.py +0 -0
  60. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_base_client.py +0 -0
  61. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_client.py +0 -0
  62. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_compat.py +0 -0
  63. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_constants.py +0 -0
  64. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_exceptions.py +0 -0
  65. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_files.py +0 -0
  66. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_models.py +0 -0
  67. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_qs.py +0 -0
  68. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_resource.py +0 -0
  69. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_response.py +0 -0
  70. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_streaming.py +0 -0
  71. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_types.py +0 -0
  72. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/__init__.py +0 -0
  73. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_logs.py +0 -0
  74. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_proxy.py +0 -0
  75. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_reflection.py +0 -0
  76. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_resources_proxy.py +0 -0
  77. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_streams.py +0 -0
  78. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_sync.py +0 -0
  79. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_transform.py +0 -0
  80. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_typing.py +0 -0
  81. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_utils/_utils.py +0 -0
  82. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/_version.py +0 -0
  83. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/context.py +0 -0
  84. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/decorator.py +0 -0
  85. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/py.typed +0 -0
  86. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/__init__.py +0 -0
  87. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/apis.py +0 -0
  88. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/resources/events.py +0 -0
  89. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/__init__.py +0 -0
  90. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_audio_params.py +0 -0
  91. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_image_params.py +0 -0
  92. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/ai_create_video_params.py +0 -0
  93. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/api_meter_request_params.py +0 -0
  94. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/api_meter_response_params.py +0 -0
  95. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/event_create_params.py +0 -0
  96. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/_metering/types/metering_response_resource.py +0 -0
  97. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/__init__.py +0 -0
  98. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/bedrock_adapter.py +0 -0
  99. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/config.py +0 -0
  100. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/prompt_extractor.py +0 -0
  101. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/provider.py +0 -0
  102. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/anthropic/stream_create.py +0 -0
  103. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/__init__.py +0 -0
  104. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/config.py +0 -0
  105. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/fal/middleware.py +0 -0
  106. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/__init__.py +0 -0
  107. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/__init__.py +0 -0
  108. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/exceptions.py +0 -0
  109. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/protocols.py +0 -0
  110. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/common/types.py +0 -0
  111. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/config.py +0 -0
  112. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/google_ai/__init__.py +0 -0
  113. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/google_ai/middleware.py +0 -0
  114. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/google_ai/provider.py +0 -0
  115. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/prompt_extractor.py +0 -0
  116. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/vertex_ai/__init__.py +0 -0
  117. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/vertex_ai/middleware.py +0 -0
  118. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/google/vertex_ai/provider.py +0 -0
  119. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/__init__.py +0 -0
  120. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/config.py +0 -0
  121. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/context.py +0 -0
  122. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/hooks.py +0 -0
  123. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/integrations/__init__.py +0 -0
  124. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/client/validation.py +0 -0
  125. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/proxy/__init__.py +0 -0
  126. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/litellm/proxy/middleware.py +0 -0
  127. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/ollama/__init__.py +0 -0
  128. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/__init__.py +0 -0
  129. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/azure_config.py +0 -0
  130. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/azure_model_resolver.py +0 -0
  131. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/config.py +0 -0
  132. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/exceptions.py +0 -0
  133. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/langchain/__init__.py +0 -0
  134. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/langchain/_utils.py +0 -0
  135. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/langchain/unified_handler.py +0 -0
  136. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/prompt_extractor.py +0 -0
  137. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/openai/provider.py +0 -0
  138. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/__init__.py +0 -0
  139. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/perplexity/provider.py +0 -0
  140. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/webhooks/__init__.py +0 -0
  141. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_middleware/webhooks/_verify.py +0 -0
  142. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/dependency_links.txt +0 -0
  143. {revenium_python_sdk-0.3.0 → revenium_python_sdk-0.5.0}/revenium_python_sdk.egg-info/top_level.txt +0 -0
  144. {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.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
- All decorators support static values, extraction from function arguments (`name_from_arg`), or extraction from object attributes (`name_from_attr`).
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
- All decorators support static values, extraction from function arguments (`name_from_arg`), or extraction from object attributes (`name_from_attr`).
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.3.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"
@@ -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)
@@ -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
+ """