revenium-python-sdk 0.1.4__tar.gz → 0.1.6__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 (89) hide show
  1. {revenium_python_sdk-0.1.4/revenium_python_sdk.egg-info → revenium_python_sdk-0.1.6}/PKG-INFO +46 -72
  2. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/README.md +45 -71
  3. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/pyproject.toml +1 -1
  4. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/__init__.py +6 -0
  5. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/__init__.py +2 -2
  6. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/config.py +2 -41
  7. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/enforcement.py +5 -5
  8. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/exceptions.py +11 -3
  9. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/fields.py +28 -6
  10. revenium_python_sdk-0.1.6/revenium_middleware/agentic_outcomes.py +348 -0
  11. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/anthropic/bedrock_adapter.py +5 -0
  12. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/anthropic/config.py +0 -3
  13. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/anthropic/middleware.py +0 -75
  14. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/common/__init__.py +0 -14
  15. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/common/trace_fields.py +3 -11
  16. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/common/utils.py +0 -66
  17. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/config.py +0 -1
  18. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/config.py +0 -16
  19. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/middleware.py +0 -20
  20. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/__init__.py +2 -2
  21. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/config.py +0 -3
  22. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/exceptions.py +5 -2
  23. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/middleware.py +6 -32
  24. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6/revenium_python_sdk.egg-info}/PKG-INFO +46 -72
  25. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_python_sdk.egg-info/SOURCES.txt +1 -4
  26. revenium_python_sdk-0.1.4/revenium_middleware/anthropic/summary_printer.py +0 -286
  27. revenium_python_sdk-0.1.4/revenium_middleware/google/common/summary_printer.py +0 -271
  28. revenium_python_sdk-0.1.4/revenium_middleware/litellm/client/summary_printer.py +0 -314
  29. revenium_python_sdk-0.1.4/revenium_middleware/openai/summary_printer.py +0 -292
  30. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/LICENSE +0 -0
  31. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/context.py +0 -0
  32. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/decorators.py +0 -0
  33. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/metering.py +0 -0
  34. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/patch_registry.py +0 -0
  35. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/prompt_extraction.py +0 -0
  36. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/subscriber.py +0 -0
  37. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/_core/trace_fields.py +0 -0
  38. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/anthropic/__init__.py +0 -0
  39. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/anthropic/prompt_extractor.py +0 -0
  40. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/anthropic/provider.py +0 -0
  41. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/anthropic/trace_fields.py +0 -0
  42. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/fal/__init__.py +0 -0
  43. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/fal/_metering.py +0 -0
  44. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/fal/config.py +0 -0
  45. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/fal/middleware.py +0 -0
  46. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/fal/trace_fields.py +0 -0
  47. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/__init__.py +0 -0
  48. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/common/exceptions.py +0 -0
  49. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/common/protocols.py +0 -0
  50. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/common/types.py +0 -0
  51. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/google_ai/__init__.py +0 -0
  52. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/google_ai/middleware.py +0 -0
  53. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/google_ai/provider.py +0 -0
  54. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/prompt_extractor.py +0 -0
  55. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/vertex_ai/__init__.py +0 -0
  56. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/vertex_ai/middleware.py +0 -0
  57. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/google/vertex_ai/provider.py +0 -0
  58. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/__init__.py +0 -0
  59. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/__init__.py +0 -0
  60. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/context.py +0 -0
  61. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/decorators.py +0 -0
  62. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/hooks.py +0 -0
  63. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/integrations/__init__.py +0 -0
  64. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/integrations/crewai.py +0 -0
  65. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/trace_fields.py +0 -0
  66. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/client/validation.py +0 -0
  67. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/proxy/__init__.py +0 -0
  68. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/litellm/proxy/middleware.py +0 -0
  69. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/ollama/__init__.py +0 -0
  70. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/ollama/middleware.py +0 -0
  71. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/ollama/trace_fields.py +0 -0
  72. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/azure_config.py +0 -0
  73. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/azure_model_resolver.py +0 -0
  74. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/langchain/__init__.py +0 -0
  75. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/langchain/_utils.py +0 -0
  76. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/langchain/unified_handler.py +0 -0
  77. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/prompt_extractor.py +0 -0
  78. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/provider.py +0 -0
  79. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/openai/trace_fields.py +0 -0
  80. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/perplexity/__init__.py +0 -0
  81. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/perplexity/middleware.py +0 -0
  82. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/perplexity/perplexity_sdk.py +0 -0
  83. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/perplexity/provider.py +0 -0
  84. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_middleware/perplexity/trace_fields.py +0 -0
  85. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_python_sdk.egg-info/dependency_links.txt +0 -0
  86. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_python_sdk.egg-info/requires.txt +0 -0
  87. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/revenium_python_sdk.egg-info/top_level.txt +0 -0
  88. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/setup.cfg +0 -0
  89. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.6}/tests/test_metering.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: revenium-python-sdk
3
- Version: 0.1.4
3
+ Version: 0.1.6
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
@@ -203,6 +203,30 @@ print(response.choices[0].message.content)
203
203
 
204
204
  ---
205
205
 
