payi 0.1.0a70__tar.gz → 0.1.0a72__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 (205) hide show
  1. payi-0.1.0a72/.release-please-manifest.json +3 -0
  2. {payi-0.1.0a70 → payi-0.1.0a72}/CHANGELOG.md +27 -0
  3. {payi-0.1.0a70 → payi-0.1.0a72}/PKG-INFO +1 -1
  4. {payi-0.1.0a70 → payi-0.1.0a72}/api.md +3 -0
  5. {payi-0.1.0a70 → payi-0.1.0a72}/pyproject.toml +1 -1
  6. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/__init__.py +5 -0
  7. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_proxy.py +4 -1
  8. payi-0.1.0a72/src/payi/_utils/_resources_proxy.py +24 -0
  9. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_version.py +1 -1
  10. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/lib/AnthropicInstrumentor.py +8 -6
  11. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/lib/BedrockInstrumentor.py +8 -8
  12. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/lib/OpenAIInstrumentor.py +11 -10
  13. payi-0.1.0a72/src/payi/lib/VertexInstrumentor.py +313 -0
  14. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/lib/helpers.py +1 -1
  15. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/lib/instrument.py +190 -47
  16. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/ingest.py +55 -24
  17. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/limits/limits.py +0 -8
  18. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/__init__.py +3 -0
  19. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/bulk_ingest_response.py +3 -8
  20. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/category_resource_response.py +2 -0
  21. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/ingest_event_param.py +7 -10
  22. payi-0.1.0a72/src/payi/types/ingest_response.py +18 -0
  23. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/ingest_units_params.py +5 -2
  24. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limit_create_params.py +2 -2
  25. payi-0.1.0a72/src/payi/types/requests/request_result.py +10 -0
  26. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/shared/__init__.py +3 -0
  27. payi-0.1.0a72/src/payi/types/shared/ingest_units.py +13 -0
  28. payi-0.1.0a72/src/payi/types/shared/xproxy_error.py +13 -0
  29. payi-0.1.0a70/src/payi/types/requests/request_result.py → payi-0.1.0a72/src/payi/types/shared/xproxy_result.py +11 -12
  30. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/shared_params/__init__.py +1 -0
  31. payi-0.1.0a72/src/payi/types/shared_params/ingest_units.py +13 -0
  32. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/test_ingest.py +4 -0
  33. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/test_limits.py +0 -4
  34. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_utils/test_proxy.py +11 -0
  35. payi-0.1.0a70/.release-please-manifest.json +0 -3
  36. payi-0.1.0a70/src/payi/types/ingest_response.py +0 -57
  37. {payi-0.1.0a70 → payi-0.1.0a72}/.gitignore +0 -0
  38. {payi-0.1.0a70 → payi-0.1.0a72}/CONTRIBUTING.md +0 -0
  39. {payi-0.1.0a70 → payi-0.1.0a72}/LICENSE +0 -0
  40. {payi-0.1.0a70 → payi-0.1.0a72}/README.md +0 -0
  41. {payi-0.1.0a70 → payi-0.1.0a72}/SECURITY.md +0 -0
  42. {payi-0.1.0a70 → payi-0.1.0a72}/bin/check-release-environment +0 -0
  43. {payi-0.1.0a70 → payi-0.1.0a72}/bin/publish-pypi +0 -0
  44. {payi-0.1.0a70 → payi-0.1.0a72}/examples/.keep +0 -0
  45. {payi-0.1.0a70 → payi-0.1.0a72}/mypy.ini +0 -0
  46. {payi-0.1.0a70 → payi-0.1.0a72}/noxfile.py +0 -0
  47. {payi-0.1.0a70 → payi-0.1.0a72}/release-please-config.json +0 -0
  48. {payi-0.1.0a70 → payi-0.1.0a72}/requirements-dev.lock +0 -0
  49. {payi-0.1.0a70 → payi-0.1.0a72}/requirements.lock +0 -0
  50. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_base_client.py +0 -0
  51. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_client.py +0 -0
  52. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_compat.py +0 -0
  53. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_constants.py +0 -0
  54. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_exceptions.py +0 -0
  55. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_files.py +0 -0
  56. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_models.py +0 -0
  57. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_qs.py +0 -0
  58. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_resource.py +0 -0
  59. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_response.py +0 -0
  60. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_streaming.py +0 -0
  61. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_types.py +0 -0
  62. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/__init__.py +0 -0
  63. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_logs.py +0 -0
  64. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_reflection.py +0 -0
  65. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_streams.py +0 -0
  66. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_sync.py +0 -0
  67. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_transform.py +0 -0
  68. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_typing.py +0 -0
  69. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/_utils/_utils.py +0 -0
  70. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/lib/.keep +0 -0
  71. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/lib/Stopwatch.py +0 -0
  72. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/pagination.py +0 -0
  73. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/py.typed +0 -0
  74. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/__init__.py +0 -0
  75. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/categories/__init__.py +0 -0
  76. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/categories/categories.py +0 -0
  77. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/categories/resources.py +0 -0
  78. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/experiences/__init__.py +0 -0
  79. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/experiences/experiences.py +0 -0
  80. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/experiences/properties.py +0 -0
  81. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/experiences/types/__init__.py +0 -0
  82. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/experiences/types/limit_config.py +0 -0
  83. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/experiences/types/types.py +0 -0
  84. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/limits/__init__.py +0 -0
  85. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/limits/tags.py +0 -0
  86. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/requests/__init__.py +0 -0
  87. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/requests/properties.py +0 -0
  88. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/requests/requests.py +0 -0
  89. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/requests/result.py +0 -0
  90. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/__init__.py +0 -0
  91. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/definitions/__init__.py +0 -0
  92. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/definitions/definitions.py +0 -0
  93. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/definitions/kpis.py +0 -0
  94. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/definitions/limit_config.py +0 -0
  95. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/definitions/version.py +0 -0
  96. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/kpis.py +0 -0
  97. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/properties.py +0 -0
  98. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/resources/use_cases/use_cases.py +0 -0
  99. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/categories/__init__.py +0 -0
  100. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/categories/resource_create_params.py +0 -0
  101. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/categories/resource_list_params.py +0 -0
  102. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/category_delete_resource_response.py +0 -0
  103. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/category_delete_response.py +0 -0
  104. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/category_list_params.py +0 -0
  105. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/category_list_resources_params.py +0 -0
  106. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/category_response.py +0 -0
  107. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/cost_data.py +0 -0
  108. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/cost_details.py +0 -0
  109. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/default_response.py +0 -0
  110. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experience_instance_response.py +0 -0
  111. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/__init__.py +0 -0
  112. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/experience_type.py +0 -0
  113. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/property_create_params.py +0 -0
  114. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/type_create_params.py +0 -0
  115. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/type_list_params.py +0 -0
  116. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/type_update_params.py +0 -0
  117. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/types/__init__.py +0 -0
  118. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/experiences/types/limit_config_create_params.py +0 -0
  119. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/ingest_bulk_params.py +0 -0
  120. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limit_history_response.py +0 -0
  121. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limit_list_params.py +0 -0
  122. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limit_list_response.py +0 -0
  123. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limit_reset_params.py +0 -0
  124. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limit_response.py +0 -0
  125. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limit_update_params.py +0 -0
  126. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/__init__.py +0 -0
  127. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/limit_tags.py +0 -0
  128. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_create_params.py +0 -0
  129. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_create_response.py +0 -0
  130. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_delete_response.py +0 -0
  131. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_list_response.py +0 -0
  132. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_remove_params.py +0 -0
  133. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_remove_response.py +0 -0
  134. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_update_params.py +0 -0
  135. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/limits/tag_update_response.py +0 -0
  136. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/pay_i_common_models_api_router_header_info_param.py +0 -0
  137. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/requests/__init__.py +0 -0
  138. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/requests/property_create_params.py +0 -0
  139. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/requests_data.py +0 -0
  140. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/shared/evaluation_response.py +0 -0
  141. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/shared/pay_i_common_models_budget_management_cost_details_base.py +0 -0
  142. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/shared/pay_i_common_models_budget_management_create_limit_base.py +0 -0
  143. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/shared/properties_response.py +0 -0
  144. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/shared_params/pay_i_common_models_budget_management_create_limit_base.py +0 -0
  145. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/total_cost_data.py +0 -0
  146. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_case_instance_response.py +0 -0
  147. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/__init__.py +0 -0
  148. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definition_create_params.py +0 -0
  149. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definition_list_params.py +0 -0
  150. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definition_update_params.py +0 -0
  151. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/__init__.py +0 -0
  152. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_create_params.py +0 -0
  153. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_create_response.py +0 -0
  154. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_delete_response.py +0 -0
  155. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_list_params.py +0 -0
  156. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_list_response.py +0 -0
  157. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_retrieve_response.py +0 -0
  158. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_update_params.py +0 -0
  159. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/kpi_update_response.py +0 -0
  160. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/definitions/limit_config_create_params.py +0 -0
  161. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/kpi_create_params.py +0 -0
  162. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/kpi_list_params.py +0 -0
  163. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/kpi_list_response.py +0 -0
  164. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/kpi_update_params.py +0 -0
  165. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/property_create_params.py +0 -0
  166. {payi-0.1.0a70 → payi-0.1.0a72}/src/payi/types/use_cases/use_case_definition.py +0 -0
  167. {payi-0.1.0a70 → payi-0.1.0a72}/tests/__init__.py +0 -0
  168. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/__init__.py +0 -0
  169. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/categories/__init__.py +0 -0
  170. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/categories/test_resources.py +0 -0
  171. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/experiences/__init__.py +0 -0
  172. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/experiences/test_properties.py +0 -0
  173. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/experiences/test_types.py +0 -0
  174. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/experiences/types/__init__.py +0 -0
  175. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/experiences/types/test_limit_config.py +0 -0
  176. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/limits/__init__.py +0 -0
  177. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/limits/test_tags.py +0 -0
  178. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/requests/__init__.py +0 -0
  179. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/requests/test_properties.py +0 -0
  180. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/requests/test_result.py +0 -0
  181. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/test_categories.py +0 -0
  182. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/test_experiences.py +0 -0
  183. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/test_use_cases.py +0 -0
  184. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/__init__.py +0 -0
  185. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/definitions/__init__.py +0 -0
  186. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/definitions/test_kpis.py +0 -0
  187. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/definitions/test_limit_config.py +0 -0
  188. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/definitions/test_version.py +0 -0
  189. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/test_definitions.py +0 -0
  190. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/test_kpis.py +0 -0
  191. {payi-0.1.0a70 → payi-0.1.0a72}/tests/api_resources/use_cases/test_properties.py +0 -0
  192. {payi-0.1.0a70 → payi-0.1.0a72}/tests/conftest.py +0 -0
  193. {payi-0.1.0a70 → payi-0.1.0a72}/tests/sample_file.txt +0 -0
  194. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_client.py +0 -0
  195. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_deepcopy.py +0 -0
  196. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_extract_files.py +0 -0
  197. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_files.py +0 -0
  198. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_models.py +0 -0
  199. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_qs.py +0 -0
  200. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_required_args.py +0 -0
  201. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_response.py +0 -0
  202. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_streaming.py +0 -0
  203. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_transform.py +0 -0
  204. {payi-0.1.0a70 → payi-0.1.0a72}/tests/test_utils/test_typing.py +0 -0
  205. {payi-0.1.0a70 → payi-0.1.0a72}/tests/utils.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.1.0-alpha.72"
