payi 0.1.0a35__tar.gz → 0.1.0a36__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.

Potentially problematic release.


This version of payi might be problematic. Click here for more details.

Files changed (157) hide show
  1. payi-0.1.0a36/.release-please-manifest.json +3 -0
  2. {payi-0.1.0a35 → payi-0.1.0a36}/CHANGELOG.md +22 -0
  3. {payi-0.1.0a35 → payi-0.1.0a36}/PKG-INFO +9 -8
  4. {payi-0.1.0a35 → payi-0.1.0a36}/README.md +7 -7
  5. {payi-0.1.0a35 → payi-0.1.0a36}/mypy.ini +1 -1
  6. {payi-0.1.0a35 → payi-0.1.0a36}/pyproject.toml +4 -1
  7. {payi-0.1.0a35 → payi-0.1.0a36}/requirements-dev.lock +5 -2
  8. {payi-0.1.0a35 → payi-0.1.0a36}/requirements.lock +3 -0
  9. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_models.py +4 -4
  10. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_response.py +9 -3
  11. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_version.py +1 -1
  12. payi-0.1.0a36/src/payi/lib/AnthropicInstrumentor.py +97 -0
  13. payi-0.1.0a36/src/payi/lib/Instruments.py +7 -0
  14. payi-0.1.0a36/src/payi/lib/OpenAIInstrumentor.py +89 -0
  15. payi-0.1.0a36/src/payi/lib/Stopwatch.py +27 -0
  16. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/lib/helpers.py +12 -10
  17. payi-0.1.0a36/src/payi/lib/instrument.py +514 -0
  18. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/billing_models.py +2 -2
  19. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/categories/categories.py +2 -2
  20. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/categories/resources.py +2 -2
  21. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/experiences/experiences.py +2 -2
  22. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/experiences/properties.py +2 -2
  23. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/experiences/types.py +2 -2
  24. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/ingest.py +31 -31
  25. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/limits/limits.py +2 -2
  26. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/limits/tags.py +2 -2
  27. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/price_modifiers.py +2 -2
  28. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/requests/properties.py +2 -2
  29. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/requests/requests.py +2 -2
  30. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/ingest_bulk_params.py +1 -1
  31. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/ingest_units_params.py +1 -0
  32. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/shared/evaluation_response.py +0 -1
  33. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_client.py +28 -17
  34. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_models.py +10 -0
  35. payi-0.1.0a35/.release-please-manifest.json +0 -3
  36. {payi-0.1.0a35 → payi-0.1.0a36}/.gitignore +0 -0
  37. {payi-0.1.0a35 → payi-0.1.0a36}/CONTRIBUTING.md +0 -0
  38. {payi-0.1.0a35 → payi-0.1.0a36}/LICENSE +0 -0
  39. {payi-0.1.0a35 → payi-0.1.0a36}/SECURITY.md +0 -0
  40. {payi-0.1.0a35 → payi-0.1.0a36}/api.md +0 -0
  41. {payi-0.1.0a35 → payi-0.1.0a36}/bin/check-release-environment +0 -0
  42. {payi-0.1.0a35 → payi-0.1.0a36}/bin/publish-pypi +0 -0
  43. {payi-0.1.0a35 → payi-0.1.0a36}/examples/.keep +0 -0
  44. {payi-0.1.0a35 → payi-0.1.0a36}/noxfile.py +0 -0
  45. {payi-0.1.0a35 → payi-0.1.0a36}/release-please-config.json +0 -0
  46. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/__init__.py +0 -0
  47. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_base_client.py +0 -0
  48. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_client.py +0 -0
  49. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_compat.py +0 -0
  50. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_constants.py +0 -0
  51. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_exceptions.py +0 -0
  52. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_files.py +0 -0
  53. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_qs.py +0 -0
  54. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_resource.py +0 -0
  55. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_streaming.py +0 -0
  56. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_types.py +0 -0
  57. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/__init__.py +0 -0
  58. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_logs.py +0 -0
  59. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_proxy.py +0 -0
  60. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_reflection.py +0 -0
  61. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_streams.py +0 -0
  62. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_sync.py +0 -0
  63. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_transform.py +0 -0
  64. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_typing.py +0 -0
  65. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/_utils/_utils.py +0 -0
  66. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/lib/.keep +0 -0
  67. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/py.typed +0 -0
  68. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/__init__.py +0 -0
  69. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/categories/__init__.py +0 -0
  70. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/experiences/__init__.py +0 -0
  71. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/limits/__init__.py +0 -0
  72. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/resources/requests/__init__.py +0 -0
  73. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/__init__.py +0 -0
  74. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/billing_model.py +0 -0
  75. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/billing_model_create_params.py +0 -0
  76. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/billing_model_list_response.py +0 -0
  77. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/billing_model_update_params.py +0 -0
  78. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/bulk_ingest_response.py +0 -0
  79. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/categories/__init__.py +0 -0
  80. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/categories/resource_create_params.py +0 -0
  81. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/categories/resource_list_response.py +0 -0
  82. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/category_delete_resource_response.py +0 -0
  83. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/category_delete_response.py +0 -0
  84. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/category_list_resources_response.py +0 -0
  85. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/category_list_response.py +0 -0
  86. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/category_resource_response.py +0 -0
  87. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/category_response.py +0 -0
  88. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/cost_data.py +0 -0
  89. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/cost_details.py +0 -0
  90. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/default_response.py +0 -0
  91. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experience_instance_response.py +0 -0
  92. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/__init__.py +0 -0
  93. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/experience_type.py +0 -0
  94. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/properties_response.py +0 -0
  95. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/property_create_params.py +0 -0
  96. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/type_create_params.py +0 -0
  97. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/type_list_params.py +0 -0
  98. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/type_list_response.py +0 -0
  99. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/experiences/type_update_params.py +0 -0
  100. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/ingest_event_param.py +0 -0
  101. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/ingest_response.py +0 -0
  102. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limit_create_params.py +0 -0
  103. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limit_history_response.py +0 -0
  104. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limit_list_params.py +0 -0
  105. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limit_reset_params.py +0 -0
  106. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limit_response.py +0 -0
  107. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limit_update_params.py +0 -0
  108. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/__init__.py +0 -0
  109. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/limit_tags.py +0 -0
  110. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_create_params.py +0 -0
  111. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_create_response.py +0 -0
  112. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_delete_response.py +0 -0
  113. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_list_response.py +0 -0
  114. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_remove_params.py +0 -0
  115. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_remove_response.py +0 -0
  116. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_update_params.py +0 -0
  117. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/limits/tag_update_response.py +0 -0
  118. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/paged_limit_list.py +0 -0
  119. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/price_modifier.py +0 -0
  120. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/price_modifier_create_params.py +0 -0
  121. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/price_modifier_retrieve_response.py +0 -0
  122. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/price_modifier_update_params.py +0 -0
  123. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/requests/__init__.py +0 -0
  124. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/requests/property_create_params.py +0 -0
  125. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/requests_data.py +0 -0
  126. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/shared/__init__.py +0 -0
  127. {payi-0.1.0a35 → payi-0.1.0a36}/src/payi/types/total_cost_data.py +0 -0
  128. {payi-0.1.0a35 → payi-0.1.0a36}/tests/__init__.py +0 -0
  129. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/__init__.py +0 -0
  130. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/categories/__init__.py +0 -0
  131. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/categories/test_resources.py +0 -0
  132. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/experiences/__init__.py +0 -0
  133. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/experiences/test_properties.py +0 -0
  134. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/experiences/test_types.py +0 -0
  135. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/limits/__init__.py +0 -0
  136. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/limits/test_tags.py +0 -0
  137. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/requests/__init__.py +0 -0
  138. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/requests/test_properties.py +0 -0
  139. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/test_billing_models.py +0 -0
  140. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/test_categories.py +0 -0
  141. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/test_experiences.py +0 -0
  142. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/test_ingest.py +0 -0
  143. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/test_limits.py +0 -0
  144. {payi-0.1.0a35 → payi-0.1.0a36}/tests/api_resources/test_price_modifiers.py +0 -0
  145. {payi-0.1.0a35 → payi-0.1.0a36}/tests/conftest.py +0 -0
  146. {payi-0.1.0a35 → payi-0.1.0a36}/tests/sample_file.txt +0 -0
  147. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_deepcopy.py +0 -0
  148. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_extract_files.py +0 -0
  149. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_files.py +0 -0
  150. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_qs.py +0 -0
  151. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_required_args.py +0 -0
  152. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_response.py +0 -0
  153. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_streaming.py +0 -0
  154. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_transform.py +0 -0
  155. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_utils/test_proxy.py +0 -0
  156. {payi-0.1.0a35 → payi-0.1.0a36}/tests/test_utils/test_typing.py +0 -0
  157. {payi-0.1.0a35 → payi-0.1.0a36}/tests/utils.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.1.0-alpha.36"