206
+ ## Agentic Outcomes (Outcome-Based Metering)
207
+
208
+ Emit per-agent terminal outcomes (`CONVERTED`, `DEFLECTED`, `ESCALATED`) alongside completion and tool-event records, so dashboards show business value next to AI cost.
209
+
210
+ ```python
211
+ from revenium_middleware.agentic_outcomes import AgenticOutcomeClient, AgenticOutcomeSettings
212
+
213
+ settings = AgenticOutcomeSettings(api_key="rev_sk_...")
214
+ client = AgenticOutcomeClient(settings)
215
+
216
+ client.emit_completion(...) # one per LLM call
217
+ client.emit_tool_event(...) # one per tool / step
218
+ client.report_outcome(job_id, {...}) # close the job with a terminal outcome
219
+ client.close()
220
+ ```
221
+
222
+ The job is created implicitly by the first metric ingested for `agenticJobId`. Call `client.create_job(job_id)` explicitly if you need to record an agent run before emitting any metrics.
223
+
224
+ See [`examples/agentic_outcomes/`](examples/agentic_outcomes/) for runnable demos (sales / coding / support) with configurable failure rates and outcome distributions.
225
+
226
+ **API reference:** [docs.revenium.io](https://docs.revenium.io) · per-endpoint reference at [revenium.readme.io/reference/meter_ai_completion](https://revenium.readme.io/reference/meter_ai_completion).
227
+
228
+ ---
229
+
206
230
  ## Provider Usage Guides
207
231
 
208
232
  ### OpenAI
@@ -292,7 +316,7 @@ client = anthropic.Anthropic()
292
316
 
293
317
  # Basic message
294
318
  message = client.messages.create(
295
- model="claude-3-haiku-20240307",
319
+ model="claude-opus-4-7",
296
320
  max_tokens=100,
297
321
  messages=[{"role": "user", "content": "Hello!"}],
298
322
  usage_metadata={
@@ -304,7 +328,7 @@ message = client.messages.create(
304
328
 
305
329
  # Streaming
306
330
  with client.messages.stream(
307
- model="claude-3-haiku-20240307",
331
+ model="claude-opus-4-7",
308
332
  max_tokens=200,
309
333
  messages=[{"role": "user", "content": "Tell me a story"}],
310
334
  usage_metadata={"task_type": "creative"}
@@ -330,7 +354,7 @@ client = anthropic.AnthropicBedrock(
330
354
  )
331
355
 
332
356
  message = client.messages.create(
333
- model="anthropic.claude-3-haiku-20240307-v1:0",
357
+ model="claude-opus-4-7",
334
358
  max_tokens=100,
335
359
  messages=[{"role": "user", "content": "Hello from Bedrock!"}]
336
360
  )
@@ -354,6 +378,11 @@ message = client.messages.create(
354
378
 
355
379
  | Anthropic Model | Bedrock Model ID |
356
380
  |----------------|------------------|
381
+ | `claude-opus-4-7` | `anthropic.claude-opus-4-7` |
382
+ | `us.claude-opus-4-7` | `us.anthropic.claude-opus-4-7` |
383
+ | `eu.claude-opus-4-7` | `eu.anthropic.claude-opus-4-7` |
384
+ | `au.claude-opus-4-7` | `au.anthropic.claude-opus-4-7` |
385
+ | `global.claude-opus-4-7` | `global.anthropic.claude-opus-4-7` |
357
386
  | `claude-3-opus-20240229` | `anthropic.claude-3-opus-20240229-v1:0` |
358
387
  | `claude-3-sonnet-20240229` | `anthropic.claude-3-sonnet-20240229-v1:0` |
359
388
  | `claude-3-haiku-20240307` | `us.anthropic.claude-3-5-haiku-20241022-v1:0` |
@@ -676,7 +705,7 @@ handler = ReveniumCallbackHandler(
676
705
  agent_name="support_agent"
677
706
  )
678
707
 
679
- llm = ChatOpenAI(model="gpt-4", callbacks=[handler])
708
+ llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler])
680
709
  response = llm.invoke("Hello!")
681
710
  ```
682
711
 
@@ -715,7 +744,7 @@ result = agent.invoke(
715
744
  from revenium_middleware_langchain import AsyncReveniumCallbackHandler
716
745
 
717
746
  handler = AsyncReveniumCallbackHandler(trace_id="async-session")
718
- llm = ChatOpenAI(model="gpt-4", callbacks=[handler])
747
+ llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler])
719
748
  response = await llm.ainvoke("Hello!")
720
749
  ```
721
750
 
@@ -783,7 +812,7 @@ response = client.chat.completions.create(
783
812
  )
784
813
  ```
785
814
 
786
- **Deprecation notice:** The old field names `organizationId`, `organization_id`, `productId`, and `product_id` are still supported for backward compatibility but are deprecated. Use `organizationName` and `productName` for new implementations.
815
+ **Deprecation notice:** The legacy field aliases `organizationId`, `organization_id`, `productId`, and `product_id` are accepted by this SDK only as an input-layer convenience and emit a `DeprecationWarning`. The Revenium backend no longer accepts them — they are translated to `organizationName` / `productName` before the wire call. Migrate to `organization_name` / `organizationName` and `product_name` / `productName` now; the input-layer aliases will be removed in the next major release.
787
816
 
788
817
  **API Reference:** [Complete metadata field documentation](https://revenium.readme.io/reference/meter_ai_completion)
789
818
 
@@ -964,7 +993,7 @@ from revenium_middleware import revenium_meter, revenium_metadata
964
993
  def premium_feature(prompt: str) -> str:
965
994
  # This WILL be metered (decorated with @revenium_meter)
966
995
  response = client.chat.completions.create(
967
- model="gpt-4o",
996
+ model="gpt-4o-mini",
968
997
  messages=[{"role": "user", "content": prompt}]
969
998
  )
970
999
  return response.choices[0].message.content
@@ -1080,65 +1109,11 @@ Prompt capture works with both streaming and non-streaming requests, and with mu
1080
1109
 
1081
1110
  ---
1082
1111
 
1083
- ## Terminal Summary Output
1084
-
1085
- Display a cost and usage summary in your terminal after each API request. Useful for development, debugging, and monitoring AI costs in real-time.
1086
-
1087
- ### Configuration
1088
-
1089
- | Environment Variable | Values | Description |
1090
- |---------------------|--------|-------------|
1091
- | `REVENIUM_PRINT_SUMMARY` | `false` (default), `true` or `human`, `json` | Controls output format |
1092
- | `REVENIUM_TEAM_ID` | Your team ID | Required to fetch and display cost information |
1093
-
1094
- ```bash
1095
- # Enable human-readable output
1096
- export REVENIUM_PRINT_SUMMARY=human
1097
-
1098
- # Required for cost display (find in Revenium web app)
1099
- export REVENIUM_TEAM_ID=your-team-id-here
1100
- ```
1101
-
1102
- ### Human-Readable Format
1103
-
1104
- ```
1105
- ============================================================
1106
- REVENIUM USAGE SUMMARY
1107
- ============================================================
1108
- Model: gpt-4o-mini
1109
- Provider: OPENAI
1110
- Duration: 1.23s
1111
-
1112
- Token Usage:
1113
- Input Tokens: 150
1114
- Output Tokens: 250
1115
- Total Tokens: 400
1116
-
1117
- Cost: $0.000045
1118
-
1119
- Trace ID: abc-123
1120
- ============================================================
1121
- ```
1122
-
1123
- ### JSON Format
1124
-
1125
- ```json
1126
- {"model":"gpt-4o-mini","provider":"OPENAI","durationSeconds":1.23,"inputTokenCount":150,"outputTokenCount":250,"totalTokenCount":400,"cost":0.000045,"costStatus":"available","traceId":"abc-123"}
1127
- ```
1128
-
1129
- ### Cost Status
1130
-
1131
- | Scenario | Display |
1132
- |----------|---------|
1133
- | Cost available | `$0.000045` |
1134
- | `REVENIUM_TEAM_ID` set, cost pending | `Pending (aggregating... check Revenium dashboard)` |
1135
- | `REVENIUM_TEAM_ID` not set | `Add REVENIUM_TEAM_ID to see pricing` |
1136
-
1137
- ---
1138
-
1139
1112
  ## Cost Controls / Enforcement
1140
1113
 
1141
- Block outbound provider requests client-side when a Revenium cost-limit rule trips. When the circuit breaker is enabled, the middleware polls enforcement rules from the Revenium API in a background daemon thread and raises `ReveniumCostLimitExceeded` **before** the upstream call, preventing spend beyond the configured limit.
1114
+ Block outbound provider requests client-side when a Revenium cost control trips. When the circuit breaker is enabled, the middleware polls compiled enforcement rules from the Revenium API in a background daemon thread and raises `BudgetExceededError` **before** the upstream call, preventing spend beyond the configured limit.
1115
+
1116
+ > **Terminology note:** The customer-facing entity is called a **cost control**, served by the backend at `/v2/api/ai/cost-controls`. This SDK polls a separate compiled-rules feed at `/v2/api/ai/enforcement-rules/{teamId}` and is unaffected by changes to the CRUD path — no SDK upgrade is required.
1142
1117
 
1143
1118
  Currently wired for the OpenAI provider (other providers land via per-provider follow-on tickets).
1144
1119
 
@@ -1164,7 +1139,7 @@ REVENIUM_ENFORCEMENT_BASE_URL=https://api.revenium.ai/profitstream # optional
1164
1139
  | `REVENIUM_TEAM_ID` | — | Hashed team ID. Path component on rule fetches; required when the breaker is enabled. |
1165
1140
  | `REVENIUM_ENFORCEMENT_BASE_URL` | origin of `REVENIUM_METERING_BASE_URL` | Base URL for the enforcement API. Set when the enforcement API lives behind a context-path. |
1166
1141
  | `REVENIUM_CB_POLL_INTERVAL_SECONDS` | `60` | Background poll interval for rule refreshes. |
1167
- | `REVENIUM_CB_FAIL_MODE` | `open` | `open` (default) lets calls through when no cache exists; `closed` raises `ReveniumCostLimitExceeded` until rules are loaded. |
1142
+ | `REVENIUM_CB_FAIL_MODE` | `open` | `open` (default) lets calls through when no cache exists; `closed` raises `BudgetExceededError` until rules are loaded. |
1168
1143
  | `REVENIUM_CACHE_DIR` | — | When set, the rule cache is mirrored to `<dir>/revenium_enforcement_rules.json` so a restarted process doesn't fail-closed on the very first call. |
1169
1144
 
1170
1145
  ### Public API
@@ -1188,7 +1163,7 @@ The pre-call check fires before every chat / embeddings / responses call. When t
1188
1163
  ### Exception Contract
1189
1164
 
1190
1165
  ```python
1191
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1166
+ from revenium_middleware.openai import BudgetExceededError
1192
1167
  ```
1193
1168
 
1194
1169
  When a tripped rule matches the current request, the middleware raises before the OpenAI call is made. All structured fields are populated when the server provides them:
@@ -1202,10 +1177,10 @@ When a tripped rule matches the current request, the middleware raises before th
1202
1177
  | `resets_at` | `str \| None` | ISO-8601 timestamp the rule next resets |
1203
1178
  | `rule_id` | `str \| int \| None` | Server-side rule identifier |
1204
1179
 
1205
- `ReveniumCostLimitExceeded` does **not** inherit from `ReveniumMiddlewareError`, so the OpenAI middleware's `handle_exception_safely` decorator never swallows it — it always reaches your `except` block.
1180
+ `BudgetExceededError` does **not** inherit from `ReveniumMiddlewareError`, so the OpenAI middleware's `handle_exception_safely` decorator never swallows it — it always reaches your `except` block.
1206
1181
 
1207
1182
  ```python
1208
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1183
+ from revenium_middleware.openai import BudgetExceededError
1209
1184
  import openai
1210
1185
 
1211
1186
  client = openai.OpenAI()
@@ -1215,7 +1190,7 @@ try:
1215
1190
  model="gpt-4o-mini",
1216
1191
  messages=[{"role": "user", "content": "Summarize the meeting notes"}],
1217
1192
  )
1218
- except ReveniumCostLimitExceeded as exc:
1193
+ except BudgetExceededError as exc:
1219
1194
  print(f"Cost limit reached: {exc.message}")
1220
1195
  print(f"Rule {exc.rule_name}: {exc.current_value} / {exc.threshold}; resets {exc.resets_at}")
1221
1196
  ```
@@ -1251,9 +1226,8 @@ See [`examples/openai/openai_blocking_demo.py`](examples/openai/openai_blocking_
1251
1226
  | `REVENIUM_METERING_BASE_URL` | `https://api.revenium.ai` | Revenium API endpoint |
1252
1227
  | `REVENIUM_LOG_LEVEL` | `INFO` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
1253
1228
  | `REVENIUM_CAPTURE_PROMPTS` | `false` | Enable prompt capture |
1254
- | `REVENIUM_PRINT_SUMMARY` | `false` | Terminal output: `false`, `true`/`human`, `json` |
1255
1229
  | `REVENIUM_SELECTIVE_METERING` | `false` | Only meter `@revenium_meter` decorated functions |
1256
- | `REVENIUM_TEAM_ID` | - | Team ID for cost display in terminal summary |
1230
+ | `REVENIUM_TEAM_ID` | - | Team ID for cost lookups |
1257
1231
  | `REVENIUM_ENVIRONMENT` | - | Deployment environment (auto-detects from `ENVIRONMENT`, `DEPLOYMENT_ENV`) |
1258
1232
  | `REVENIUM_REGION` | - | Cloud region (auto-detects from `AWS_REGION`, `AZURE_REGION`, `GCP_REGION`) |
1259
1233
  | `REVENIUM_CREDENTIAL_ALIAS` | - | Human-readable API key name |
@@ -115,6 +115,30 @@ print(response.choices[0].message.content)
115
115
 
116
116
  ---
117
117
 
118
+ ## Agentic Outcomes (Outcome-Based Metering)
119
+
120
+ Emit per-agent terminal outcomes (`CONVERTED`, `DEFLECTED`, `ESCALATED`) alongside completion and tool-event records, so dashboards show business value next to AI cost.
121
+
122
+ ```python
123
+ from revenium_middleware.agentic_outcomes import AgenticOutcomeClient, AgenticOutcomeSettings
124
+
125
+ settings = AgenticOutcomeSettings(api_key="rev_sk_...")
126
+ client = AgenticOutcomeClient(settings)
127
+
128
+ client.emit_completion(...) # one per LLM call
129
+ client.emit_tool_event(...) # one per tool / step
130
+ client.report_outcome(job_id, {...}) # close the job with a terminal outcome
131
+ client.close()
132
+ ```
133
+
134
+ The job is created implicitly by the first metric ingested for `agenticJobId`. Call `client.create_job(job_id)` explicitly if you need to record an agent run before emitting any metrics.
135
+
136
+ See [`examples/agentic_outcomes/`](examples/agentic_outcomes/) for runnable demos (sales / coding / support) with configurable failure rates and outcome distributions.
137
+
138
+ **API reference:** [docs.revenium.io](https://docs.revenium.io) · per-endpoint reference at [revenium.readme.io/reference/meter_ai_completion](https://revenium.readme.io/reference/meter_ai_completion).
139
+
140
+ ---
141
+
118
142
  ## Provider Usage Guides
119
143
 
120
144
  ### OpenAI
@@ -204,7 +228,7 @@ client = anthropic.Anthropic()
204
228
 
205
229
  # Basic message
206
230
  message = client.messages.create(
207
- model="claude-3-haiku-20240307",
231
+ model="claude-opus-4-7",
208
232
  max_tokens=100,
209
233
  messages=[{"role": "user", "content": "Hello!"}],
210
234
  usage_metadata={
@@ -216,7 +240,7 @@ message = client.messages.create(
216
240
 
217
241
  # Streaming
218
242
  with client.messages.stream(
219
- model="claude-3-haiku-20240307",
243
+ model="claude-opus-4-7",
220
244
  max_tokens=200,
221
245
  messages=[{"role": "user", "content": "Tell me a story"}],
222
246
  usage_metadata={"task_type": "creative"}
@@ -242,7 +266,7 @@ client = anthropic.AnthropicBedrock(
242
266
  )
243
267
 
244
268
  message = client.messages.create(
245
- model="anthropic.claude-3-haiku-20240307-v1:0",
269
+ model="claude-opus-4-7",
246
270
  max_tokens=100,
247
271
  messages=[{"role": "user", "content": "Hello from Bedrock!"}]
248
272
  )
@@ -266,6 +290,11 @@ message = client.messages.create(
266
290
 
267
291
  | Anthropic Model | Bedrock Model ID |
268
292
  |----------------|------------------|
293
+ | `claude-opus-4-7` | `anthropic.claude-opus-4-7` |
294
+ | `us.claude-opus-4-7` | `us.anthropic.claude-opus-4-7` |
295
+ | `eu.claude-opus-4-7` | `eu.anthropic.claude-opus-4-7` |
296
+ | `au.claude-opus-4-7` | `au.anthropic.claude-opus-4-7` |
297
+ | `global.claude-opus-4-7` | `global.anthropic.claude-opus-4-7` |
269
298
  | `claude-3-opus-20240229` | `anthropic.claude-3-opus-20240229-v1:0` |
270
299
  | `claude-3-sonnet-20240229` | `anthropic.claude-3-sonnet-20240229-v1:0` |
271
300
  | `claude-3-haiku-20240307` | `us.anthropic.claude-3-5-haiku-20241022-v1:0` |
@@ -588,7 +617,7 @@ handler = ReveniumCallbackHandler(
588
617
  agent_name="support_agent"
589
618
  )
590
619
 
591
- llm = ChatOpenAI(model="gpt-4", callbacks=[handler])
620
+ llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler])
592
621
  response = llm.invoke("Hello!")
593
622
  ```
594
623
 
@@ -627,7 +656,7 @@ result = agent.invoke(
627
656
  from revenium_middleware_langchain import AsyncReveniumCallbackHandler
628
657
 
629
658
  handler = AsyncReveniumCallbackHandler(trace_id="async-session")
630
- llm = ChatOpenAI(model="gpt-4", callbacks=[handler])
659
+ llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler])
631
660
  response = await llm.ainvoke("Hello!")
632
661
  ```
633
662
 
@@ -695,7 +724,7 @@ response = client.chat.completions.create(
695
724
  )
696
725
  ```
697
726
 
698
- **Deprecation notice:** The old field names `organizationId`, `organization_id`, `productId`, and `product_id` are still supported for backward compatibility but are deprecated. Use `organizationName` and `productName` for new implementations.
727
+ **Deprecation notice:** The legacy field aliases `organizationId`, `organization_id`, `productId`, and `product_id` are accepted by this SDK only as an input-layer convenience and emit a `DeprecationWarning`. The Revenium backend no longer accepts them — they are translated to `organizationName` / `productName` before the wire call. Migrate to `organization_name` / `organizationName` and `product_name` / `productName` now; the input-layer aliases will be removed in the next major release.
699
728
 
700
729
  **API Reference:** [Complete metadata field documentation](https://revenium.readme.io/reference/meter_ai_completion)
701
730
 
@@ -876,7 +905,7 @@ from revenium_middleware import revenium_meter, revenium_metadata
876
905
  def premium_feature(prompt: str) -> str:
877
906
  # This WILL be metered (decorated with @revenium_meter)
878
907
  response = client.chat.completions.create(
879
- model="gpt-4o",
908
+ model="gpt-4o-mini",
880
909
  messages=[{"role": "user", "content": prompt}]
881
910
  )
882
911
  return response.choices[0].message.content
@@ -992,65 +1021,11 @@ Prompt capture works with both streaming and non-streaming requests, and with mu
992
1021
 
993
1022
  ---
994
1023
 
995
- ## Terminal Summary Output
996
-
997
- Display a cost and usage summary in your terminal after each API request. Useful for development, debugging, and monitoring AI costs in real-time.
998
-
999
- ### Configuration
1000
-
1001
- | Environment Variable | Values | Description |
1002
- |---------------------|--------|-------------|
1003
- | `REVENIUM_PRINT_SUMMARY` | `false` (default), `true` or `human`, `json` | Controls output format |
1004
- | `REVENIUM_TEAM_ID` | Your team ID | Required to fetch and display cost information |
1005
-
1006
- ```bash
1007
- # Enable human-readable output
1008
- export REVENIUM_PRINT_SUMMARY=human
1009
-
1010
- # Required for cost display (find in Revenium web app)
1011
- export REVENIUM_TEAM_ID=your-team-id-here
1012
- ```
1013
-
1014
- ### Human-Readable Format
1015
-
1016
- ```
1017
- ============================================================
1018
- REVENIUM USAGE SUMMARY
1019
- ============================================================
1020
- Model: gpt-4o-mini
1021
- Provider: OPENAI
1022
- Duration: 1.23s
1023
-
1024
- Token Usage:
1025
- Input Tokens: 150
1026
- Output Tokens: 250
1027
- Total Tokens: 400
1028
-
1029
- Cost: $0.000045
1030
-
1031
- Trace ID: abc-123
1032
- ============================================================
1033
- ```
1034
-
1035
- ### JSON Format
1036
-
1037
- ```json
1038
- {"model":"gpt-4o-mini","provider":"OPENAI","durationSeconds":1.23,"inputTokenCount":150,"outputTokenCount":250,"totalTokenCount":400,"cost":0.000045,"costStatus":"available","traceId":"abc-123"}
1039
- ```
1040
-
1041
- ### Cost Status
1042
-
1043
- | Scenario | Display |
1044
- |----------|---------|
1045
- | Cost available | `$0.000045` |
1046
- | `REVENIUM_TEAM_ID` set, cost pending | `Pending (aggregating... check Revenium dashboard)` |
1047
- | `REVENIUM_TEAM_ID` not set | `Add REVENIUM_TEAM_ID to see pricing` |
1048
-
1049
- ---
1050
-
1051
1024
  ## Cost Controls / Enforcement
1052
1025
 
1053
- Block outbound provider requests client-side when a Revenium cost-limit rule trips. When the circuit breaker is enabled, the middleware polls enforcement rules from the Revenium API in a background daemon thread and raises `ReveniumCostLimitExceeded` **before** the upstream call, preventing spend beyond the configured limit.
1026
+ Block outbound provider requests client-side when a Revenium cost control trips. When the circuit breaker is enabled, the middleware polls compiled enforcement rules from the Revenium API in a background daemon thread and raises `BudgetExceededError` **before** the upstream call, preventing spend beyond the configured limit.
1027
+
1028
+ > **Terminology note:** The customer-facing entity is called a **cost control**, served by the backend at `/v2/api/ai/cost-controls`. This SDK polls a separate compiled-rules feed at `/v2/api/ai/enforcement-rules/{teamId}` and is unaffected by changes to the CRUD path — no SDK upgrade is required.
1054
1029
 
1055
1030
  Currently wired for the OpenAI provider (other providers land via per-provider follow-on tickets).
1056
1031
 
@@ -1076,7 +1051,7 @@ REVENIUM_ENFORCEMENT_BASE_URL=https://api.revenium.ai/profitstream # optional
1076
1051
  | `REVENIUM_TEAM_ID` | — | Hashed team ID. Path component on rule fetches; required when the breaker is enabled. |
1077
1052
  | `REVENIUM_ENFORCEMENT_BASE_URL` | origin of `REVENIUM_METERING_BASE_URL` | Base URL for the enforcement API. Set when the enforcement API lives behind a context-path. |
1078
1053
  | `REVENIUM_CB_POLL_INTERVAL_SECONDS` | `60` | Background poll interval for rule refreshes. |
1079
- | `REVENIUM_CB_FAIL_MODE` | `open` | `open` (default) lets calls through when no cache exists; `closed` raises `ReveniumCostLimitExceeded` until rules are loaded. |
1054
+ | `REVENIUM_CB_FAIL_MODE` | `open` | `open` (default) lets calls through when no cache exists; `closed` raises `BudgetExceededError` until rules are loaded. |
1080
1055
  | `REVENIUM_CACHE_DIR` | — | When set, the rule cache is mirrored to `<dir>/revenium_enforcement_rules.json` so a restarted process doesn't fail-closed on the very first call. |
1081
1056
 
1082
1057
  ### Public API
@@ -1100,7 +1075,7 @@ The pre-call check fires before every chat / embeddings / responses call. When t
1100
1075
  ### Exception Contract
1101
1076
 
1102
1077
  ```python
1103
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1078
+ from revenium_middleware.openai import BudgetExceededError
1104
1079
  ```
1105
1080
 
1106
1081
  When a tripped rule matches the current request, the middleware raises before the OpenAI call is made. All structured fields are populated when the server provides them:
@@ -1114,10 +1089,10 @@ When a tripped rule matches the current request, the middleware raises before th
1114
1089
  | `resets_at` | `str \| None` | ISO-8601 timestamp the rule next resets |
1115
1090
  | `rule_id` | `str \| int \| None` | Server-side rule identifier |
1116
1091
 
1117
- `ReveniumCostLimitExceeded` does **not** inherit from `ReveniumMiddlewareError`, so the OpenAI middleware's `handle_exception_safely` decorator never swallows it — it always reaches your `except` block.
1092
+ `BudgetExceededError` does **not** inherit from `ReveniumMiddlewareError`, so the OpenAI middleware's `handle_exception_safely` decorator never swallows it — it always reaches your `except` block.
1118
1093
 
1119
1094
  ```python
1120
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1095
+ from revenium_middleware.openai import BudgetExceededError
1121
1096
  import openai
1122
1097
 
1123
1098
  client = openai.OpenAI()
@@ -1127,7 +1102,7 @@ try:
1127
1102
  model="gpt-4o-mini",
1128
1103
  messages=[{"role": "user", "content": "Summarize the meeting notes"}],
1129
1104
  )
1130
- except ReveniumCostLimitExceeded as exc:
1105
+ except BudgetExceededError as exc:
1131
1106
  print(f"Cost limit reached: {exc.message}")
1132
1107
  print(f"Rule {exc.rule_name}: {exc.current_value} / {exc.threshold}; resets {exc.resets_at}")
1133
1108
  ```
@@ -1163,9 +1138,8 @@ See [`examples/openai/openai_blocking_demo.py`](examples/openai/openai_blocking_
1163
1138
  | `REVENIUM_METERING_BASE_URL` | `https://api.revenium.ai` | Revenium API endpoint |
1164
1139
  | `REVENIUM_LOG_LEVEL` | `INFO` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
1165
1140
  | `REVENIUM_CAPTURE_PROMPTS` | `false` | Enable prompt capture |
1166
- | `REVENIUM_PRINT_SUMMARY` | `false` | Terminal output: `false`, `true`/`human`, `json` |
1167
1141
  | `REVENIUM_SELECTIVE_METERING` | `false` | Only meter `@revenium_meter` decorated functions |
1168
- | `REVENIUM_TEAM_ID` | - | Team ID for cost display in terminal summary |
1142
+ | `REVENIUM_TEAM_ID` | - | Team ID for cost lookups |
1169
1143
  | `REVENIUM_ENVIRONMENT` | - | Deployment environment (auto-detects from `ENVIRONMENT`, `DEPLOYMENT_ENV`) |
1170
1144
  | `REVENIUM_REGION` | - | Cloud region (auto-detects from `AWS_REGION`, `AZURE_REGION`, `GCP_REGION`) |
1171
1145
  | `REVENIUM_CREDENTIAL_ALIAS` | - | Human-readable API key name |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "revenium-python-sdk"
7
- version = "0.1.4"
7
+ version = "0.1.6"
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"
@@ -157,6 +157,9 @@ from ._core import ( # noqa: E402
157
157
  # Re-export tool metering utilities from revenium_metering (v6.8.2+)
158
158
  from revenium_metering import meter_tool, report_tool_call, configure # noqa: E402
159
159
 
160
+ # Agentic-outcome client (used by examples/agentic_outcomes/ pack)
161
+ from .agentic_outcomes import AgenticOutcomeClient, AgenticOutcomeSettings # noqa: E402
162
+
160
163
  __all__ = [
161
164
  # Metering exports
162
165
  "client",
@@ -181,4 +184,7 @@ __all__ = [
181
184
  "meter_tool",
182
185
  "report_tool_call",
183
186
  "configure",
187
+ # Agentic-outcome exports
188
+ "AgenticOutcomeClient",
189
+ "AgenticOutcomeSettings",
184
190
  ]
@@ -6,7 +6,7 @@ provider-specific middleware implementations.
6
6
  """
7
7
 
8
8
  from .metering import run_async_in_thread, shutdown_event, client
9
- from .exceptions import ReveniumCostLimitExceeded
9
+ from .exceptions import BudgetExceededError
10
10
  from .enforcement import check_enforcement, is_circuit_breaker_enabled, stop_polling
11
11
  from .context import (
12
12
  is_inside_decorated_function,
@@ -49,7 +49,7 @@ __all__ = [
49
49
  "run_async_in_thread",
50
50
  "shutdown_event",
51
51
  # Enforcement / circuit breaker
52
- "ReveniumCostLimitExceeded",
52
+ "BudgetExceededError",
53
53
  "check_enforcement",
54
54
  "is_circuit_breaker_enabled",
55
55
  "stop_polling",
@@ -8,13 +8,10 @@ this via class inheritance and re-export symbols for backward compatibility.
8
8
 
9
9
  import logging
10
10
  import os
11
- from typing import Set, Literal, Union, Optional
11
+ from typing import Set, Optional
12
12
 
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
- # Type alias for summary format
16
- SummaryFormat = Literal["human", "json"]
17
-
18
15
 
19
16
  class Config:
20
17
  """Shared configuration constants for all Revenium middleware providers."""
@@ -65,13 +62,9 @@ class Config:
65
62
  CAPTURE_PROMPTS: bool = False
66
63
  MAX_PROMPT_LENGTH: int = 50_000 # Maximum characters per prompt field
67
64
 
68
- # Terminal summary settings
69
- ENV_REVENIUM_PRINT_SUMMARY: str = "REVENIUM_PRINT_SUMMARY"
65
+ # Revenium API targeting
70
66
  ENV_REVENIUM_TEAM_ID: str = "REVENIUM_TEAM_ID"
71
67
  ENV_REVENIUM_BASE_URL: str = "REVENIUM_METERING_BASE_URL"
72
- SUMMARY_RETRY_ATTEMPTS: int = 3
73
- SUMMARY_RETRY_DELAY: float = 2.0
74
- SUMMARY_API_TIMEOUT: float = 5.0
75
68
  DEFAULT_BASE_URL: str = "https://api.revenium.ai"
76
69
 
77
70
  # Enforcement / circuit breaker — see _core/enforcement.py.
@@ -139,38 +132,6 @@ def get_timeout_config() -> dict:
139
132
  }
140
133
 
141
134
 
142
- def parse_print_summary_value(value: Optional[str]) -> Union[bool, SummaryFormat]:
143
- """
144
- Parse REVENIUM_PRINT_SUMMARY environment variable value.
145
-
146
- Returns:
147
- False if disabled, 'human' or 'json' if enabled
148
- """
149
- if value is None:
150
- return False
151
-
152
- value_lower = value.lower().strip()
153
-
154
- if value_lower in ('false', '0', 'no', 'off', 'disabled', ''):
155
- return False
156
- elif value_lower in ('true', '1', 'yes', 'on', 'enabled', 'human'):
157
- return 'human'
158
- elif value_lower == 'json':
159
- return 'json'
160
- else:
161
- logger.warning(
162
- f"Invalid REVENIUM_PRINT_SUMMARY value '{value}'. "
163
- f"Expected 'true', 'human', 'json', or 'false'. Defaulting to disabled."
164
- )
165
- return False
166
-
167
-
168
- def get_print_summary_config() -> Union[bool, SummaryFormat]:
169
- """Get print summary configuration from environment."""
170
- value = get_config_value(Config.ENV_REVENIUM_PRINT_SUMMARY)
171
- return parse_print_summary_value(value)
172
-
173
-
174
135
  def get_team_id() -> Optional[str]:
175
136
  """Get Revenium team ID from environment."""
176
137
  return get_config_value(Config.ENV_REVENIUM_TEAM_ID)
@@ -3,7 +3,7 @@ Enforcement engine for the Revenium circuit breaker.
3
3
 
4
4
  Polls cost-limit rules from the Revenium API in a daemon thread and caches
5
5
  them in memory. ``check_enforcement(...)`` is a pre-call hook that raises
6
- ``ReveniumCostLimitExceeded`` when a tripped rule matches the current
6
+ ``BudgetExceededError`` when a tripped rule matches the current
7
7
  request, blocking the outbound provider call before any spend occurs.
8
8
 
9
9
  Opt-in via ``REVENIUM_CIRCUIT_BREAKER_ENABLED``. Disabled by default so the
@@ -21,7 +21,7 @@ from urllib.parse import quote, urlparse
21
21
  import httpx
22
22
 
23
23
  from .config import Config
24
- from .exceptions import ReveniumCostLimitExceeded
24
+ from .exceptions import BudgetExceededError
25
25
 
26
26
  logger = logging.getLogger("revenium_middleware.extension")
27
27
 
@@ -276,7 +276,7 @@ def check_enforcement(usage_metadata: Optional[dict] = None) -> None:
276
276
  is disabled or no rules are tripped.
277
277
 
278
278
  Raises:
279
- ReveniumCostLimitExceeded: when a cost-limit rule blocks the call.
279
+ BudgetExceededError: when a cost-limit rule blocks the call.
280
280
  All structured fields (``rule_name``, ``current_value``,
281
281
  ``threshold``, ``resets_at``, ``rule_id``) are populated when the
282
282
  server provides them.
@@ -295,7 +295,7 @@ def check_enforcement(usage_metadata: Optional[dict] = None) -> None:
295
295
  # valid initialized state and must pass through. Use the snapshot taken
296
296
  # under _cache_lock so the decision can't see a torn write.
297
297
  if _fail_mode_is_closed() and not initialized:
298
- raise ReveniumCostLimitExceeded(
298
+ raise BudgetExceededError(
299
299
  "Request blocked: enforcement cache is uninitialized and "
300
300
  "REVENIUM_CB_FAIL_MODE=closed."
301
301
  )
@@ -319,7 +319,7 @@ def check_enforcement(usage_metadata: Optional[dict] = None) -> None:
319
319
  continue
320
320
 
321
321
  rule_name = rule.get("name", "cost limit")
322
- raise ReveniumCostLimitExceeded(
322
+ raise BudgetExceededError(
323
323
  message=f"Request blocked by Revenium enforcement rule: {rule_name}",
324
324
  rule_name=rule_name,
325
325
  current_value=_coerce_float(rule.get("currentValue")),
@@ -1,15 +1,15 @@
1
1
  """
2
2
  Core exceptions shared across all Revenium middleware providers.
3
3
 
4
- The unified SDK ships ``ReveniumCostLimitExceeded`` from ``_core`` so every
4
+ The unified SDK ships ``BudgetExceededError`` from ``_core`` so every
5
5
  provider subpackage (openai, anthropic, google, …) raises the same exception
6
- type and downstream callers can ``except ReveniumCostLimitExceeded`` once.
6
+ type and downstream callers can ``except BudgetExceededError`` once.
7
7
  """
8
8
 
9
9
  from typing import Optional, Union
10
10
 
11
11
 
12
- class ReveniumCostLimitExceeded(Exception):
12
+ class BudgetExceededError(Exception):
13
13
  """Raised when a Revenium enforcement rule blocks the outbound request.
14
14
 
15
15
  Inherits directly from ``Exception`` (not from any middleware-error base)
@@ -33,3 +33,11 @@ class ReveniumCostLimitExceeded(Exception):
33
33
  self.threshold = threshold
34
34
  self.resets_at = resets_at
35
35
  self.rule_id = rule_id
36
+
37
+
38
+ # Deprecated alias preserved for backward compatibility. The exception was
39
+ # renamed in v0.1.5 to align with the Go and Node SDKs and the backend
40
+ # `BudgetExceededException`. Existing code that does
41
+ # `except ReveniumCostLimitExceeded:` continues to catch the new exception
42
+ # unchanged. Plan to remove in a future major release.
43
+ ReveniumCostLimitExceeded = BudgetExceededError