revenium-python-sdk 0.1.4__tar.gz → 0.1.5__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.5}/PKG-INFO +60 -29
  2. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/README.md +59 -28
  3. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/pyproject.toml +1 -1
  4. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/__init__.py +6 -0
  5. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/__init__.py +2 -2
  6. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/enforcement.py +5 -5
  7. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/exceptions.py +11 -3
  8. revenium_python_sdk-0.1.5/revenium_middleware/agentic_outcomes.py +348 -0
  9. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/bedrock_adapter.py +5 -0
  10. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/__init__.py +2 -2
  11. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/exceptions.py +5 -2
  12. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/middleware.py +6 -6
  13. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5/revenium_python_sdk.egg-info}/PKG-INFO +60 -29
  14. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_python_sdk.egg-info/SOURCES.txt +1 -0
  15. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/LICENSE +0 -0
  16. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/config.py +0 -0
  17. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/context.py +0 -0
  18. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/decorators.py +0 -0
  19. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/fields.py +0 -0
  20. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/metering.py +0 -0
  21. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/patch_registry.py +0 -0
  22. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/prompt_extraction.py +0 -0
  23. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/subscriber.py +0 -0
  24. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/_core/trace_fields.py +0 -0
  25. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/__init__.py +0 -0
  26. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/config.py +0 -0
  27. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/middleware.py +0 -0
  28. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/prompt_extractor.py +0 -0
  29. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/provider.py +0 -0
  30. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/summary_printer.py +0 -0
  31. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/anthropic/trace_fields.py +0 -0
  32. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/fal/__init__.py +0 -0
  33. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/fal/_metering.py +0 -0
  34. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/fal/config.py +0 -0
  35. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/fal/middleware.py +0 -0
  36. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/fal/trace_fields.py +0 -0
  37. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/__init__.py +0 -0
  38. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/common/__init__.py +0 -0
  39. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/common/exceptions.py +0 -0
  40. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/common/protocols.py +0 -0
  41. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/common/summary_printer.py +0 -0
  42. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/common/trace_fields.py +0 -0
  43. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/common/types.py +0 -0
  44. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/common/utils.py +0 -0
  45. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/config.py +0 -0
  46. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/google_ai/__init__.py +0 -0
  47. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/google_ai/middleware.py +0 -0
  48. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/google_ai/provider.py +0 -0
  49. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/prompt_extractor.py +0 -0
  50. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/vertex_ai/__init__.py +0 -0
  51. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/vertex_ai/middleware.py +0 -0
  52. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/google/vertex_ai/provider.py +0 -0
  53. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/__init__.py +0 -0
  54. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/__init__.py +0 -0
  55. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/config.py +0 -0
  56. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/context.py +0 -0
  57. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/decorators.py +0 -0
  58. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/hooks.py +0 -0
  59. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/integrations/__init__.py +0 -0
  60. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/integrations/crewai.py +0 -0
  61. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/middleware.py +0 -0
  62. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/summary_printer.py +0 -0
  63. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/trace_fields.py +0 -0
  64. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/client/validation.py +0 -0
  65. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/proxy/__init__.py +0 -0
  66. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/litellm/proxy/middleware.py +0 -0
  67. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/ollama/__init__.py +0 -0
  68. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/ollama/middleware.py +0 -0
  69. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/ollama/trace_fields.py +0 -0
  70. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/azure_config.py +0 -0
  71. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/azure_model_resolver.py +0 -0
  72. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/config.py +0 -0
  73. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/langchain/__init__.py +0 -0
  74. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/langchain/_utils.py +0 -0
  75. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/langchain/unified_handler.py +0 -0
  76. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/prompt_extractor.py +0 -0
  77. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/provider.py +0 -0
  78. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/summary_printer.py +0 -0
  79. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/openai/trace_fields.py +0 -0
  80. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/perplexity/__init__.py +0 -0
  81. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/perplexity/middleware.py +0 -0
  82. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/perplexity/perplexity_sdk.py +0 -0
  83. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/perplexity/provider.py +0 -0
  84. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_middleware/perplexity/trace_fields.py +0 -0
  85. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_python_sdk.egg-info/dependency_links.txt +0 -0
  86. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_python_sdk.egg-info/requires.txt +0 -0
  87. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/revenium_python_sdk.egg-info/top_level.txt +0 -0
  88. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/setup.cfg +0 -0
  89. {revenium_python_sdk-0.1.4 → revenium_python_sdk-0.1.5}/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.5
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
@@ -194,7 +194,7 @@ import revenium_middleware_openai # Auto-initializes on import
194
194
 
