revenium-python-sdk 0.2.0__tar.gz → 0.4.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 (136) hide show
  1. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/PKG-INFO +152 -48
  2. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/README.md +151 -47
  3. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/pyproject.toml +1 -1
  4. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/__init__.py +19 -0
  5. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/config.py +9 -0
  6. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/context.py +44 -0
  7. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/exceptions.py +42 -0
  8. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/fields.py +24 -1
  9. revenium_python_sdk-0.4.0/revenium_middleware/_core/outcomes.py +424 -0
  10. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/trace_fields.py +58 -0
  11. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/resources/ai.py +8 -0
  12. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/ai_create_completion_params.py +3 -0
  13. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/agentic_outcomes.py +58 -104
  14. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/__init__.py +11 -0
  15. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/bedrock_adapter.py +36 -14
  16. revenium_python_sdk-0.4.0/revenium_middleware/anthropic/bedrock_transport.py +483 -0
  17. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/middleware.py +23 -4
  18. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/trace_fields.py +3 -0
  19. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/fal/_metering.py +2 -0
  20. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/fal/trace_fields.py +6 -0
  21. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/common/trace_fields.py +4 -0
  22. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/common/utils.py +12 -5
  23. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/google_ai/middleware.py +1 -1
  24. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/vertex_ai/middleware.py +1 -1
  25. revenium_python_sdk-0.4.0/revenium_middleware/job_context.py +508 -0
  26. revenium_python_sdk-0.4.0/revenium_middleware/job_history.py +127 -0
  27. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/__init__.py +2 -0
  28. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/decorators.py +110 -1
  29. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/integrations/crewai.py +151 -24
  30. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/middleware.py +19 -5
  31. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/trace_fields.py +3 -0
  32. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/ollama/middleware.py +5 -0
  33. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/ollama/trace_fields.py +3 -0
  34. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/middleware.py +14 -3
  35. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/trace_fields.py +3 -0
  36. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/perplexity/middleware.py +4 -1
  37. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/perplexity/perplexity_sdk.py +4 -1
  38. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/perplexity/trace_fields.py +3 -0
  39. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_python_sdk.egg-info/PKG-INFO +152 -48
  40. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_python_sdk.egg-info/SOURCES.txt +4 -0
  41. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/tests/test_metering.py +30 -0
  42. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/LICENSE +0 -0
  43. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/__init__.py +0 -0
  44. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/decorators.py +0 -0
  45. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/enforcement.py +0 -0
  46. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/log_sanitize.py +0 -0
  47. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/metering.py +0 -0
  48. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/metering_buffer.py +0 -0
  49. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/metering_submission.py +0 -0
  50. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/patch_registry.py +0 -0
  51. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/prompt_extraction.py +0 -0
  52. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_core/subscriber.py +0 -0
  53. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/LICENSE +0 -0
  54. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/__init__.py +0 -0
  55. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_base_client.py +0 -0
  56. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_client.py +0 -0
  57. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_compat.py +0 -0
  58. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_constants.py +0 -0
  59. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_exceptions.py +0 -0
  60. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_files.py +0 -0
  61. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_models.py +0 -0
  62. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_qs.py +0 -0
  63. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_resource.py +0 -0
  64. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_response.py +0 -0
  65. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_streaming.py +0 -0
  66. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_types.py +0 -0
  67. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/__init__.py +0 -0
  68. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_logs.py +0 -0
  69. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_proxy.py +0 -0
  70. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_reflection.py +0 -0
  71. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_resources_proxy.py +0 -0
  72. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_streams.py +0 -0
  73. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_sync.py +0 -0
  74. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_transform.py +0 -0
  75. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_typing.py +0 -0
  76. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_utils/_utils.py +0 -0
  77. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/_version.py +0 -0
  78. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/context.py +0 -0
  79. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/decorator.py +0 -0
  80. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/py.typed +0 -0
  81. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/resources/__init__.py +0 -0
  82. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/resources/apis.py +0 -0
  83. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/resources/events.py +0 -0
  84. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/__init__.py +0 -0
  85. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/ai_create_audio_params.py +0 -0
  86. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/ai_create_image_params.py +0 -0
  87. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/ai_create_video_params.py +0 -0
  88. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/api_meter_request_params.py +0 -0
  89. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/api_meter_response_params.py +0 -0
  90. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/event_create_params.py +0 -0
  91. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/_metering/types/metering_response_resource.py +0 -0
  92. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/config.py +0 -0
  93. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/prompt_extractor.py +0 -0
  94. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/provider.py +0 -0
  95. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/anthropic/stream_create.py +0 -0
  96. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/fal/__init__.py +0 -0
  97. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/fal/config.py +0 -0
  98. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/fal/middleware.py +0 -0
  99. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/__init__.py +0 -0
  100. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/common/__init__.py +0 -0
  101. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/common/exceptions.py +0 -0
  102. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/common/protocols.py +0 -0
  103. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/common/types.py +0 -0
  104. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/config.py +0 -0
  105. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/google_ai/__init__.py +0 -0
  106. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/google_ai/provider.py +0 -0
  107. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/prompt_extractor.py +0 -0
  108. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/vertex_ai/__init__.py +0 -0
  109. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/google/vertex_ai/provider.py +0 -0
  110. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/__init__.py +0 -0
  111. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/config.py +0 -0
  112. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/context.py +0 -0
  113. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/hooks.py +0 -0
  114. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/integrations/__init__.py +0 -0
  115. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/client/validation.py +0 -0
  116. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/proxy/__init__.py +0 -0
  117. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/litellm/proxy/middleware.py +0 -0
  118. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/ollama/__init__.py +0 -0
  119. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/__init__.py +0 -0
  120. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/azure_config.py +0 -0
  121. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/azure_model_resolver.py +0 -0
  122. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/config.py +0 -0
  123. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/exceptions.py +0 -0
  124. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/langchain/__init__.py +0 -0
  125. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/langchain/_utils.py +0 -0
  126. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/langchain/unified_handler.py +0 -0
  127. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/prompt_extractor.py +0 -0
  128. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/openai/provider.py +0 -0
  129. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/perplexity/__init__.py +0 -0
  130. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/perplexity/provider.py +0 -0
  131. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/webhooks/__init__.py +0 -0
  132. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_middleware/webhooks/_verify.py +0 -0
  133. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_python_sdk.egg-info/dependency_links.txt +0 -0
  134. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_python_sdk.egg-info/requires.txt +0 -0
  135. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/revenium_python_sdk.egg-info/top_level.txt +0 -0
  136. {revenium_python_sdk-0.2.0 → revenium_python_sdk-0.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: revenium-python-sdk
3
- Version: 0.2.0
3
+ Version: 0.4.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
@@ -195,7 +195,7 @@ from dotenv import load_dotenv
195
195
  load_dotenv()
196
196
 
197
197
  import openai
198
- import revenium_middleware_openai # Auto-initializes on import
198
+ import revenium_middleware.openai # Auto-initializes on import
199
199
 
200
200
  client = openai.OpenAI()
201
201
  response = client.chat.completions.create(
@@ -212,6 +212,70 @@ print(response.choices[0].message.content)
212
212
 
213
213
  Emit per-agent terminal outcomes (`CONVERTED`, `DEFLECTED`, `ESCALATED`) alongside completion and tool-event records, so dashboards show business value next to AI cost.
214
214
 
215
+ > **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`.
216
+
217
+ ### JobContext
218
+
219
+ `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.
220
+
221
+ ```python
222
+ from revenium_middleware import JobContext
223
+
224
+ with JobContext("loan-app-12345", type="loan_processing", version="2.1") as job:
225
+ response = client.chat.completions.create(...) # metered against the job automatically
226
+ job.report_outcome(
227
+ execution_status="SUCCESS", # SUCCESS | FAILED | CANCELLED
228
+ outcome_type="CONVERTED",
229
+ outcome_value=500.0,
230
+ outcome_currency="USD",
231
+ )
232
+ ```
233
+
234
+ - **Async:** `async with JobContext(...) as job:` works identically.
235
+ - **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.
236
+ - **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`.
237
+ - **Team resolution:** explicit `team_id=` > `REVENIUM_TEAM_ID` > automatic resolution from the API key; `OutcomeReportingError` is raised if none of these yields a team.
238
+ - **Nesting:** a nested `JobContext` is a different job (replace, not merge); exiting the inner context restores the outer job's fields.
239
+
240
+ 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).
241
+
242
+ ### Amending an Outcome
243
+
244
+ 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.
245
+
246
+ ```python
247
+ from revenium_middleware import JobContext, get_outcome_history
248
+
249
+ # Two weeks after the agent converted the lead at $500,
250
+ # the customer expands to the annual plan.
251
+ job = JobContext.attach("sales-lead-8842")
252
+ job.amend_outcome(
253
+ reason="Customer expanded to the annual plan after the initial conversion",
254
+ outcome_value=750.0,
255
+ )
256
+ job.close()
257
+
258
+ history = get_outcome_history("sales-lead-8842")
259
+ # List[JobOutcomeAmendment], ordered by amendment_sequence (1 = the initial report)
260
+ ```
261
+
262
+ `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.
263
+
264
+ ### Outcome Exceptions
265
+
266
+ All outcome exceptions are importable from `revenium_middleware` and share the `OutcomeReportingError` base, so `except OutcomeReportingError:` catches the whole family:
267
+
268
+ | Exception | Raised when | What to do |
269
+ |-----------|-------------|------------|
270
+ | `OutcomeReportingError` | Base class — configuration failures (no API key available, unresolvable `team_id`) | Fix the key / team configuration |
271
+ | `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` |
272
+ | `OutcomeNotReportedError` | Amending a job that has no outcome yet (backend 422) | Call `report_outcome()` first |
273
+ | `OutcomeAmendConflictError` | A concurrent amendment changed the outcome (backend 409, optimistic lock) | Refetch with `get_outcome_history()` and retry — the SDK does not auto-retry |
274
+
275
+ ### Low-Level Client
276
+
277
+ For manual control over every metric (one `emit_completion` per LLM call, one `emit_tool_event` per tool/step), use `AgenticOutcomeClient` directly:
278
+
215
279
  ```python
216
280
  from revenium_middleware.agentic_outcomes import AgenticOutcomeClient, AgenticOutcomeSettings
217
281
 
@@ -335,7 +399,7 @@ from dotenv import load_dotenv
335
399
  load_dotenv()
336
400
 
337
401
  import openai
338
- import revenium_middleware_openai # Auto-initializes
402
+ import revenium_middleware.openai # Auto-initializes
339
403
 
340
404
  client = openai.OpenAI()
341
405
 
@@ -374,7 +438,7 @@ The middleware automatically detects Azure OpenAI when using `AzureOpenAI()` and
374
438
 
375
439
  ```python
376
440
  from openai import AzureOpenAI
377
- import revenium_middleware_openai
441
+ import revenium_middleware.openai
378
442
 
379
443
  client = AzureOpenAI(
380
444
  azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
@@ -407,7 +471,7 @@ from dotenv import load_dotenv
407
471
  load_dotenv()
408
472
 
409
473
  import anthropic
410
- import revenium_middleware_anthropic # Auto-initializes
474
+ import revenium_middleware.anthropic # Auto-initializes
411
475
 
412
476
  client = anthropic.Anthropic()
413
477
 
@@ -442,7 +506,7 @@ The middleware provides complete AWS Bedrock integration with automatic detectio
442
506
 
443
507
  ```python
444
508
  import anthropic
445
- import revenium_middleware_anthropic
509
+ import revenium_middleware.anthropic
446
510
 
447
511
  # Bedrock is automatically detected when AWS credentials are available
448
512
  # and base_url contains 'amazonaws.com'
@@ -511,7 +575,7 @@ pip install "revenium-python-sdk[google-vertex]"
511
575
  from dotenv import load_dotenv
512
576
  load_dotenv()
513
577
 
514
- import revenium_middleware_google
578
+ import revenium_middleware.google
515
579
  from google import genai
516
580
 
517
581
  client = genai.Client()
@@ -532,7 +596,7 @@ print(response.text)
532
596
  from dotenv import load_dotenv
533
597
  load_dotenv()
534
598
 
535
- import revenium_middleware_google
599
+ import revenium_middleware.google
536
600
  import vertexai
537
601
  from vertexai.generative_models import GenerativeModel
538
602
 
@@ -573,7 +637,7 @@ from dotenv import load_dotenv
573
637
  load_dotenv()
574
638
 
575
639
  import ollama
576
- import revenium_middleware_ollama # Auto-initializes
640
+ import revenium_middleware.ollama # Auto-initializes
577
641
 
578
642
  # Chat completion
579
643
  response = ollama.chat(
@@ -608,7 +672,7 @@ response = ollama.embed(model='nomic-embed-text', input=['Text 1', 'Text 2', 'Te
608
672
 
609
673
  ```python
610
674
  import openai
611
- import revenium_middleware_openai
675
+ import revenium_middleware.openai
612
676
 
613
677
  openai.api_key = 'ollama'
614
678
  openai.base_url = 'http://localhost:11434/v1/'
@@ -636,7 +700,7 @@ Supports all LLM providers available through LiteLLM with two integration patter
636
700
  from dotenv import load_dotenv
637
701
  load_dotenv()
638
702
 
639
- import revenium_middleware_litellm_client.middleware # Auto-initializes
703
+ import revenium_middleware.litellm.client.middleware # Auto-initializes
640
704
  import litellm
641
705
  import os
642
706
 
@@ -659,7 +723,7 @@ Add the callback to your LiteLLM `config.yaml` for server-side integration:
659
723
 
660
724
  ```yaml
661
725
  litellm_settings:
662
- callbacks: ["revenium_middleware_litellm_proxy.middleware.proxy_handler_instance"]
726
+ callbacks: ["revenium_middleware.litellm.proxy.middleware.proxy_handler_instance"]
663
727
  ```
664
728
 
665
729
  When using the LiteLLM proxy, pass metadata via HTTP headers (`x-revenium-*`).
@@ -678,17 +742,47 @@ LiteLLM provides additional tracking decorators beyond the standard `@revenium_m
678
742
  | `@track_product()` | Track product-specific usage |
679
743
  | `@track_subscriber()` | Identify end users |
680
744
  | `@track_quality()` | Track response quality scores |
745
+ | `@track_job()` | Inject agentic job fields for cost/ROI correlation, e.g. `@track_job(job_id="loan-app-12345", type="loan_processing")` |
681
746
 
682
- All decorators support static values, extraction from function arguments (`name_from_arg`), or extraction from object attributes (`name_from_attr`).
747
+ 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
748
 
684
749
  #### CrewAI Integration
685
750
 
686
751
  ```bash
687
- pip install "revenium-middleware-litellm[crewai]"
752
+ pip install "revenium-python-sdk[litellm]" crewai
688
753
  ```
689
754
 
690
755
  Pre-built wrapper for tracking CrewAI agent executions. **Note:** CrewAI requires Python 3.12 or earlier.
691
756
 
757
+ **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).
758
+
759
+ ```python
760
+ from revenium_middleware.litellm.client.integrations.crewai import ReveniumCrewWrapper
761
+
762
+ crew = ReveniumCrewWrapper(
763
+ agents=[support_agent],
764
+ tasks=[triage_task],
765
+ organization_id="AcmeCorp",
766
+ subscription_id="82764738",
767
+ product_id="Platinum",
768
+ agentic_job_id="support-ticket-456",
769
+ agentic_job_name="Support Ticket Triage",
770
+ agentic_job_type="customer_support",
771
+ agentic_job_version="2.0",
772
+ )
773
+ result = crew.kickoff()
774
+
775
+ crew.report_job_outcome(
776
+ execution_status="SUCCESS",
777
+ outcome_type="DEFLECTED",
778
+ outcome_value=25.0,
779
+ )
780
+
781
+ # Later, if the business result changes:
782
+ # crew.amend_job_outcome(reason="Ticket reopened and escalated to a human agent",
783
+ # outcome_type="ESCALATED", outcome_value=0.0)
784
+ ```
785
+
692
786
  **LiteLLM environment variables:**
693
787
  - `LITELLM_PROXY_URL` - Your LiteLLM proxy URL
694
788
  - `LITELLM_API_KEY` - Your LiteLLM proxy API key
@@ -708,7 +802,7 @@ from dotenv import load_dotenv
708
802
  load_dotenv()
709
803
 
710
804
  from openai import OpenAI
711
- import revenium_middleware_perplexity # Auto-patches OpenAI
805
+ import revenium_middleware.perplexity # Auto-patches OpenAI
712
806
 
713
807
  client = OpenAI(
714
808
  api_key=os.getenv("PERPLEXITY_API_KEY"),
@@ -726,7 +820,7 @@ response = client.chat.completions.create(
726
820
 
727
821
  ```python
728
822
  from perplexity import Perplexity
729
- import revenium_middleware_perplexity # Auto-patches Perplexity
823
+ import revenium_middleware.perplexity # Auto-patches Perplexity
730
824
 
731
825
  client = Perplexity(api_key=os.getenv("PERPLEXITY_API_KEY"))
732
826
 
@@ -761,7 +855,7 @@ for chunk in stream:
761
855
  Supports image, video, and audio generation through fal.ai with automatic media type detection.
762
856
 
763
857
  ```python
764
- import revenium_middleware_fal # Auto-activates
858
+ import revenium_middleware.fal # Auto-activates
765
859
  import fal_client
766
860
 
767
861
  result = fal_client.subscribe(
@@ -793,16 +887,23 @@ for image in result.get("images", []):
793
887
 
794
888
  Callback handler that automatically tracks LLM calls, chains, tools, and agent actions.
795
889
 
890
+ ```bash
891
+ pip install "revenium-python-sdk[langchain]"
892
+ ```
893
+
894
+ Wrap any LangChain LLM (or embeddings model) with `wrap()` — the Revenium callback handler is attached for you:
895
+
796
896
  ```python
797
897
  from langchain_openai import ChatOpenAI
798
- from revenium_middleware_langchain import ReveniumCallbackHandler
898
+ from revenium_middleware.openai.langchain import wrap
799
899
 
800
- handler = ReveniumCallbackHandler(
801
- trace_id="session-123",
802
- agent_name="support_agent"
900
+ llm = wrap(
901
+ ChatOpenAI(model="gpt-4o-mini"),
902
+ usage_metadata={
903
+ "trace_id": "session-123",
904
+ "agent": "support_agent",
905
+ },
803
906
  )
804
-
805
- llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler])
806
907
  response = llm.invoke("Hello!")
807
908
  ```
808
909
 
@@ -813,13 +914,14 @@ from langchain_core.prompts import ChatPromptTemplate
813
914
  from langchain_core.output_parsers import StrOutputParser
814
915
 
815
916
  prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
816
- chain = prompt | llm | output_parser
917
+ chain = prompt | llm | StrOutputParser()
817
918
  result = chain.invoke({"topic": "programming"})
818
919
  ```
819
920
 
820
921
  **With agents:**
821
922
 
822
923
  ```python
924
+ from langchain_core.messages import HumanMessage
823
925
  from langchain_core.tools import tool
824
926
  from langgraph.prebuilt import create_react_agent
825
927
 
@@ -829,40 +931,32 @@ def get_weather(city: str) -> str:
829
931
  return f"Sunny, 72F in {city}"
830
932
 
831
933
  agent = create_react_agent(llm, [get_weather])
832
- result = agent.invoke(
833
- {"messages": [HumanMessage(content="Weather in NYC?")]},
834
- config={"callbacks": [handler]}
835
- )
934
+ result = agent.invoke({"messages": [HumanMessage(content="Weather in NYC?")]})
836
935
  ```
837
936
 
838
- **Async support:**
937
+ **Async support:** the handler is async-native — wrap once and use `ainvoke`/`astream` directly:
839
938
 
840
939
  ```python
841
- from revenium_middleware_langchain import AsyncReveniumCallbackHandler
842
-
843
- handler = AsyncReveniumCallbackHandler(trace_id="async-session")
844
- llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler])
940
+ llm = wrap(ChatOpenAI(model="gpt-4o-mini"))
845
941
  response = await llm.ainvoke("Hello!")
846
942
  ```
847
943
 
848
944
  **Supported providers:** OpenAI, Anthropic, Google, AWS Bedrock, Azure OpenAI, Cohere, HuggingFace, Ollama. Provider is auto-detected from LangChain class name or model name prefix.
849
945
 
850
- **Programmatic configuration:**
946
+ **Attaching to an existing LLM:** use `attach_to()` to add tracking in-place, with any of the standard metadata fields (see [Metadata Fields](#metadata-fields)):
851
947
 
852
948
  ```python
853
- from revenium_middleware_langchain import ReveniumCallbackHandler, ReveniumConfig, SubscriberConfig
854
-
855
- config = ReveniumConfig(
856
- api_key="hak_your_api_key",
857
- environment="production",
858
- organization_name="my_org",
859
- product_name="my_product",
860
- subscriber=SubscriberConfig(id="user_123", email="user@example.com"),
861
- )
949
+ from revenium_middleware.openai.langchain import attach_to
862
950
 
863
- handler = ReveniumCallbackHandler(config=config, trace_id="session-123")
951
+ attach_to(llm, usage_metadata={
952
+ "organizationName": "my_org",
953
+ "productName": "my_product",
954
+ "subscriber": {"id": "user_123", "email": "user@example.com"},
955
+ })
864
956
  ```
865
957
 
958
+ Credentials come from the standard environment variables (`REVENIUM_METERING_API_KEY`, `REVENIUM_METERING_BASE_URL`) or `revenium_middleware.configure()`.
959
+
866
960
  ---
867
961
 
868
962
  ## Metadata Fields
@@ -931,6 +1025,7 @@ Enhanced observability fields for tracking AI operations across environments, re
931
1025
  | `parent_transaction_id` | `REVENIUM_PARENT_TRANSACTION_ID` | Parent transaction ID | Link child operations to parents across microservices |
932
1026
  | `transaction_name` | `REVENIUM_TRANSACTION_NAME` | Human-friendly operation name | Label operations (e.g., `"Generate Response"`, `"Analyze Sentiment"`) |
933
1027
  | `retry_number` | `REVENIUM_RETRY_NUMBER` | Retry attempt number (0 = first attempt) | Track retry attempts for failed operations |
1028
+ | `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
1029
 
935
1030
  **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
1031
 
@@ -958,7 +1053,8 @@ response = client.chat.completions.create(
958
1053
  "trace_type": "customer-support",
959
1054
  "trace_name": "Support Chat Session",
960
1055
  "transaction_name": "Generate Response",
961
- "parent_transaction_id": "parent-txn-123"
1056
+ "parent_transaction_id": "parent-txn-123",
1057
+ "ticket_id": "JIRA-123"
962
1058
  }
963
1059
  )
964
1060
  ```
@@ -1179,7 +1275,7 @@ Each field has a maximum length of **50,000 characters**. If exceeded, it's trun
1179
1275
  import os
1180
1276
  os.environ["REVENIUM_CAPTURE_PROMPTS"] = "true"
1181
1277
 
1182
- import revenium_middleware_openai
1278
+ import revenium_middleware.openai
1183
1279
  from openai import OpenAI
1184
1280
 
1185
1281
  client = OpenAI()
@@ -1362,7 +1458,7 @@ print(get_buffer_stats())
1362
1458
  | `REVENIUM_LOG_LEVEL` | `INFO` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
1363
1459
  | `REVENIUM_CAPTURE_PROMPTS` | `false` | Enable prompt capture |
1364
1460
  | `REVENIUM_SELECTIVE_METERING` | `false` | Only meter `@revenium_meter` decorated functions |
1365
- | `REVENIUM_TEAM_ID` | - | Team ID for cost lookups |
1461
+ | `REVENIUM_TEAM_ID` | - | Team ID for cost lookups and outcome reporting (JobContext team resolution) |
1366
1462
  | `REVENIUM_ENVIRONMENT` | - | Deployment environment (auto-detects from `ENVIRONMENT`, `DEPLOYMENT_ENV`) |
1367
1463
  | `REVENIUM_REGION` | - | Cloud region (auto-detects from `AWS_REGION`, `AZURE_REGION`, `GCP_REGION`) |
1368
1464
  | `REVENIUM_CREDENTIAL_ALIAS` | - | Human-readable API key name |
@@ -1371,10 +1467,18 @@ print(get_buffer_stats())
1371
1467
  | `REVENIUM_PARENT_TRANSACTION_ID` | - | Parent transaction ID for distributed tracing |
1372
1468
  | `REVENIUM_TRANSACTION_NAME` | - | Human-friendly operation name |
1373
1469
  | `REVENIUM_RETRY_NUMBER` | - | Retry attempt number |
1470
+ | `REVENIUM_AGENTIC_JOB_ID` | - | Agentic job instance ID attached to all completions in the process (triggers backend job auto-creation) |
1471
+ | `REVENIUM_AGENTIC_JOB_NAME` | - | Human-readable agentic job name |
1472
+ | `REVENIUM_AGENTIC_JOB_TYPE` | - | Agentic job type category |
1473
+ | `REVENIUM_AGENTIC_JOB_VERSION` | - | Agentic job version |
1474
+ | `REVENIUM_OUTCOME_API_KEY` | - | Write-scope key (`rev_sk_`) for the agentic outcomes API (report/amend/history); falls back to `REVENIUM_METERING_API_KEY` |
1475
+ | `REVENIUM_PROFITSTREAM_BASE_URL` | `https://api.revenium.io` | Agentic outcomes API base URL |
1374
1476
  | `REVENIUM_BEDROCK_DISABLE` | - | Set to `1` to disable Bedrock auto-detection |
1375
1477
  | `REVENIUM_BUFFER_MAX_SIZE` | `1000` | Store-and-forward buffer capacity (oldest events evicted when full) |
1376
1478
  | `REVENIUM_BUFFER_FLUSH_INTERVAL` | `30` | Seconds between automatic replay attempts for buffered events |
1377
1479
 
1480
+ 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.
1481
+
1378
1482
  ### Provider-Specific Environment Variables
1379
1483
 
1380
1484
  | Variable | Provider | Description |
@@ -1416,7 +1520,7 @@ print(get_buffer_stats())
1416
1520
 
1417
1521
  **Force direct Anthropic API:** Set `REVENIUM_BEDROCK_DISABLE=1` to disable Bedrock auto-detection.
1418
1522
 
1419
- **Check initialization status:** Use `revenium_middleware_<provider>.is_initialized()` to verify setup.
1523
+ **Check initialization status (Anthropic):** Use `revenium_middleware.anthropic.is_initialized()` to verify setup.
1420
1524
 
1421
1525
  ---
1422
1526