3
+ }
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-alpha.72 (2025-05-10)
4
+
5
+ Full Changelog: [v0.1.0-alpha.71...v0.1.0-alpha.72](https://github.com/Pay-i/pay-i-python/compare/v0.1.0-alpha.71...v0.1.0-alpha.72)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **package:** support direct resource imports ([314f86c](https://github.com/Pay-i/pay-i-python/commit/314f86cb0d46aa35628c9d059c1b1ca25d24d911))
10
+
11
+
12
+ ### Chores
13
+
14
+ * **internal:** avoid errors for isinstance checks on proxies ([b935f82](https://github.com/Pay-i/pay-i-python/commit/b935f82c9de01353ec83b9c12c4ed6749e984c7d))
15
+
16
+ ## 0.1.0-alpha.71 (2025-05-03)
17
+
18
+ Full Changelog: [v0.1.0-alpha.70...v0.1.0-alpha.71](https://github.com/Pay-i/pay-i-python/compare/v0.1.0-alpha.70...v0.1.0-alpha.71)
19
+
20
+ ### Features
21
+
22
+ * **api:** google vertex ([#294](https://github.com/Pay-i/pay-i-python/issues/294)) ([6d9e39b](https://github.com/Pay-i/pay-i-python/commit/6d9e39b2ee522589d4962c73f697f9cce76fdf6a))
23
+ * **api:** manual updates ([7d11112](https://github.com/Pay-i/pay-i-python/commit/7d1111205f30fe78c9e2e833d3f99a62f95419e8))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * lint ([e7d5a21](https://github.com/Pay-i/pay-i-python/commit/e7d5a210db38448629511b470a4d1b426368577f))
29
+
3
30
  ## 0.1.0-alpha.70 (2025-04-24)
4
31
 
5
32
  Full Changelog: [v0.1.0-alpha.69...v0.1.0-alpha.70](https://github.com/Pay-i/pay-i-python/compare/v0.1.0-alpha.69...v0.1.0-alpha.70)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: payi
3
- Version: 0.1.0a70
3
+ Version: 0.1.0a72
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
@@ -2,9 +2,12 @@
2
2
 
3
3
  ```python
4
4
  from payi.types import (
5
+ IngestUnits,
5
6
  PayICommonModelsBudgetManagementCostDetailsBase,
6
7
  PayICommonModelsBudgetManagementCreateLimitBase,
7
8
  PropertiesResponse,
9
+ XproxyError,
10
+ XproxyResult,
8
11
  )
9
12
  ```
10
13
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "payi"
3
- version = "0.1.0-alpha.70"
3
+ version = "0.1.0-alpha.72"
4
4
  description = "The official Python library for the payi API"
5
5
  dynamic = ["readme"]
6
6
  license = "Apache-2.0"
@@ -1,5 +1,7 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
+ import typing as _t
4
+
3
5
  from . import types
4
6
  from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
5
7
  from ._utils import file_from_path
@@ -68,6 +70,9 @@ __all__ = [
68
70
  "DefaultAsyncHttpxClient",
69
71
  ]
70
72
 
73
+ if not _t.TYPE_CHECKING:
74
+ from ._utils._resources_proxy import resources as resources
75
+
71
76
  _setup_logging()
72
77
 
73
78
  # Update the __module__ attribute for exported symbols so that
@@ -46,7 +46,10 @@ class LazyProxy(Generic[T], ABC):
46
46
  @property # type: ignore
47
47
  @override
48
48
  def __class__(self) -> type: # pyright: ignore
49
- proxied = self.__get_proxied__()
49
+ try:
50
+ proxied = self.__get_proxied__()
51
+ except Exception:
52
+ return type(self)
50
53
  if issubclass(type(proxied), LazyProxy):
51
54
  return type(proxied)
52
55
  return proxied.__class__
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+ from typing_extensions import override
5
+
6
+ from ._proxy import LazyProxy
7
+
8
+
9
+ class ResourcesProxy(LazyProxy[Any]):
10
+ """A proxy for the `payi.resources` module.
11
+
12
+ This is used so that we can lazily import `payi.resources` only when
13
+ needed *and* so that users can just import `payi` and reference `payi.resources`
14
+ """
15
+
16
+ @override
17
+ def __load__(self) -> Any:
18
+ import importlib
19
+
20
+ mod = importlib.import_module("payi.resources")
21
+ return mod
22
+
23
+
24
+ resources = ResourcesProxy().__as_proxied__()
@@ -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.70" # x-release-please-version
4
+ __version__ = "0.1.0-alpha.72" # x-release-please-version
@@ -1,10 +1,11 @@
1
1
  import logging
2
- from typing import Any, Union, Optional
2
+ from typing import Any, Union, Optional, Sequence
3
3
  from typing_extensions import override
4
4
 
5
5
  import tiktoken
6
6
  from wrapt import wrap_function_wrapper # type: ignore
7
7
 
8
+ from payi.lib.helpers import PayiCategories
8
9
  from payi.types.ingest_units_params import Units
9
10
 
10
11
  from .instrument import _IsStreaming, _ProviderRequest, _PayiInstrumentor
@@ -53,7 +54,7 @@ def chat_wrapper(
53
54
  *args: Any,
54
55
  **kwargs: Any,
55
56
  ) -> Any:
56
- return instrumentor.chat_wrapper(
57
+ return instrumentor.invoke_wrapper(
57
58
  _AnthropicProviderRequest(instrumentor),
58
59
  _IsStreaming.kwargs,
59
60
  wrapped,
@@ -70,7 +71,7 @@ async def achat_wrapper(
70
71
  *args: Any,
71
72
  **kwargs: Any,
72
73
  ) -> Any:
73
- return await instrumentor.achat_wrapper(
74
+ return await instrumentor.async_invoke_wrapper(
74
75
  _AnthropicProviderRequest(instrumentor),
75
76
  _IsStreaming.kwargs,
76
77
  wrapped,
@@ -81,7 +82,7 @@ async def achat_wrapper(
81
82
 
82
83
  class _AnthropicProviderRequest(_ProviderRequest):
83
84
  def __init__(self, instrumentor: _PayiInstrumentor):
84
- super().__init__(instrumentor=instrumentor, category="system.anthropic")
85
+ super().__init__(instrumentor=instrumentor, category=PayiCategories.anthropic)
85
86
 
86
87
  @override
87
88
  def process_chunk(self, chunk: Any) -> bool:
@@ -136,7 +137,7 @@ class _AnthropicProviderRequest(_ProviderRequest):
136
137
  return None
137
138
 
138
139
  @override
139
- def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
140
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', args: Sequence[Any], kwargs: Any) -> bool:
140
141
  self._ingest["resource"] = kwargs.get("model", "")
141
142
  messages = kwargs.get("messages")
142
143
  if messages:
@@ -166,7 +167,8 @@ class _AnthropicProviderRequest(_ProviderRequest):
166
167
  self._ingest["http_status_code"] = status_code
167
168
 
168
169
  if not status_code:
169
- return False
170
+ self.exception_to_semantic_failure(exception,)
171
+ return True
170
172
 
171
173
  if hasattr(exception, "request_id"):
172
174
  request_id = getattr(exception, "request_id", None)
@@ -1,13 +1,13 @@
1
1
  import os
2
2
  import json
3
3
  import logging
4
- from typing import Any
4
+ from typing import Any, Sequence
5
5
  from functools import wraps
6
6
  from typing_extensions import override
7
7
 
8
8
  from wrapt import ObjectProxy, wrap_function_wrapper # type: ignore
9
9
 
10
- from payi.lib.helpers import PayiHeaderNames, payi_aws_bedrock_url
10
+ from payi.lib.helpers import PayiCategories, PayiHeaderNames, payi_aws_bedrock_url
11
11
  from payi.types.ingest_units_params import Units, IngestUnitsParams
12
12
  from payi.types.pay_i_common_models_api_router_header_info_param import PayICommonModelsAPIRouterHeaderInfoParam
13
13
 
@@ -148,7 +148,7 @@ def wrap_invoke(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
148
148
  modelId:str = kwargs.get("modelId", "") # type: ignore
149
149
 
150
150
  if _is_supported_model(modelId):
151
- return instrumentor.chat_wrapper(
151
+ return instrumentor.invoke_wrapper(
152
152
  _BedrockInvokeSynchronousProviderRequest(instrumentor=instrumentor),
153
153
  _IsStreaming.false,
154
154
  wrapped,
@@ -166,7 +166,7 @@ def wrap_invoke_stream(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
166
166
  modelId: str = kwargs.get("modelId", "") # type: ignore
167
167
 
168
168
  if _is_supported_model(modelId):
169
- return instrumentor.chat_wrapper(
169
+ return instrumentor.invoke_wrapper(
170
170
  _BedrockInvokeStreamingProviderRequest(instrumentor=instrumentor, model_id=modelId),
171
171
  _IsStreaming.true,
172
172
  wrapped,
@@ -184,7 +184,7 @@ def wrap_converse(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
184
184
  modelId:str = kwargs.get("modelId", "") # type: ignore
185
185
 
186
186
  if _is_supported_model(modelId):
187
- return instrumentor.chat_wrapper(
187
+ return instrumentor.invoke_wrapper(
188
188
  _BedrockConverseSynchronousProviderRequest(instrumentor=instrumentor),
189
189
  _IsStreaming.false,
190
190
  wrapped,
@@ -202,7 +202,7 @@ def wrap_converse_stream(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
202
202
  modelId: str = kwargs.get("modelId", "") # type: ignore
203
203
 
204
204
  if _is_supported_model(modelId):
205
- return instrumentor.chat_wrapper(
205
+ return instrumentor.invoke_wrapper(
206
206
  _BedrockConverseStreamingProviderRequest(instrumentor=instrumentor),
207
207
  _IsStreaming.true,
208
208
  wrapped,
@@ -216,10 +216,10 @@ def wrap_converse_stream(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
216
216
 
217
217
  class _BedrockProviderRequest(_ProviderRequest):
218
218
  def __init__(self, instrumentor: _PayiInstrumentor):
219
- super().__init__(instrumentor=instrumentor, category="system.aws.bedrock")
219
+ super().__init__(instrumentor=instrumentor, category=PayiCategories.aws_bedrock)
220
220
 
221
221
  @override
222
- def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
222
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', args: Sequence[Any], kwargs: Any) -> bool:
223
223
  # boto3 doesn't allow extra_headers
224
224
  kwargs.pop("extra_headers", None)
225
225
  self._ingest["resource"] = kwargs.get("modelId", "")
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import logging
3
- from typing import Any, Union, Optional
3
+ from typing import Any, Union, Optional, Sequence
4
4
  from typing_extensions import override
5
5
  from importlib.metadata import version
6
6
 
@@ -63,7 +63,7 @@ def embeddings_wrapper(
63
63
  *args: Any,
64
64
  **kwargs: Any,
65
65
  ) -> Any:
66
- return instrumentor.chat_wrapper(
66
+ return instrumentor.invoke_wrapper(
67
67
  _OpenAiEmbeddingsProviderRequest(instrumentor),
68
68
  _IsStreaming.false,
69
69
  wrapped,
@@ -80,7 +80,7 @@ async def aembeddings_wrapper(
80
80
  *args: Any,
81
81
  **kwargs: Any,
82
82
  ) -> Any:
83
- return await instrumentor.achat_wrapper(
83
+ return await instrumentor.async_invoke_wrapper(
84
84
  _OpenAiEmbeddingsProviderRequest(instrumentor),
85
85
  _IsStreaming.false,
86
86
  wrapped,
@@ -97,7 +97,7 @@ def chat_wrapper(
97
97
  *args: Any,
98
98
  **kwargs: Any,
99
99
  ) -> Any:
100
- return instrumentor.chat_wrapper(
100
+ return instrumentor.invoke_wrapper(
101
101
  _OpenAiChatProviderRequest(instrumentor),
102
102
  _IsStreaming.kwargs,
103
103
  wrapped,
@@ -114,7 +114,7 @@ async def achat_wrapper(
114
114
  *args: Any,
115
115
  **kwargs: Any,
116
116
  ) -> Any:
117
- return await instrumentor.achat_wrapper(
117
+ return await instrumentor.async_invoke_wrapper(
118
118
  _OpenAiChatProviderRequest(instrumentor),
119
119
  _IsStreaming.kwargs,
120
120
  wrapped,
@@ -125,10 +125,10 @@ async def achat_wrapper(
125
125
 
126
126
  class _OpenAiProviderRequest(_ProviderRequest):
127
127
  def __init__(self, instrumentor: _PayiInstrumentor):
128
- super().__init__(instrumentor=instrumentor, category="system.openai")
128
+ super().__init__(instrumentor=instrumentor, category=PayiCategories.openai)
129
129
 
130
130
  @override
131
- def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
131
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', args: Sequence[Any], kwargs: Any) -> bool: # type: ignore
132
132
  self._ingest["resource"] = kwargs.get("model", "")
133
133
 
134
134
  if not (instance and hasattr(instance, "_client")) or OpenAiInstrumentor.is_azure(instance) is False:
@@ -172,7 +172,8 @@ class _OpenAiProviderRequest(_ProviderRequest):
172
172
  self._ingest["http_status_code"] = status_code
173
173
 
174
174
  if not status_code:
175
- return False
175
+ self.exception_to_semantic_failure(exception,)
176
+ return True
176
177
 
177
178
  if hasattr(exception, "request_id"):
178
179
  request_id = getattr(exception, "request_id", None)
@@ -232,8 +233,8 @@ class _OpenAiChatProviderRequest(_OpenAiProviderRequest):
232
233
  return send_chunk_to_client
233
234
 
234
235
  @override
235
- def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
236
- result = super().process_request(instance, extra_headers, kwargs)
236
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', args: Sequence[Any], kwargs: Any) -> bool:
237
+ result = super().process_request(instance, extra_headers, args, kwargs)
237
238
  if result is False:
238
239
  return result
239
240
 
@@ -0,0 +1,313 @@
1
+ import json
2
+ import math
3
+ import logging
4
+ from typing import Any, List, Union, Optional, Sequence
5
+ from typing_extensions import override
6
+
7
+ from wrapt import wrap_function_wrapper # type: ignore
8
+
9
+ from payi.lib.helpers import PayiCategories
10
+ from payi.types.ingest_units_params import Units
11
+
12
+ from .instrument import _IsStreaming, _ProviderRequest, _PayiInstrumentor
13
+
14
+
15
+ class VertexInstrumentor:
16
+ @staticmethod
17
+ def instrument(instrumentor: _PayiInstrumentor) -> None:
18
+ try:
19
+ import vertexai # type: ignore # noqa: F401 I001
20
+
21
+ wrap_function_wrapper(
22
+ "vertexai.generative_models",
23
+ "GenerativeModel.generate_content",
24
+ generate_wrapper(instrumentor),
25
+ )
26
+
27
+ wrap_function_wrapper(
28
+ "vertexai.preview.generative_models",
29
+ "GenerativeModel.generate_content",
30
+ generate_wrapper(instrumentor),
31
+ )
32
+
33
+ wrap_function_wrapper(
34
+ "vertexai.generative_models",
35
+ "GenerativeModel.generate_content_async",
36
+ agenerate_wrapper(instrumentor),
37
+ )
38
+
39
+ wrap_function_wrapper(
40
+ "vertexai.preview.generative_models",
41
+ "GenerativeModel.generate_content_async",
42
+ agenerate_wrapper(instrumentor),
43
+ )
44
+
45
+ except Exception as e:
46
+ logging.debug(f"Error instrumenting vertex: {e}")
47
+ return
48
+
49
+ @_PayiInstrumentor.payi_wrapper
50
+ def generate_wrapper(
51
+ instrumentor: _PayiInstrumentor,
52
+ wrapped: Any,
53
+ instance: Any,
54
+ *args: Any,
55
+ **kwargs: Any,
56
+ ) -> Any:
57
+ return instrumentor.invoke_wrapper(
58
+ _GoogleVertexRequest(instrumentor),
59
+ _IsStreaming.kwargs,
60
+ wrapped,
61
+ instance,
62
+ args,
63
+ kwargs,
64
+ )
65
+
66
+ @_PayiInstrumentor.payi_awrapper
67
+ async def agenerate_wrapper(
68
+ instrumentor: _PayiInstrumentor,
69
+ wrapped: Any,
70
+ instance: Any,
71
+ *args: Any,
72
+ **kwargs: Any,
73
+ ) -> Any:
74
+ return await instrumentor.async_invoke_wrapper(
75
+ _GoogleVertexRequest(instrumentor),
76
+ _IsStreaming.kwargs,
77
+ wrapped,
78
+ instance,
79
+ args,
80
+ kwargs,
81
+ )
82
+
83
+ def count_chars_skip_spaces(text: str) -> int:
84
+ return sum(1 for c in text if not c.isspace())
85
+
86
+ class _GoogleVertexRequest(_ProviderRequest):
87
+ def __init__(self, instrumentor: _PayiInstrumentor):
88
+ super().__init__(instrumentor=instrumentor, category=PayiCategories.google_vertex)
89
+ self._prompt_character_count = 0
90
+ self._candiates_character_count = 0
91
+
92
+ @override
93
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', args: Sequence[Any], kwargs: Any) -> bool:
94
+ from vertexai.generative_models import Content, Image, Part # type: ignore # noqa: F401 I001
95
+
96
+ if not args:
97
+ return True
98
+
99
+ value: Union[ # type: ignore
100
+ Content,
101
+ str,
102
+ Image,
103
+ Part,
104
+ List[Union[str, Image, Part]],
105
+ ] = args[0] # type: ignore
106
+
107
+ items: List[Union[str, Image, Part]] = [] # type: ignore # noqa: F401 I001
108
+
109
+ if not value:
110
+ raise TypeError("value must not be empty")
111
+
112
+ if isinstance(value, Content):
113
+ return value.parts # type: ignore
114
+ if isinstance(value, (str, Image, Part)):
115
+ items = [value] # type: ignore
116
+
117
+ elif isinstance(value, list):
118
+ items = value # type: ignore
119
+
120
+ for item in items: # type: ignore
121
+ text = ""
122
+ if isinstance(item, Part):
123
+ d = item.to_dict() # type: ignore
124
+ if "text" in d:
125
+ text = d["text"] # type: ignore
126
+ elif isinstance(item, str):
127
+ text = item
128
+
129
+ if text != "":
130
+ self._prompt_character_count += count_chars_skip_spaces(text) # type: ignore
131
+
132
+ return True
133
+
134
+ @override
135
+ def process_request_prompt(self, prompt: 'dict[str, Any]', args: Sequence[Any], kwargs: 'dict[str, Any]') -> None:
136
+ from vertexai.generative_models import Content, Image, Part, Tool # type: ignore # noqa: F401 I001
137
+
138
+ key = "contents"
139
+
140
+ if not args:
141
+ return
142
+
143
+ value: Union[ # type: ignore
144
+ Content,
145
+ str,
146
+ Image,
147
+ Part,
148
+ List[Union[str, Image, Part]],
149
+ ] = args[0] # type: ignore
150
+
151
+ items: List[Union[str, Image, Part]] = [] # type: ignore # noqa: F401 I001
152
+
153
+ if not value:
154
+ return
155
+
156
+ if isinstance(value, str):
157
+ prompt[key] = Content(parts=[Part.from_text(value)]).to_dict() # type: ignore
158
+ elif isinstance(value, (Image, Part)):
159
+ prompt[key] = Content(parts=[value]).to_dict() # type: ignore
160
+ elif isinstance(value, Content):
161
+ prompt[key] = value.to_dict() # type: ignore
162
+ elif isinstance(value, list):
163
+ items = value # type: ignore
164
+ parts = []
165
+
166
+ for item in items: # type: ignore
167
+ if isinstance(item, str):
168
+ parts.append(Part.from_text(item)) # type: ignore
169
+ elif isinstance(item, Part):
170
+ parts.append(item) # type: ignore
171
+ elif isinstance(item, Image):
172
+ parts.append(Part.from_image(item)) # type: ignore
173
+
174
+ prompt[key] = Content(parts=parts).to_dict() # type: ignore
175
+
176
+ tools: Optional[list[Tool]] = kwargs.get("tools", None) # type: ignore
177
+ if tools:
178
+ t: list[dict[Any, Any]] = []
179
+ for tool in tools: # type: ignore
180
+ if isinstance(tool, Tool):
181
+ t.append(tool.to_dict()) # type: ignore
182
+ if t:
183
+ prompt["tools"] = t
184
+
185
+ tool_config = kwargs.get("tool_config", None) # type: ignore
186
+ if tool_config:
187
+ # tool_config does not have to_dict or any other serializable object
188
+ prompt["tool_config"] = str(tool_config) # type: ignore
189
+
190
+ @override
191
+ def process_chunk(self, chunk: Any) -> bool:
192
+ response_dict: dict[str, Any] = chunk.to_dict()
193
+ if "provider_response_id" not in self._ingest:
194
+ id = response_dict.get("response_id", None)
195
+ if id:
196
+ self._ingest["provider_response_id"] = id
197
+
198
+ model: str = response_dict.get("model_version", "")
199
+
200
+ self._ingest["resource"] = "google." + model
201
+
202
+ for candidate in response_dict.get("candidates", []):
203
+ parts = candidate.get("content", {}).get("parts", [])
204
+ for part in parts:
205
+ self._candiates_character_count += count_chars_skip_spaces(part.get("text", ""))
206
+
207
+ usage = response_dict.get("usage_metadata", {})
208
+ if usage and "prompt_token_count" in usage and "candidates_token_count" in usage:
209
+ self._compute_usage(response_dict, streaming_candidates_characters=self._candiates_character_count)
210
+
211
+ return True
212
+
213
+ @staticmethod
214
+ def _is_character_billing_model(model: str) -> bool:
215
+ return model.startswith("gemini-1.")
216
+
217
+ @override
218
+ def process_synchronous_response(
219
+ self,
220
+ response: Any,
221
+ log_prompt_and_response: bool,
222
+ kwargs: Any) -> Any:
223
+ response_dict = response.to_dict()
224
+
225
+ self._ingest["provider_response_id"] = response_dict["response_id"]
226
+ self._ingest["resource"] = "google." + response_dict["model_version"]
227
+
228
+ self._compute_usage(response_dict)
229
+
230
+ if log_prompt_and_response:
231
+ self._ingest["provider_response_json"] = [json.dumps(response_dict)]
232
+
233
+ return None
234
+
235
+ def add_units(self, key: str, input: Optional[int] = None, output: Optional[int] = None) -> None:
236
+ if key not in self._ingest["units"]:
237
+ self._ingest["units"][key] = {}
238
+ if input is not None:
239
+ self._ingest["units"][key]["input"] = input
240
+ if output is not None:
241
+ self._ingest["units"][key]["output"] = output
242
+
243
+ def _compute_usage(self, response_dict: 'dict[str, Any]', streaming_candidates_characters: Optional[int] = None) -> None:
244
+ usage = response_dict.get("usage_metadata", {})
245
+ input = usage.get("prompt_token_count", 0)
246
+
247
+ prompt_tokens_details: list[dict[str, Any]] = usage.get("prompt_tokens_details")
248
+ candidates_tokens_details: list[dict[str, Any]] = usage.get("candidates_tokens_details")
249
+
250
+ model: str = response_dict.get("model_version", "")
251
+
252
+ if self._is_character_billing_model(model):
253
+ # gemini 1.0 and 1.5 units are reported in characters, per second, per image, etc...
254
+ large_context = "" if input < 128000 else "_large_context"
255
+
256
+ for details in prompt_tokens_details:
257
+ modality = details.get("modality", "")
258
+ if not modality:
259
+ continue
260
+
261
+ modality_token_count = details.get("token_count", 0)
262
+ if modality == "TEXT":
263
+ input = self._prompt_character_count
264
+ if input == 0:
265
+ # back up calc if nothing was calculated from the prompt
266
+ input = response_dict["usage_metadata"]["prompt_token_count"] * 4
267
+
268
+ output = 0
269
+ if streaming_candidates_characters is None:
270
+ for candidate in response_dict.get("candidates", []):
271
+ parts = candidate.get("content", {}).get("parts", [])
272
+ for part in parts:
273
+ output += count_chars_skip_spaces(part.get("text", ""))
274
+
275
+ if output == 0:
276
+ # back up calc if no parts
277
+ output = response_dict["usage_metadata"]["candidates_token_count"] * 4
278
+ else:
279
+ output = streaming_candidates_characters
280
+
281
+ self._ingest["units"]["text"+large_context] = Units(input=input, output=output)
282
+
283
+ elif modality == "IMAGE":
284
+ num_images = math.ceil(modality_token_count / 258)
285
+ self.add_units("vision"+large_context, input=num_images)
286
+
287
+ elif modality == "VIDEO":
288
+ video_seconds = math.ceil(modality_token_count / 285)
289
+ self.add_units("video"+large_context, input=video_seconds)
290
+
291
+ elif modality == "AUDIO":
292
+ audio_seconds = math.ceil(modality_token_count / 25)
293
+ self.add_units("audio"+large_context, input=audio_seconds)
294
+
295
+ elif model.startswith("gemini-2.0"):
296
+ for details in prompt_tokens_details:
297
+ modality = details.get("modality", "")
298
+ if not modality:
299
+ continue
300
+
301
+ modality_token_count = details.get("token_count", 0)
302
+ if modality == "IMAGE":
303
+ self.add_units("vision", input=modality_token_count)
304
+ elif modality in ("VIDEO", "AUDIO", "TEXT"):
305
+ self.add_units(modality.lower(), input=modality_token_count)
306
+ for details in candidates_tokens_details:
307
+ modality = details.get("modality", "")
308
+ if not modality:
309
+ continue
310
+
311
+ modality_token_count = details.get("token_count", 0)
312
+ if modality in ("VIDEO", "AUDIO", "TEXT", "IMAGE"):
313
+ self.add_units(modality.lower(), output=modality_token_count)
@@ -22,6 +22,7 @@ class PayiCategories:
22
22
  openai:str = "system.openai"
23
23
  azure_openai:str = "system.azureopenai"
24
24
  aws_bedrock:str = "system.aws.bedrock"
25
+ google_vertex:str = "system.google.vertex"
25
26
 
26
27
  def create_limit_header_from_ids(limit_ids: List[str]) -> Dict[str, str]:
27
28
  if not isinstance(limit_ids, list): # type: ignore
@@ -40,7 +41,6 @@ def create_request_header_from_tags(request_tags: List[str]) -> Dict[str, str]:
40
41
 
41
42
  return { PayiHeaderNames.request_tags: ",".join(valid_tags) } if valid_tags else {}
42
43
 
43
-
44
44
  def create_headers(
45
45
  limit_ids: Union[List[str], None] = None,
46
46
  request_tags: Union[List[str], None] = None,