195
195
  client = openai.OpenAI()
196
196
  response = client.chat.completions.create(
197
- model="gpt-4o-mini",
197
+ model="gpt-5.5",
198
198
  messages=[{"role": "user", "content": "Hello!"}]
199
199
  )
200
200
  print(response.choices[0].message.content)
@@ -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
@@ -220,7 +244,7 @@ client = openai.OpenAI()
220
244
 
221
245
  # Basic chat completion
222
246
  response = client.chat.completions.create(
223
- model="gpt-4o-mini",
247
+ model="gpt-5.5",
224
248
  messages=[{"role": "user", "content": "Hello!"}],
225
249
  usage_metadata={
226
250
  "organizationName": "AcmeCorp",
@@ -232,7 +256,7 @@ response = client.chat.completions.create(
232
256
 
233
257
  # Streaming
234
258
  stream = client.chat.completions.create(
235
- model="gpt-4o-mini",
259
+ model="gpt-5.5",
236
260
  messages=[{"role": "user", "content": "Tell me a story"}],
237
261
  stream=True
238
262
  )
@@ -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` |
@@ -518,7 +547,7 @@ litellm.api_base = os.getenv("LITELLM_PROXY_URL")
518
547
  litellm.api_key = os.getenv("LITELLM_API_KEY")
519
548
 
520
549
  response = litellm.completion(
521
- model="gpt-4o-mini",
550
+ model="gpt-5.5",
522
551
  messages=[{"role": "user", "content": "Hello!"}],
523
552
  usage_metadata={
524
553
  "organizationName": "AcmeCorp",
@@ -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-5.5", 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-5.5", callbacks=[handler])
719
748
  response = await llm.ainvoke("Hello!")
720
749
  ```
721
750
 
@@ -761,7 +790,7 @@ Add business context to any API call by passing a `usage_metadata` dictionary. A
761
790
 
762
791
  ```python
763
792
  response = client.chat.completions.create(
764
- model="gpt-4o-mini",
793
+ model="gpt-5.5",
765
794
  messages=[{"role": "user", "content": "Hello!"}],
766
795
  usage_metadata={
767
796
  "trace_id": "conv-28a7e9d4",
@@ -824,7 +853,7 @@ REVENIUM_TRACE_TYPE=customer-support
824
853
 
825
854
  ```python
826
855
  response = client.chat.completions.create(
827
- model="gpt-4o-mini",
856
+ model="gpt-5.5",
828
857
  messages=[{"role": "user", "content": "Hello!"}],
829
858
  usage_metadata={
830
859
  "environment": "production",
@@ -848,7 +877,7 @@ workflow_id = str(uuid.uuid4())
848
877
 
849
878
  # Step 1: Parent operation
850
879
  parent_response = client.chat.completions.create(
851
- model="gpt-4o-mini",
880
+ model="gpt-5.5",
852
881
  messages=[{"role": "user", "content": "Analyze this document"}],
853
882
  usage_metadata={
854
883
  "trace_id": "analysis-session-456",
@@ -859,7 +888,7 @@ parent_response = client.chat.completions.create(
859
888
 
860
889
  # Step 2: Child operation linked to parent
861
890
  child_response = client.chat.completions.create(
862
- model="gpt-4o-mini",
891
+ model="gpt-5.5",
863
892
  messages=[{"role": "user", "content": "Summarize findings"}],
864
893
  usage_metadata={
865
894
  "trace_id": "analysis-session-456",
@@ -890,7 +919,7 @@ from revenium_middleware import revenium_metadata
890
919
  def handle_customer_query(question: str) -> str:
891
920
  # All API calls automatically include the decorator metadata
892
921
  response = client.chat.completions.create(
893
- model="gpt-4o-mini",
922
+ model="gpt-5.5",
894
923
  messages=[{"role": "user", "content": question}]
895
924
  )
896
925
  return response.choices[0].message.content
@@ -929,13 +958,13 @@ def outer_function():
929
958
  def mixed_metadata():
930
959
  # Uses decorator metadata
931
960
  response1 = client.chat.completions.create(
932
- model="gpt-4o-mini",
961
+ model="gpt-5.5",
933
962
  messages=[{"role": "user", "content": "Hello"}]
934
963
  )
935
964
 
936
965
  # API-level metadata overrides decorator's task_type
937
966
  response2 = client.chat.completions.create(
938
- model="gpt-4o-mini",
967
+ model="gpt-5.5",
939
968
  messages=[{"role": "user", "content": "Hello"}],
940
969
  usage_metadata={
941
970
  "task_type": "special-override", # Overrides decorator
@@ -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-5.5",
968
997
  messages=[{"role": "user", "content": prompt}]
969
998
  )
970
999
  return response.choices[0].message.content
@@ -972,7 +1001,7 @@ def premium_feature(prompt: str) -> str:
972
1001
  def free_feature(prompt: str) -> str:
973
1002
  # This will NOT be metered (no @revenium_meter decorator)
974
1003
  response = client.chat.completions.create(
975
- model="gpt-4o-mini",
1004
+ model="gpt-5.5",
976
1005
  messages=[{"role": "user", "content": prompt}]
977
1006
  )
978
1007
  return response.choices[0].message.content
@@ -1058,7 +1087,7 @@ from openai import OpenAI
1058
1087
 
1059
1088
  client = OpenAI()
1060
1089
  response = client.chat.completions.create(
1061
- model="gpt-4o-mini",
1090
+ model="gpt-5.5",
1062
1091
  messages=[
1063
1092
  {"role": "system", "content": "You are a helpful assistant."},
1064
1093
  {"role": "user", "content": "What is the capital of France?"}
@@ -1105,7 +1134,7 @@ export REVENIUM_TEAM_ID=your-team-id-here
1105
1134
  ============================================================
1106
1135
  REVENIUM USAGE SUMMARY
1107
1136
  ============================================================
1108
- Model: gpt-4o-mini
1137
+ Model: gpt-5.5
1109
1138
  Provider: OPENAI
1110
1139
  Duration: 1.23s
1111
1140
 
@@ -1123,7 +1152,7 @@ Trace ID: abc-123
1123
1152
  ### JSON Format
1124
1153
 
1125
1154
  ```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"}
1155
+ {"model":"gpt-5.5","provider":"OPENAI","durationSeconds":1.23,"inputTokenCount":150,"outputTokenCount":250,"totalTokenCount":400,"cost":0.000045,"costStatus":"available","traceId":"abc-123"}
1127
1156
  ```
1128
1157
 
1129
1158
  ### Cost Status
@@ -1138,7 +1167,9 @@ Trace ID: abc-123
1138
1167
 
1139
1168
  ## Cost Controls / Enforcement
1140
1169
 
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.
1170
+ 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.
1171
+
1172
+ > **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
1173
 
1143
1174
  Currently wired for the OpenAI provider (other providers land via per-provider follow-on tickets).
1144
1175
 
@@ -1164,7 +1195,7 @@ REVENIUM_ENFORCEMENT_BASE_URL=https://api.revenium.ai/profitstream # optional
1164
1195
  | `REVENIUM_TEAM_ID` | — | Hashed team ID. Path component on rule fetches; required when the breaker is enabled. |
1165
1196
  | `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
1197
  | `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. |
1198
+ | `REVENIUM_CB_FAIL_MODE` | `open` | `open` (default) lets calls through when no cache exists; `closed` raises `BudgetExceededError` until rules are loaded. |
1168
1199
  | `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
1200
 
1170
1201
  ### Public API
@@ -1188,7 +1219,7 @@ The pre-call check fires before every chat / embeddings / responses call. When t
1188
1219
  ### Exception Contract
1189
1220
 
1190
1221
  ```python
1191
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1222
+ from revenium_middleware.openai import BudgetExceededError
1192
1223
  ```
1193
1224
 
1194
1225
  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,20 +1233,20 @@ When a tripped rule matches the current request, the middleware raises before th
1202
1233
  | `resets_at` | `str \| None` | ISO-8601 timestamp the rule next resets |
1203
1234
  | `rule_id` | `str \| int \| None` | Server-side rule identifier |
1204
1235
 
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.
1236
+ `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
1237
 
1207
1238
  ```python
1208
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1239
+ from revenium_middleware.openai import BudgetExceededError
1209
1240
  import openai
1210
1241
 
1211
1242
  client = openai.OpenAI()
1212
1243
 
1213
1244
  try:
1214
1245
  response = client.chat.completions.create(
1215
- model="gpt-4o-mini",
1246
+ model="gpt-5.5",
1216
1247
  messages=[{"role": "user", "content": "Summarize the meeting notes"}],
1217
1248
  )
1218
- except ReveniumCostLimitExceeded as exc:
1249
+ except BudgetExceededError as exc:
1219
1250
  print(f"Cost limit reached: {exc.message}")
1220
1251
  print(f"Rule {exc.rule_name}: {exc.current_value} / {exc.threshold}; resets {exc.resets_at}")
1221
1252
  ```
@@ -106,7 +106,7 @@ import revenium_middleware_openai # Auto-initializes on import
106
106
 
107
107
  client = openai.OpenAI()
108
108
  response = client.chat.completions.create(
109
- model="gpt-4o-mini",
109
+ model="gpt-5.5",
110
110
  messages=[{"role": "user", "content": "Hello!"}]
111
111
  )
112
112
  print(response.choices[0].message.content)
@@ -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
@@ -132,7 +156,7 @@ client = openai.OpenAI()
132
156
 
133
157
  # Basic chat completion
134
158
  response = client.chat.completions.create(
135
- model="gpt-4o-mini",
159
+ model="gpt-5.5",
136
160
  messages=[{"role": "user", "content": "Hello!"}],
137
161
  usage_metadata={
138
162
  "organizationName": "AcmeCorp",
@@ -144,7 +168,7 @@ response = client.chat.completions.create(
144
168
 
145
169
  # Streaming
146
170
  stream = client.chat.completions.create(
147
- model="gpt-4o-mini",
171
+ model="gpt-5.5",
148
172
  messages=[{"role": "user", "content": "Tell me a story"}],
149
173
  stream=True
150
174
  )
@@ -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` |
@@ -430,7 +459,7 @@ litellm.api_base = os.getenv("LITELLM_PROXY_URL")
430
459
  litellm.api_key = os.getenv("LITELLM_API_KEY")
431
460
 
432
461
  response = litellm.completion(
433
- model="gpt-4o-mini",
462
+ model="gpt-5.5",
434
463
  messages=[{"role": "user", "content": "Hello!"}],
435
464
  usage_metadata={
436
465
  "organizationName": "AcmeCorp",
@@ -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-5.5", 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-5.5", callbacks=[handler])
631
660
  response = await llm.ainvoke("Hello!")
632
661
  ```
633
662
 
@@ -673,7 +702,7 @@ Add business context to any API call by passing a `usage_metadata` dictionary. A
673
702
 
674
703
  ```python
675
704
  response = client.chat.completions.create(
676
- model="gpt-4o-mini",
705
+ model="gpt-5.5",
677
706
  messages=[{"role": "user", "content": "Hello!"}],
678
707
  usage_metadata={
679
708
  "trace_id": "conv-28a7e9d4",
@@ -736,7 +765,7 @@ REVENIUM_TRACE_TYPE=customer-support
736
765
 
737
766
  ```python
738
767
  response = client.chat.completions.create(
739
- model="gpt-4o-mini",
768
+ model="gpt-5.5",
740
769
  messages=[{"role": "user", "content": "Hello!"}],
741
770
  usage_metadata={
742
771
  "environment": "production",
@@ -760,7 +789,7 @@ workflow_id = str(uuid.uuid4())
760
789
 
761
790
  # Step 1: Parent operation
762
791
  parent_response = client.chat.completions.create(
763
- model="gpt-4o-mini",
792
+ model="gpt-5.5",
764
793
  messages=[{"role": "user", "content": "Analyze this document"}],
765
794
  usage_metadata={
766
795
  "trace_id": "analysis-session-456",
@@ -771,7 +800,7 @@ parent_response = client.chat.completions.create(
771
800
 
772
801
  # Step 2: Child operation linked to parent
773
802
  child_response = client.chat.completions.create(
774
- model="gpt-4o-mini",
803
+ model="gpt-5.5",
775
804
  messages=[{"role": "user", "content": "Summarize findings"}],
776
805
  usage_metadata={
777
806
  "trace_id": "analysis-session-456",
@@ -802,7 +831,7 @@ from revenium_middleware import revenium_metadata
802
831
  def handle_customer_query(question: str) -> str:
803
832
  # All API calls automatically include the decorator metadata
804
833
  response = client.chat.completions.create(
805
- model="gpt-4o-mini",
834
+ model="gpt-5.5",
806
835
  messages=[{"role": "user", "content": question}]
807
836
  )
808
837
  return response.choices[0].message.content
@@ -841,13 +870,13 @@ def outer_function():
841
870
  def mixed_metadata():
842
871
  # Uses decorator metadata
843
872
  response1 = client.chat.completions.create(
844
- model="gpt-4o-mini",
873
+ model="gpt-5.5",
845
874
  messages=[{"role": "user", "content": "Hello"}]
846
875
  )
847
876
 
848
877
  # API-level metadata overrides decorator's task_type
849
878
  response2 = client.chat.completions.create(
850
- model="gpt-4o-mini",
879
+ model="gpt-5.5",
851
880
  messages=[{"role": "user", "content": "Hello"}],
852
881
  usage_metadata={
853
882
  "task_type": "special-override", # Overrides decorator
@@ -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-5.5",
880
909
  messages=[{"role": "user", "content": prompt}]
881
910
  )
882
911
  return response.choices[0].message.content
@@ -884,7 +913,7 @@ def premium_feature(prompt: str) -> str:
884
913
  def free_feature(prompt: str) -> str:
885
914
  # This will NOT be metered (no @revenium_meter decorator)
886
915
  response = client.chat.completions.create(
887
- model="gpt-4o-mini",
916
+ model="gpt-5.5",
888
917
  messages=[{"role": "user", "content": prompt}]
889
918
  )
890
919
  return response.choices[0].message.content
@@ -970,7 +999,7 @@ from openai import OpenAI
970
999
 
971
1000
  client = OpenAI()
972
1001
  response = client.chat.completions.create(
973
- model="gpt-4o-mini",
1002
+ model="gpt-5.5",
974
1003
  messages=[
975
1004
  {"role": "system", "content": "You are a helpful assistant."},
976
1005
  {"role": "user", "content": "What is the capital of France?"}
@@ -1017,7 +1046,7 @@ export REVENIUM_TEAM_ID=your-team-id-here
1017
1046
  ============================================================
1018
1047
  REVENIUM USAGE SUMMARY
1019
1048
  ============================================================
1020
- Model: gpt-4o-mini
1049
+ Model: gpt-5.5
1021
1050
  Provider: OPENAI
1022
1051
  Duration: 1.23s
1023
1052
 
@@ -1035,7 +1064,7 @@ Trace ID: abc-123
1035
1064
  ### JSON Format
1036
1065
 
1037
1066
  ```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"}
1067
+ {"model":"gpt-5.5","provider":"OPENAI","durationSeconds":1.23,"inputTokenCount":150,"outputTokenCount":250,"totalTokenCount":400,"cost":0.000045,"costStatus":"available","traceId":"abc-123"}
1039
1068
  ```
1040
1069
 
1041
1070
  ### Cost Status
@@ -1050,7 +1079,9 @@ Trace ID: abc-123
1050
1079
 
1051
1080
  ## Cost Controls / Enforcement
1052
1081
 
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.
1082
+ 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.
1083
+
1084
+ > **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
1085
 
1055
1086
  Currently wired for the OpenAI provider (other providers land via per-provider follow-on tickets).
1056
1087
 
@@ -1076,7 +1107,7 @@ REVENIUM_ENFORCEMENT_BASE_URL=https://api.revenium.ai/profitstream # optional
1076
1107
  | `REVENIUM_TEAM_ID` | — | Hashed team ID. Path component on rule fetches; required when the breaker is enabled. |
1077
1108
  | `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
1109
  | `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. |
1110
+ | `REVENIUM_CB_FAIL_MODE` | `open` | `open` (default) lets calls through when no cache exists; `closed` raises `BudgetExceededError` until rules are loaded. |
1080
1111
  | `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
1112
 
1082
1113
  ### Public API
@@ -1100,7 +1131,7 @@ The pre-call check fires before every chat / embeddings / responses call. When t
1100
1131
  ### Exception Contract
1101
1132
 
1102
1133
  ```python
1103
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1134
+ from revenium_middleware.openai import BudgetExceededError
1104
1135
  ```
1105
1136
 
1106
1137
  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,20 +1145,20 @@ When a tripped rule matches the current request, the middleware raises before th
1114
1145
  | `resets_at` | `str \| None` | ISO-8601 timestamp the rule next resets |
1115
1146
  | `rule_id` | `str \| int \| None` | Server-side rule identifier |
1116
1147
 
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.
1148
+ `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
1149
 
1119
1150
  ```python
1120
- from revenium_middleware.openai import ReveniumCostLimitExceeded
1151
+ from revenium_middleware.openai import BudgetExceededError
1121
1152
  import openai
1122
1153
 
1123
1154
  client = openai.OpenAI()
1124
1155
 
1125
1156
  try:
1126
1157
  response = client.chat.completions.create(
1127
- model="gpt-4o-mini",
1158
+ model="gpt-5.5",
1128
1159
  messages=[{"role": "user", "content": "Summarize the meeting notes"}],
1129
1160
  )
1130
- except ReveniumCostLimitExceeded as exc:
1161
+ except BudgetExceededError as exc:
1131
1162
  print(f"Cost limit reached: {exc.message}")
1132
1163
  print(f"Rule {exc.rule_name}: {exc.current_value} / {exc.threshold}; resets {exc.resets_at}")
1133
1164
  ```
@@ -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.5"
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",
@@ -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")),