3
+ }
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-alpha.36 (2025-01-28)
4
+
5
+ Full Changelog: [v0.1.0-alpha.35...v0.1.0-alpha.36](https://github.com/Pay-i/pay-i-python/compare/v0.1.0-alpha.35...v0.1.0-alpha.36)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **tests:** make test_get_platform less flaky ([#181](https://github.com/Pay-i/pay-i-python/issues/181)) ([86d5b4a](https://github.com/Pay-i/pay-i-python/commit/86d5b4aa623c276776f0bdba5fa936d5408c6ab3))
10
+
11
+
12
+ ### Chores
13
+
14
+ * **internal:** avoid pytest-asyncio deprecation warning ([#182](https://github.com/Pay-i/pay-i-python/issues/182)) ([38bb22c](https://github.com/Pay-i/pay-i-python/commit/38bb22ca2fa0f6dd88942b4744f9b0ffee030d5c))
15
+ * **internal:** codegen related update ([#178](https://github.com/Pay-i/pay-i-python/issues/178)) ([315a2c5](https://github.com/Pay-i/pay-i-python/commit/315a2c576e14d51239590f3bfc53872a288559c7))
16
+ * **internal:** codegen related update ([#183](https://github.com/Pay-i/pay-i-python/issues/183)) ([00fdbe7](https://github.com/Pay-i/pay-i-python/commit/00fdbe791e16fa12e73e1203dd0ab02270127e27))
17
+ * **internal:** codegen related update ([#184](https://github.com/Pay-i/pay-i-python/issues/184)) ([bf04c0e](https://github.com/Pay-i/pay-i-python/commit/bf04c0e4707fc2218da6fa6e676fd6c2e85bff07))
18
+ * **internal:** codegen related update ([#185](https://github.com/Pay-i/pay-i-python/issues/185)) ([e0d7ab1](https://github.com/Pay-i/pay-i-python/commit/e0d7ab154ce6d0a60095d8740b9ba5949a37aa00))
19
+
20
+
21
+ ### Documentation
22
+
23
+ * **raw responses:** fix duplicate `the` ([#180](https://github.com/Pay-i/pay-i-python/issues/180)) ([ef72f36](https://github.com/Pay-i/pay-i-python/commit/ef72f367b02d8a71b6ef108a8e3544be77d5bd60))
24
+
3
25
  ## 0.1.0-alpha.35 (2025-01-12)
4
26
 
5
27
  Full Changelog: [v0.1.0-alpha.34...v0.1.0-alpha.35](https://github.com/Pay-i/pay-i-python/compare/v0.1.0-alpha.34...v0.1.0-alpha.35)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: payi
3
- Version: 0.1.0a35
3
+ Version: 0.1.0a36
4
4
  Summary: The official Python library for the payi API
5
5
  Project-URL: Homepage, https://github.com/Pay-i/pay-i-python
6
6
  Project-URL: Repository, https://github.com/Pay-i/pay-i-python
@@ -28,6 +28,7 @@ Requires-Dist: httpx<1,>=0.23.0
28
28
  Requires-Dist: pydantic<3,>=1.9.0
29
29
  Requires-Dist: sniffio
30
30
  Requires-Dist: typing-extensions<5,>=4.10
31
+ Requires-Dist: wrapt>=1.17.2
31
32
  Description-Content-Type: text/markdown
32
33
 
33
34
  # Payi Python API library
@@ -65,7 +66,7 @@ client = Payi(
65
66
 
66
67
  limit_response = client.limits.create(
67
68
  limit_name="x",
68
- max=0,
69
+ max=1,
69
70
  )
70
71
  print(limit_response.request_id)
71
72
  ```
@@ -92,7 +93,7 @@ client = AsyncPayi(
92
93
  async def main() -> None:
93
94
  limit_response = await client.limits.create(
94
95
  limit_name="x",
95
- max=0,
96
+ max=1,
96
97
  )
97
98
  print(limit_response.request_id)
98
99
 
@@ -129,7 +130,7 @@ client = Payi()
129
130
  try:
130
131
  client.limits.create(
131
132
  limit_name="x",
132
- max=0,
133
+ max=1,
133
134
  )
134
135
  except payi.APIConnectionError as e:
135
136
  print("The server could not be reached")
@@ -175,7 +176,7 @@ client = Payi(
175
176
  # Or, configure per-request:
176
177
  client.with_options(max_retries=5).limits.create(
177
178
  limit_name="x",
178
- max=0,
179
+ max=1,
179
180
  )
180
181
  ```
181
182
 
@@ -201,7 +202,7 @@ client = Payi(
201
202
  # Override per-request:
202
203
  client.with_options(timeout=5.0).limits.create(
203
204
  limit_name="x",
204
- max=0,
205
+ max=1,
205
206
  )
206
207
  ```
207
208
 
@@ -245,7 +246,7 @@ from payi import Payi
245
246
  client = Payi()
246
247
  response = client.limits.with_raw_response.create(
247
248
  limit_name="x",
248
- max=0,
249
+ max=1,
249
250
  )
250
251
  print(response.headers.get('X-My-Header'))
251
252
 
@@ -266,7 +267,7 @@ To stream the response body, use `.with_streaming_response` instead, which requi
266
267
  ```python
267
268
  with client.limits.with_streaming_response.create(
268
269
  limit_name="x",
269
- max=0,
270
+ max=1,
270
271
  ) as response:
271
272
  print(response.headers.get("X-My-Header"))
272
273
 
@@ -33,7 +33,7 @@ client = Payi(
33
33
 
34
34
  limit_response = client.limits.create(
35
35
  limit_name="x",
36
- max=0,
36
+ max=1,
37
37
  )
38
38
  print(limit_response.request_id)
39
39
  ```
@@ -60,7 +60,7 @@ client = AsyncPayi(
60
60
  async def main() -> None:
61
61
  limit_response = await client.limits.create(
62
62
  limit_name="x",
63
- max=0,
63
+ max=1,
64
64
  )
65
65
  print(limit_response.request_id)
66
66
 
@@ -97,7 +97,7 @@ client = Payi()
97
97
  try:
98
98
  client.limits.create(
99
99
  limit_name="x",
100
- max=0,
100
+ max=1,
101
101
  )
102
102
  except payi.APIConnectionError as e:
103
103
  print("The server could not be reached")
@@ -143,7 +143,7 @@ client = Payi(
143
143
  # Or, configure per-request:
144
144
  client.with_options(max_retries=5).limits.create(
145
145
  limit_name="x",
146
- max=0,
146
+ max=1,
147
147
  )
148
148
  ```
149
149
 
@@ -169,7 +169,7 @@ client = Payi(
169
169
  # Override per-request:
170
170
  client.with_options(timeout=5.0).limits.create(
171
171
  limit_name="x",
172
- max=0,
172
+ max=1,
173
173
  )
174
174
  ```
175
175
 
@@ -213,7 +213,7 @@ from payi import Payi
213
213
  client = Payi()
214
214
  response = client.limits.with_raw_response.create(
215
215
  limit_name="x",
216
- max=0,
216
+ max=1,
217
217
  )
218
218
  print(response.headers.get('X-My-Header'))
219
219
 
@@ -234,7 +234,7 @@ To stream the response body, use `.with_streaming_response` instead, which requi
234
234
  ```python
235
235
  with client.limits.with_streaming_response.create(
236
236
  limit_name="x",
237
- max=0,
237
+ max=1,
238
238
  ) as response:
239
239
  print(response.headers.get("X-My-Header"))
240
240
 
@@ -41,7 +41,7 @@ cache_fine_grained = True
41
41
  # ```
42
42
  # Changing this codegen to make mypy happy would increase complexity
43
43
  # and would not be worth it.
44
- disable_error_code = func-returns-value
44
+ disable_error_code = func-returns-value,overload-cannot-match
45
45
 
46
46
  # https://github.com/python/mypy/issues/12162
47
47
  [mypy.overrides]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "payi"
3
- version = "0.1.0-alpha.35"
3
+ version = "0.1.0-alpha.36"
4
4
  description = "The official Python library for the payi API"
5
5
  dynamic = ["readme"]
6
6
  license = "Apache-2.0"
@@ -14,6 +14,7 @@ dependencies = [
14
14
  "anyio>=3.5.0, <5",
15
15
  "distro>=1.7.0, <2",
16
16
  "sniffio",
17
+ "wrapt>=1.17.2",
17
18
  ]
18
19
  requires-python = ">= 3.8"
19
20
  classifiers = [
@@ -55,6 +56,7 @@ dev-dependencies = [
55
56
  "importlib-metadata>=6.7.0",
56
57
  "rich>=13.7.1",
57
58
  "nest_asyncio==1.6.0",
59
+ "wrapt",
58
60
  ]
59
61
 
60
62
  [tool.rye.scripts]
@@ -129,6 +131,7 @@ testpaths = ["tests"]
129
131
  addopts = "--tb=short"
130
132
  xfail_strict = true
131
133
  asyncio_mode = "auto"
134
+ asyncio_default_fixture_loop_scope = "session"
132
135
  filterwarnings = [
133
136
  "error"
134
137
  ]
@@ -7,6 +7,7 @@
7
7
  # all-features: true
8
8
  # with-sources: false
9
9
  # generate-hashes: false
10
+ # universal: false
10
11
 
11
12
  -e file:.
12
13
  annotated-types==0.6.0
@@ -48,7 +49,7 @@ markdown-it-py==3.0.0
48
49
  # via rich
49
50
  mdurl==0.1.2
50
51
  # via markdown-it-py
51
- mypy==1.13.0
52
+ mypy==1.14.1
52
53
  mypy-extensions==1.0.0
53
54
  # via mypy
54
55
  nest-asyncio==1.6.0
@@ -68,7 +69,7 @@ pydantic-core==2.27.1
68
69
  # via pydantic
69
70
  pygments==2.18.0
70
71
  # via rich
71
- pyright==1.1.390
72
+ pyright==1.1.392.post0
72
73
  pytest==8.3.3
73
74
  # via pytest-asyncio
74
75
  pytest-asyncio==0.24.0
@@ -99,5 +100,7 @@ typing-extensions==4.12.2
99
100
  # via pyright
100
101
  virtualenv==20.24.5
101
102
  # via nox
103
+ wrapt==1.17.2
104
+ # via payi
102
105
  zipp==3.17.0
103
106
  # via importlib-metadata
@@ -7,6 +7,7 @@
7
7
  # all-features: true
8
8
  # with-sources: false
9
9
  # generate-hashes: false
10
+ # universal: false
10
11
 
11
12
  -e file:.
12
13
  annotated-types==0.6.0
@@ -42,3 +43,5 @@ typing-extensions==4.12.2
42
43
  # via payi
43
44
  # via pydantic
44
45
  # via pydantic-core
46
+ wrapt==1.17.2
47
+ # via payi
@@ -179,14 +179,14 @@ class BaseModel(pydantic.BaseModel):
179
179
  @classmethod
180
180
  @override
181
181
  def construct( # pyright: ignore[reportIncompatibleMethodOverride]
182
- cls: Type[ModelT],
182
+ __cls: Type[ModelT],
183
183
  _fields_set: set[str] | None = None,
184
184
  **values: object,
185
185
  ) -> ModelT:
186
- m = cls.__new__(cls)
186
+ m = __cls.__new__(__cls)
187
187
  fields_values: dict[str, object] = {}
188
188
 
189
- config = get_model_config(cls)
189
+ config = get_model_config(__cls)
190
190
  populate_by_name = (
191
191
  config.allow_population_by_field_name
192
192
  if isinstance(config, _ConfigProtocol)
@@ -196,7 +196,7 @@ class BaseModel(pydantic.BaseModel):
196
196
  if _fields_set is None:
197
197
  _fields_set = set()
198
198
 
199
- model_fields = get_model_fields(cls)
199
+ model_fields = get_model_fields(__cls)
200
200
  for name, field in model_fields.items():
201
201
  key = field.alias
202
202
  if key is None or (key not in values and populate_by_name):
@@ -136,6 +136,8 @@ class BaseAPIResponse(Generic[R]):
136
136
  if cast_to and is_annotated_type(cast_to):
137
137
  cast_to = extract_type_arg(cast_to, 0)
138
138
 
139
+ origin = get_origin(cast_to) or cast_to
140
+
139
141
  if self._is_sse_stream:
140
142
  if to:
141
143
  if not is_stream_class_type(to):
@@ -195,8 +197,6 @@ class BaseAPIResponse(Generic[R]):
195
197
  if cast_to == bool:
196
198
  return cast(R, response.text.lower() == "true")
197
199
 
198
- origin = get_origin(cast_to) or cast_to
199
-
200
200
  if origin == APIResponse:
201
201
  raise RuntimeError("Unexpected state - cast_to is `APIResponse`")
202
202
 
@@ -210,7 +210,13 @@ class BaseAPIResponse(Generic[R]):
210
210
  raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`")
211
211
  return cast(R, response)
212
212
 
213
- if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel):
213
+ if (
214
+ inspect.isclass(
215
+ origin # pyright: ignore[reportUnknownArgumentType]
216
+ )
217
+ and not issubclass(origin, BaseModel)
218
+ and issubclass(origin, pydantic.BaseModel)
219
+ ):
214
220
  raise TypeError("Pydantic models must subclass our base model type, e.g. `from payi import BaseModel`")
215
221
 
216
222
  if (
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "payi"
4
- __version__ = "0.1.0-alpha.35" # x-release-please-version
4
+ __version__ = "0.1.0-alpha.36" # x-release-please-version
@@ -0,0 +1,97 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ from wrapt import wrap_function_wrapper # type: ignore
5
+
6
+ from payi.types import IngestUnitsParams
7
+ from payi.types.ingest_units_params import Units
8
+
9
+ from .instrument import PayiInstrumentor
10
+
11
+
12
+ class AnthropicIntrumentor:
13
+ @staticmethod
14
+ def instrument(instrumentor: PayiInstrumentor) -> None:
15
+ try:
16
+ import anthropic # type: ignore # noqa: F401 I001
17
+
18
+ # wrap_function_wrapper(
19
+ # "anthropic.resources.completions",
20
+ # "Completions.create",
21
+ # chat_wrapper(instrumentor),
22
+ # )
23
+
24
+ wrap_function_wrapper(
25
+ "anthropic.resources.messages",
26
+ "Messages.create",
27
+ chat_wrapper(instrumentor),
28
+ )
29
+
30
+ wrap_function_wrapper(
31
+ "anthropic.resources.messages",
32
+ "Messages.stream",
33
+ chat_wrapper(instrumentor),
34
+ )
35
+
36
+ except Exception as e:
37
+ logging.debug(f"Error instrumenting anthropic: {e}")
38
+ return
39
+
40
+
41
+ @PayiInstrumentor.payi_wrapper
42
+ def chat_wrapper(
43
+ instrumentor: PayiInstrumentor,
44
+ wrapped: Any,
45
+ instance: Any,
46
+ args: Any,
47
+ kwargs: Any,
48
+ ) -> Any:
49
+ return instrumentor.chat_wrapper(
50
+ "system.anthropic",
51
+ process_chunk,
52
+ process_synchronous_response,
53
+ wrapped,
54
+ instance,
55
+ args,
56
+ kwargs,
57
+ )
58
+
59
+
60
+ def process_chunk(chunk: Any, ingest: IngestUnitsParams) -> None:
61
+ if chunk.type == "message_start":
62
+ usage = chunk.message.usage
63
+ units = ingest["units"]
64
+
65
+ units["text"] = Units(input=usage.input_tokens, output=0)
66
+
67
+ if hasattr(usage, "cache_creation_input_tokens") and usage.cache_creation_input_tokens > 0:
68
+ text_cache_write = usage.cache_creation_input_tokens
69
+ units["text_cache_write"] = Units(input=text_cache_write, output=0)
70
+
71
+ if hasattr(usage, "cache_read_input_tokens") and usage.cache_read_input_tokens > 0:
72
+ text_cache_read = usage.cache_read_input_tokens
73
+ units["text_cache_read"] = Units(input=text_cache_read, output=0)
74
+
75
+ elif chunk.type == "message_delta":
76
+ usage = chunk.usage
77
+ ingest["units"]["text"]["output"] = usage.output_tokens
78
+
79
+
80
+ def process_synchronous_response(response: Any, ingest: IngestUnitsParams, log_prompt_and_response: bool) -> None:
81
+ usage = response.usage
82
+ input = usage.input_tokens
83
+ ouptut = usage.output_tokens
84
+ units: dict[str, Units] = ingest["units"]
85
+
86
+ if hasattr(usage, "cache_creation_input_tokens") and usage.cache_creation_input_tokens > 0:
87
+ text_cache_write = usage.cache_creation_input_tokens
88
+ units["text_cache_write"] = Units(input=text_cache_write, output=0)
89
+
90
+ if hasattr(usage, "cache_read_input_tokens") and usage.cache_read_input_tokens > 0:
91
+ text_cache_read = usage.cache_read_input_tokens
92
+ units["text_cache_read"] = Units(input=text_cache_read, output=0)
93
+
94
+ units["text"] = Units(input=input, output=ouptut)
95
+
96
+ if log_prompt_and_response:
97
+ ingest["provider_response_json"] = response.to_json()
@@ -0,0 +1,7 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Instruments(Enum):
5
+ ALL = "all"
6
+ OPENAI = "openai"
7
+ ANTHROPIC = "anthropic"
@@ -0,0 +1,89 @@
1
+ import json
2
+ import logging
3
+ from typing import Any
4
+ from importlib.metadata import version
5
+
6
+ from wrapt import wrap_function_wrapper # type: ignore
7
+
8
+ from payi.types import IngestUnitsParams
9
+ from payi.types.ingest_units_params import Units
10
+
11
+ from .instrument import PayiInstrumentor
12
+
13
+
14
+ class OpenAiInstrumentor:
15
+ @staticmethod
16
+ def instrument(instrumentor: PayiInstrumentor) -> None:
17
+ try:
18
+ from openai import OpenAI # type: ignore # noqa: F401 I001
19
+
20
+ wrap_function_wrapper(
21
+ "openai.resources.chat.completions",
22
+ "Completions.create",
23
+ chat_wrapper(instrumentor),
24
+ )
25
+ except Exception as e:
26
+ logging.debug(f"Error instrumenting openai: {e}")
27
+ return
28
+
29
+
30
+ @PayiInstrumentor.payi_wrapper
31
+ def chat_wrapper(
32
+ instrumentor: PayiInstrumentor,
33
+ wrapped: Any,
34
+ instance: Any,
35
+ args: Any,
36
+ kwargs: Any,
37
+ ) -> Any:
38
+ return instrumentor.chat_wrapper(
39
+ "system.openai",
40
+ process_chat_chunk,
41
+ process_chat_synchronous_response,
42
+ wrapped,
43
+ instance,
44
+ args,
45
+ kwargs,
46
+ )
47
+
48
+
49
+ def process_chat_synchronous_response(response: str, ingest: IngestUnitsParams, log_prompt_and_response: bool) -> None:
50
+ response_dict = model_to_dict(response)
51
+
52
+ add_usage_units(response_dict["usage"], ingest["units"])
53
+
54
+ if log_prompt_and_response:
55
+ ingest["provider_response_json"] = [json.dumps(response_dict)]
56
+
57
+
58
+ def process_chat_chunk(chunk: Any, ingest: IngestUnitsParams) -> None:
59
+ model = model_to_dict(chunk)
60
+ usage = model.get("usage")
61
+ if usage:
62
+ add_usage_units(usage, ingest["units"])
63
+
64
+
65
+ def model_to_dict(model: Any) -> Any:
66
+ if version("pydantic") < "2.0.0":
67
+ return model.dict()
68
+ if hasattr(model, "model_dump"):
69
+ return model.model_dump()
70
+ elif hasattr(model, "parse"): # Raw API response
71
+ return model_to_dict(model.parse())
72
+ else:
73
+ return model
74
+
75
+
76
+ def add_usage_units(usage: "dict[str, Any]", units: "dict[str, Units]") -> None:
77
+ input = usage["prompt_tokens"] if "prompt_tokens" in usage else 0
78
+ output = usage["completion_tokens"] if "completion_tokens" in usage else 0
79
+ input_cache = 0
80
+
81
+ prompt_tokens_details = usage.get("prompt_tokens_details")
82
+ if prompt_tokens_details:
83
+ input_cache = prompt_tokens_details.get("cached_tokens", 0)
84
+ if input_cache != 0:
85
+ units["text_cache_read"] = Units(input=input_cache, output=0)
86
+
87
+ input -= input_cache
88
+
89
+ units["text"] = Units(input=input, output=output)
@@ -0,0 +1,27 @@
1
+ import time
2
+ from typing import Optional
3
+
4
+
5
+ class Stopwatch:
6
+ def __init__(self) -> None:
7
+ self.start_time: Optional[float] = None
8
+ self.end_time: Optional[float] = None
9
+
10
+ def start(self) -> None:
11
+ self.start_time = time.perf_counter()
12
+
13
+ def stop(self) -> None:
14
+ self.end_time = time.perf_counter()
15
+
16
+ def elapsed_s(self) -> float:
17
+ if self.start_time is None:
18
+ raise ValueError("Stopwatch has not been started")
19
+ if self.end_time is None:
20
+ return time.perf_counter() - self.start_time
21
+ return self.end_time - self.start_time
22
+
23
+ def elapsed_ms(self) -> float:
24
+ return self.elapsed_s() * 1000
25
+
26
+ def elapsed_ms_int(self) -> int:
27
+ return int(self.elapsed_ms())
@@ -3,28 +3,30 @@ from typing import Dict, List, Union
3
3
 
4
4
 
5
5
  def create_limit_header_from_ids(limit_ids: List[str]) -> Dict[str, str]:
6
- if not isinstance(limit_ids, list): # type: ignore
6
+ if not isinstance(limit_ids, list): # type: ignore
7
7
  raise TypeError("limit_ids must be a list")
8
8
 
9
- valid_ids = [id.strip() for id in limit_ids if isinstance(id, str) and id.strip()] # type: ignore
9
+ valid_ids = [id.strip() for id in limit_ids if isinstance(id, str) and id.strip()] # type: ignore
10
10
 
11
11
  return {"xProxy-Limit-IDs": ",".join(valid_ids)} if valid_ids else {}
12
12
 
13
+
13
14
  def create_request_header_from_tags(request_tags: List[str]) -> Dict[str, str]:
14
- if not isinstance(request_tags, list): # type: ignore
15
+ if not isinstance(request_tags, list): # type: ignore
15
16
  raise TypeError("request_tags must be a list")
16
17
 
17
- valid_tags = [tag.strip() for tag in request_tags if isinstance(tag, str) and tag.strip()] # type: ignore
18
-
18
+ valid_tags = [tag.strip() for tag in request_tags if isinstance(tag, str) and tag.strip()] # type: ignore
19
+
19
20
  return {"xProxy-Request-Tags": ",".join(valid_tags)} if valid_tags else {}
20
21
 
22
+
21
23
  def create_headers(
22
- limit_ids: Union[List[str], None] = None,
24
+ limit_ids: Union[List[str], None] = None,
23
25
  request_tags: Union[List[str], None] = None,
24
26
  user_id: Union[str, None] = None,
25
- experience_id: Union[str, None] = None,
26
- experience_name: Union[str, None] = None,
27
- ) -> Dict[str, str]:
27
+ experience_id: Union[str, None] = None,
28
+ experience_name: Union[str, None] = None,
29
+ ) -> Dict[str, str]:
28
30
  headers: Dict[str, str] = {}
29
31
 
30
32
  if limit_ids:
@@ -38,4 +40,4 @@ def create_headers(
38
40
  if experience_name:
39
41
  headers.update({"xProxy-Experience-Name": experience_name})
40
42
 
41
- return headers
43
+ return headers