jaxl-python 0.0.38__tar.gz → 0.0.39__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 (269) hide show
  1. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/PKG-INFO +1 -1
  2. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/__init__.py +2 -0
  3. jaxl_python-0.0.39/examples/streaming_wav_file.py +95 -0
  4. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/_scm_version.py +2 -2
  5. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/apps.py +23 -16
  6. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/calls.py +56 -0
  7. jaxl_python-0.0.39/jaxl/api/resources/silence.py +151 -0
  8. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl_python.egg-info/PKG-INFO +1 -1
  9. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl_python.egg-info/SOURCES.txt +1 -0
  10. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/tests/test_streaming_vad.py +17 -2
  11. jaxl_python-0.0.38/jaxl/api/resources/silence.py +0 -86
  12. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/.github/workflows/sdk.yml +0 -0
  13. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/.gitignore +0 -0
  14. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/.isort.cfg +0 -0
  15. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/.pylintrc +0 -0
  16. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/.vscode/settings.json +0 -0
  17. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/LICENSE.md +0 -0
  18. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/README.md +0 -0
  19. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/SPECIFICATION.md +0 -0
  20. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/docs.sh +0 -0
  21. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/README.md +0 -0
  22. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/prompts/example.txt +0 -0
  23. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/py.typed +0 -0
  24. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/request_and_confirm_code_then_send_to_phone.py +0 -0
  25. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/request_code_and_send_to_phone.py +0 -0
  26. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/send_to_phone.py +0 -0
  27. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/streaming_aiagent.py +0 -0
  28. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/streaming_audio_chunks.py +0 -0
  29. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/streaming_speech_segments.py +0 -0
  30. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/examples/streaming_transcriptions.py +0 -0
  31. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/index.md +0 -0
  32. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/__init__.py +0 -0
  33. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/_client.py +0 -0
  34. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/_sdk.py +0 -0
  35. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/base.py +0 -0
  36. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/cli.py +0 -0
  37. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/__init__.py +0 -0
  38. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/__init__.py +0 -0
  39. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/__init__.py +0 -0
  40. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_app_organizations_list.py +0 -0
  41. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_app_organizations_providers_list.py +0 -0
  42. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_appusers_me_retrieve.py +0 -0
  43. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_add_create.py +0 -0
  44. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_audio_retrieve.py +0 -0
  45. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_hangup_retrieve.py +0 -0
  46. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_list.py +0 -0
  47. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_messages_create.py +0 -0
  48. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_report_retrieve.py +0 -0
  49. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_retrieve.py +0 -0
  50. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_tags_create.py +0 -0
  51. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_token_create.py +0 -0
  52. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_transfer_create.py +0 -0
  53. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_tts_create.py +0 -0
  54. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_calls_usage_retrieve.py +0 -0
  55. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_campaign_list.py +0 -0
  56. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_campaign_upload_create.py +0 -0
  57. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_customer_consumables_retrieve.py +0 -0
  58. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_devices_attest_create.py +0 -0
  59. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_devices_list.py +0 -0
  60. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_integrations_create.py +0 -0
  61. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_ivr_create.py +0 -0
  62. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_ivr_list.py +0 -0
  63. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_ivr_options_create.py +0 -0
  64. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_ivr_options_list.py +0 -0
  65. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_ivr_options_partial_update.py +0 -0
  66. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_kyc_list.py +0 -0
  67. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_messages_list.py +0 -0
  68. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_phonenumbers_list.py +0 -0
  69. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_phonenumbers_partial_update.py +0 -0
  70. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v1/v1_phonenumbers_search_retrieve.py +0 -0
  71. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v2/__init__.py +0 -0
  72. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v2/v2_app_organizations_employees_list.py +0 -0
  73. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v2/v2_app_organizations_groups_list.py +0 -0
  74. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v3/__init__.py +0 -0
  75. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/api/v3/v3_orders_subscriptions_list.py +0 -0
  76. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/client.py +0 -0
  77. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/errors.py +0 -0
  78. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/__init__.py +0 -0
  79. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/address_provider.py +0 -0
  80. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/address_provider_status_enum.py +0 -0
  81. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/analytic.py +0 -0
  82. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/app_price.py +0 -0
  83. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/app_user.py +0 -0
  84. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/available_phone_number.py +0 -0
  85. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/available_phone_number_capabilities.py +0 -0
  86. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/available_phone_number_provider_enum.py +0 -0
  87. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call.py +0 -0
  88. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_add_request_request.py +0 -0
  89. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_audio_reason.py +0 -0
  90. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_cost.py +0 -0
  91. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_location_epoch.py +0 -0
  92. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_message_request_request.py +0 -0
  93. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_message_request_type_enum.py +0 -0
  94. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_metadata.py +0 -0
  95. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_report.py +0 -0
  96. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_report_reason.py +0 -0
  97. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_report_status_enum.py +0 -0
  98. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_tag_request.py +0 -0
  99. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_tag_response.py +0 -0
  100. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_token_request.py +0 -0
  101. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_token_response.py +0 -0
  102. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_transfer_request_request.py +0 -0
  103. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_tts_request_request.py +0 -0
  104. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_type_enum.py +0 -0
  105. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_usage_by_currency_response.py +0 -0
  106. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_usage_response.py +0 -0
  107. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/call_usage_stats_response.py +0 -0
  108. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign.py +0 -0
  109. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_metadata.py +0 -0
  110. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_metadata_metadata.py +0 -0
  111. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_response.py +0 -0
  112. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_response_status_enum.py +0 -0
  113. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_stats.py +0 -0
  114. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_tag.py +0 -0
  115. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_upload_request.py +0 -0
  116. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_upload_request_options.py +0 -0
  117. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_upload_type_enum.py +0 -0
  118. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/campaign_window_request.py +0 -0
  119. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/can_user_resubscribe_plan.py +0 -0
  120. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/canceled_by_enum.py +0 -0
  121. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/capabilities.py +0 -0
  122. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/connection.py +0 -0
  123. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/content_type_enum.py +0 -0
  124. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/country.py +0 -0
  125. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/cta.py +0 -0
  126. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/cta_request.py +0 -0
  127. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/cta_type_enum.py +0 -0
  128. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/currency_enum.py +0 -0
  129. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/customer_consumable_total.py +0 -0
  130. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/customer_order_subscriptions_serializer_v2.py +0 -0
  131. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/customer_order_subscriptions_serializer_v2_status_enum.py +0 -0
  132. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/customer_provider_serializer_v2.py +0 -0
  133. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/device.py +0 -0
  134. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/device_attestation_error.py +0 -0
  135. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/device_attestation_error_reason_enum.py +0 -0
  136. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/device_attestation_response.py +0 -0
  137. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/dh_message.py +0 -0
  138. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/dh_message_attachment.py +0 -0
  139. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/dh_message_reaction.py +0 -0
  140. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/dh_message_type_enum.py +0 -0
  141. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/direction_enum.py +0 -0
  142. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/emoji.py +0 -0
  143. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/emoji_reaction.py +0 -0
  144. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/exotel_auth_request_request.py +0 -0
  145. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/id_enum.py +0 -0
  146. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/integrations_error_response.py +0 -0
  147. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/integrations_properties_request.py +0 -0
  148. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/integrations_request_provider_enum.py +0 -0
  149. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/integrations_request_request.py +0 -0
  150. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/integrations_response.py +0 -0
  151. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/intent_enum.py +0 -0
  152. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/invalid_provider_request.py +0 -0
  153. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/iso_country_enum.py +0 -0
  154. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/item.py +0 -0
  155. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_collection.py +0 -0
  156. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_collection_request.py +0 -0
  157. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_menu_request.py +0 -0
  158. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_menu_response.py +0 -0
  159. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_menu_response_status_enum.py +0 -0
  160. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_options_invalid_response.py +0 -0
  161. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_options_request.py +0 -0
  162. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/ivr_options_response.py +0 -0
  163. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/kyc.py +0 -0
  164. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/kyc_status_enum.py +0 -0
  165. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/kyc_upload_metadata.py +0 -0
  166. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/location.py +0 -0
  167. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/next_or_cta_request.py +0 -0
  168. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/order_status_enum.py +0 -0
  169. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization.py +0 -0
  170. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization_employee.py +0 -0
  171. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization_employee_preferences.py +0 -0
  172. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization_employee_status_enum.py +0 -0
  173. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization_group_inline.py +0 -0
  174. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization_group_response.py +0 -0
  175. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization_preferences.py +0 -0
  176. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/organization_provider.py +0 -0
  177. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_call_list.py +0 -0
  178. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_campaign_response_list.py +0 -0
  179. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_customer_order_subscriptions_serializer_v2_list.py +0 -0
  180. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_device_list.py +0 -0
  181. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_dh_message_list.py +0 -0
  182. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_ivr_menu_response_list.py +0 -0
  183. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_ivr_options_response_list.py +0 -0
  184. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_kyc_list.py +0 -0
  185. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_organization_employee_list.py +0 -0
  186. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_organization_group_response_list.py +0 -0
  187. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_organization_list.py +0 -0
  188. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_organization_provider_list.py +0 -0
  189. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/paginated_phone_number_list.py +0 -0
  190. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/patched_ivr_options_update_request.py +0 -0
  191. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/patched_phone_number_request.py +0 -0
  192. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/payment_gateway_fees_info.py +0 -0
  193. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/period_enum.py +0 -0
  194. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/phone_number.py +0 -0
  195. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/phone_number_attributes.py +0 -0
  196. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/phone_number_capabilities.py +0 -0
  197. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/phone_number_provider_enum.py +0 -0
  198. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/phone_number_search_response.py +0 -0
  199. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/phone_number_status_enum.py +0 -0
  200. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan.py +0 -0
  201. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan_cancel_info.py +0 -0
  202. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan_expiry_timestamp.py +0 -0
  203. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan_expiry_timestamp_type_enum.py +0 -0
  204. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan_extra_details.py +0 -0
  205. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan_item.py +0 -0
  206. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan_type.py +0 -0
  207. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/plan_type_cycle.py +0 -0
  208. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/platform_enum.py +0 -0
  209. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/product_group.py +0 -0
  210. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/proof.py +0 -0
  211. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/proof_status_enum.py +0 -0
  212. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/provider_status_enum.py +0 -0
  213. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/reaction_by.py +0 -0
  214. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/rental_currency_enum.py +0 -0
  215. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/resource_enum.py +0 -0
  216. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/shopify_auth_request_request.py +0 -0
  217. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/stripe_auth_request_request.py +0 -0
  218. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/user_agent.py +0 -0
  219. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/user_agent_browser.py +0 -0
  220. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/user_agent_device.py +0 -0
  221. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/user_agent_operating_system.py +0 -0
  222. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/user_agent_platform.py +0 -0
  223. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/user_identity.py +0 -0
  224. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_app_organizations_list_status_item.py +0 -0
  225. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_calls_list_direction.py +0 -0
  226. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_calls_report_retrieve_date_range.py +0 -0
  227. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_calls_report_retrieve_fields_item.py +0 -0
  228. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_campaign_list_status_item.py +0 -0
  229. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_customer_consumables_retrieve_currency.py +0 -0
  230. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_ivr_list_duration.py +0 -0
  231. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_kyc_list_iso_country.py +0 -0
  232. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_kyc_list_provider_status_item.py +0 -0
  233. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_kyc_list_resource.py +0 -0
  234. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_kyc_list_status.py +0 -0
  235. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_phonenumbers_list_additional_status_item.py +0 -0
  236. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_phonenumbers_list_provider.py +0 -0
  237. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_phonenumbers_list_status.py +0 -0
  238. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_phonenumbers_search_retrieve_intent.py +0 -0
  239. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_phonenumbers_search_retrieve_iso_country_code.py +0 -0
  240. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v1_phonenumbers_search_retrieve_resource.py +0 -0
  241. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v2_app_organizations_employees_list_status_item.py +0 -0
  242. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v3_orders_subscriptions_list_currency.py +0 -0
  243. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/v3_orders_subscriptions_list_status_item.py +0 -0
  244. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/models/why_enum.py +0 -0
  245. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/client/types.py +0 -0
  246. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/py.typed +0 -0
  247. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/__init__.py +0 -0
  248. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/_constants.py +0 -0
  249. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/accounts.py +0 -0
  250. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/campaigns.py +0 -0
  251. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/devices.py +0 -0
  252. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/ivrs.py +0 -0
  253. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/kycs.py +0 -0
  254. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/members.py +0 -0
  255. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/messages.py +0 -0
  256. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/notifications.py +0 -0
  257. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/orgs.py +0 -0
  258. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/payments.py +0 -0
  259. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/phones.py +0 -0
  260. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl/api/resources/teams.py +0 -0
  261. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl_python.egg-info/dependency_links.txt +0 -0
  262. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl_python.egg-info/entry_points.txt +0 -0
  263. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl_python.egg-info/not-zip-safe +0 -0
  264. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl_python.egg-info/requires.txt +0 -0
  265. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/jaxl_python.egg-info/top_level.txt +0 -0
  266. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/mkdocs.yml +0 -0
  267. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/pyproject.toml +0 -0
  268. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/setup.cfg +0 -0
  269. {jaxl_python-0.0.38 → jaxl_python-0.0.39}/setup.gif +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaxl-python
3
- Version: 0.0.38
3
+ Version: 0.0.39
4
4
  Summary: Official Python SDK and CLI for interfacing with the Jaxl API. Manage calls, messages, IVRs, devices, teams, payments, campaigns, streaming transcriptions and AI Agents. Built on OpenAPI, it offers both generated API clients and Pythonic wrappers for ease of use.
5
5
  Home-page: https://github.com/jaxl-innovations-private-limited/jaxl-python
6
6
  Download-URL: https://github.com/jaxl-innovations-private-limited/jaxl-python/archive/main.zip
@@ -16,6 +16,7 @@ from .streaming_aiagent import JaxlAppStreamingAIAgent
16
16
  from .streaming_audio_chunks import JaxlAppStreamingAudioChunk
17
17
  from .streaming_speech_segments import JaxlAppStreamingSpeechSegment
18
18
  from .streaming_transcriptions import JaxlAppStreamingTranscription
19
+ from .streaming_wav_file import JaxlAppStreamingWavFile
19
20
 
20
21
 
21
22
  __all__ = [
@@ -26,4 +27,5 @@ __all__ = [
26
27
  "JaxlAppStreamingSpeechSegment",
27
28
  "JaxlAppStreamingTranscription",
28
29
  "JaxlAppStreamingAIAgent",
30
+ "JaxlAppStreamingWavFile",
29
31
  ]
@@ -0,0 +1,95 @@
1
+ """
2
+ Copyright (c) 2010-present by Jaxl Innovations Private Limited.
3
+
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms,
7
+ with or without modification, is strictly prohibited.
8
+ """
9
+
10
+ import asyncio
11
+ # pylint: disable=deprecated-module
12
+ import audioop
13
+ import os
14
+ import wave
15
+ from pathlib import Path
16
+ from typing import Dict
17
+
18
+ from jaxl.api.base import (
19
+ HANDLER_RESPONSE,
20
+ BaseJaxlApp,
21
+ JaxlWebhookRequest,
22
+ JaxlWebhookResponse,
23
+ )
24
+
25
+
26
+ SAMPLE_RATE = 8000
27
+ SAMPLE_WIDTH = 2
28
+ CHANNELS = 1
29
+ FRAME_MS = 20
30
+ FRAME_BYTES = SAMPLE_RATE * SAMPLE_WIDTH * FRAME_MS // 1000
31
+ SEND_INTERVAL_S = 0.010 # Send faster than real-time to avoid SIP jitter.
32
+
33
+
34
+ def load_wav_as_slin16_8k_mono(path: str) -> bytes:
35
+ with wave.open(path, "rb") as wav:
36
+ audio = wav.readframes(wav.getnframes())
37
+ sample_width = wav.getsampwidth()
38
+ channels = wav.getnchannels()
39
+ sample_rate = wav.getframerate()
40
+
41
+ if channels != CHANNELS:
42
+ audio = audioop.tomono(audio, sample_width, 0.5, 0.5)
43
+ channels = CHANNELS
44
+ if sample_width != SAMPLE_WIDTH:
45
+ audio = audioop.lin2lin(audio, sample_width, SAMPLE_WIDTH)
46
+ sample_width = SAMPLE_WIDTH
47
+ if sample_rate != SAMPLE_RATE:
48
+ audio, _ = audioop.ratecv(
49
+ audio,
50
+ sample_width,
51
+ channels,
52
+ sample_rate,
53
+ SAMPLE_RATE,
54
+ None,
55
+ )
56
+ return audio
57
+
58
+
59
+ class JaxlAppStreamingWavFile(BaseJaxlApp):
60
+ """Minimal incoming-call app that streams one WAV file over the live stream."""
61
+
62
+ def __init__(self) -> None:
63
+ self._playback_tasks: Dict[int, asyncio.Task[None]] = {}
64
+ self._wav_path = os.environ.get("JAXL_STREAMING_WAV_PATH", "example.wav")
65
+ self._audio = load_wav_as_slin16_8k_mono(self._wav_path)
66
+
67
+ async def handle_setup(self, req: JaxlWebhookRequest) -> HANDLER_RESPONSE:
68
+ return JaxlWebhookResponse(prompt=[], num_characters=-1)
69
+
70
+ async def handle_teardown(self, req: JaxlWebhookRequest) -> HANDLER_RESPONSE:
71
+ assert req.state is not None
72
+ self._cancel_playback(req.state.call_id)
73
+ return None
74
+
75
+ async def on_stream_connect(self, call_id: int) -> None:
76
+ self._cancel_playback(call_id)
77
+ self._playback_tasks[call_id] = asyncio.create_task(self._stream_wav(call_id))
78
+
79
+ async def on_stream_disconnect(self, call_id: int) -> None:
80
+ self._cancel_playback(call_id)
81
+
82
+ def _cancel_playback(self, call_id: int) -> None:
83
+ task = self._playback_tasks.pop(call_id, None)
84
+ if task is not None:
85
+ task.cancel()
86
+
87
+ async def _stream_wav(self, call_id: int) -> None:
88
+ print(f"Streaming {Path(self._wav_path)} to call {call_id}")
89
+ for offset in range(0, len(self._audio), FRAME_BYTES):
90
+ chunk = self._audio[offset : offset + FRAME_BYTES]
91
+ if len(chunk) < FRAME_BYTES:
92
+ chunk += b"\x00" * (FRAME_BYTES - len(chunk))
93
+ if not await self.send_audio(call_id, chunk):
94
+ break
95
+ await asyncio.sleep(SEND_INTERVAL_S)
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.0.38'
21
- __version_tuple__ = version_tuple = (0, 0, 38)
20
+ __version__ = version = '0.0.39'
21
+ __version_tuple__ = version_tuple = (0, 0, 39)
@@ -20,8 +20,7 @@ import tempfile
20
20
  import uuid
21
21
  import warnings
22
22
  import wave
23
- from collections import deque
24
- from typing import TYPE_CHECKING, Any, Deque, Dict, List, Optional, cast
23
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
25
24
 
26
25
  from fastapi.websockets import WebSocketState
27
26
  from starlette.websockets import WebSocketDisconnect
@@ -37,7 +36,7 @@ from jaxl.api.base import (
37
36
  from jaxl.api.client.models.call_tag_response import CallTagResponse
38
37
  from jaxl.api.client.types import Response
39
38
  from jaxl.api.resources.accounts import accounts_me
40
- from jaxl.api.resources.calls import calls_hangup, calls_tag_add
39
+ from jaxl.api.resources.calls import calls_ahangup, calls_atag_add
41
40
  from jaxl.api.resources.silence import SilenceDetector
42
41
 
43
42
 
@@ -138,10 +137,10 @@ def _start_server(
138
137
  wss: Dict[int, WebSocket] = {}
139
138
 
140
139
  async def _add_tag(call_id: int, tag: str) -> Response[CallTagResponse]:
141
- return calls_tag_add({"call_id": call_id, "tag": tag})
140
+ return await calls_atag_add({"call_id": call_id, "tag": tag})
142
141
 
143
142
  async def _hangup(call_id: int) -> Response[Any]:
144
- return calls_hangup({"call_id": call_id})
143
+ return await calls_ahangup({"call_id": call_id})
145
144
 
146
145
  async def _clear_audio(call_id: int) -> bool:
147
146
  try:
@@ -239,7 +238,6 @@ def _start_server(
239
238
  speech_frame_threshold=vad_speech_frame_threshold,
240
239
  )
241
240
  speaking: bool = False
242
- buffer: Deque[bytes] = deque(maxlen=4)
243
241
  slin16s: List[bytes] = []
244
242
 
245
243
  await ws.accept()
@@ -257,18 +255,22 @@ def _start_server(
257
255
  # Invoke audio chunk handlers
258
256
  await app.handle_audio_chunk(req, slin16)
259
257
  # Detect start/end of speech
260
- buffer.append(slin16)
261
258
  change = sdetector.process(slin16)
262
- current_frame_is_speech = sdetector.last_frame_is_speech
263
259
  # Manage speech segments
264
260
  if change is True:
265
261
  speaking = change
266
262
  await app.handle_speech_detection(state["call_id"], speaking)
267
- if len(slin16s) == 0:
268
- # Copy over a short, fixed preroll window.
269
- slin16s = list(buffer)
270
- if len(slin16s) > 0:
271
- await app.handle_speech_chunks(req, slin16s)
263
+ # Pull the detector's preroll — this contains the
264
+ # trigger frames (the `speech_frame_threshold` speech
265
+ # frames that fired speech-start) plus a few earlier
266
+ # frames for consonant-onset context. Forwarding
267
+ # this preroll fixes the head-clipping bug where
268
+ # short utterances ("haan", "ji") were truncated.
269
+ preroll_bytes = sdetector.consume_preroll()
270
+ if preroll_bytes:
271
+ preroll_chunks = [preroll_bytes]
272
+ slin16s.extend(preroll_chunks)
273
+ await app.handle_speech_chunks(req, preroll_chunks)
272
274
  elif change is False:
273
275
  speaking = change
274
276
  await app.handle_speech_detection(state["call_id"], speaking)
@@ -290,9 +292,14 @@ def _start_server(
290
292
  else:
291
293
  assert change is None
292
294
  if speaking is True:
293
- if current_frame_is_speech:
294
- await app.handle_speech_chunks(req, [slin16])
295
- slin16s.append(slin16)
295
+ # Forward EVERY chunk during an active speech
296
+ # segment — including frames webrtcvad flagged
297
+ # as not-speech. Inter-word silences (e.g.
298
+ # between "haan" and "maam") are part of the
299
+ # utterance and dropping them corrupts prosody
300
+ # and confuses downstream ASR.
301
+ await app.handle_speech_chunks(req, [slin16])
302
+ slin16s.append(slin16)
296
303
  else:
297
304
  assert speaking is False
298
305
  elif ev == "connected":
@@ -274,6 +274,18 @@ def calls_tag_add(args: Dict[str, Any]) -> Response[CallTagResponse]:
274
274
  )
275
275
 
276
276
 
277
+ async def calls_atag_add(args: Dict[str, Any]) -> Response[CallTagResponse]:
278
+ return await v1_calls_tags_create.asyncio_detailed(
279
+ call_id=args["call_id"],
280
+ client=jaxl_api_client(
281
+ JaxlApiModule.CALL,
282
+ credentials=args.get("credentials", None),
283
+ auth_token=args.get("auth_token", None),
284
+ ),
285
+ json_body=CallTagRequest(name=args["tag"]),
286
+ )
287
+
288
+
277
289
  def calls_hangup(args: Dict[str, Any]) -> Response[Any]:
278
290
  return v1_calls_hangup_retrieve.sync_detailed(
279
291
  id=args["call_id"],
@@ -285,6 +297,17 @@ def calls_hangup(args: Dict[str, Any]) -> Response[Any]:
285
297
  )
286
298
 
287
299
 
300
+ async def calls_ahangup(args: Dict[str, Any]) -> Response[Any]:
301
+ return await v1_calls_hangup_retrieve.asyncio_detailed(
302
+ id=args["call_id"],
303
+ client=jaxl_api_client(
304
+ JaxlApiModule.CALL,
305
+ credentials=args.get("credentials", None),
306
+ auth_token=args.get("auth_token", None),
307
+ ),
308
+ )
309
+
310
+
288
311
  def calls_transfer(args: Dict[str, Any]) -> Response[Any]:
289
312
  return v1_calls_transfer_create.sync_detailed(
290
313
  id=args["call_id"],
@@ -321,6 +344,30 @@ def calls_message(args: Dict[str, Any]) -> Response[Any]:
321
344
  )
322
345
 
323
346
 
347
+ async def calls_amessage(args: Dict[str, Any]) -> Response[Any]:
348
+ return await v1_calls_messages_create.asyncio_detailed(
349
+ id=args["call_id"],
350
+ client=jaxl_api_client(
351
+ JaxlApiModule.CALL,
352
+ credentials=args.get("credentials", None),
353
+ auth_token=args.get("auth_token", None),
354
+ ),
355
+ json_body=CallMessageRequestRequest(
356
+ text=encrypt(args["text"]),
357
+ timestamp=datetime.fromtimestamp(
358
+ args.get("epoch", None) or time.time(),
359
+ tz=timezone.utc,
360
+ ),
361
+ why=WhyEnum[cast(str, args["direction"]).upper()],
362
+ type=(
363
+ CallMessageRequestTypeEnum.VALUE_1
364
+ if args["type"] == "chat"
365
+ else CallMessageRequestTypeEnum.VALUE_10
366
+ ),
367
+ ),
368
+ )
369
+
370
+
324
371
  def calls_audio(args: Dict[str, Any]) -> Response[Any | CallAudioReason]:
325
372
  assert "call_id" in args and "path" in args
326
373
  response = v1_calls_audio_retrieve.sync_detailed(
@@ -611,11 +658,20 @@ class JaxlCallsSDK:
611
658
  def add_tag(self, **kwargs: Any) -> Response[CallTagResponse]:
612
659
  return calls_tag_add(kwargs)
613
660
 
661
+ async def aadd_tag(self, **kwargs: Any) -> Response[CallTagResponse]:
662
+ return await calls_atag_add(kwargs)
663
+
614
664
  def hangup(self, **kwargs: Any) -> Response[Any]:
615
665
  return calls_hangup(kwargs)
616
666
 
667
+ async def ahangup(self, **kwargs: Any) -> Response[Any]:
668
+ return await calls_ahangup(kwargs)
669
+
617
670
  def message(self, **kwargs: Any) -> Response[Any]:
618
671
  return calls_message(kwargs)
619
672
 
673
+ async def amessage(self, **kwargs: Any) -> Response[Any]:
674
+ return await calls_amessage(kwargs)
675
+
620
676
  def audio(self, **kwargs: Any) -> Response[Any | CallAudioReason]:
621
677
  return calls_audio(kwargs)
@@ -0,0 +1,151 @@
1
+ """
2
+ Copyright (c) 2010-present by Jaxl Innovations Private Limited.
3
+
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms,
7
+ with or without modification, is strictly prohibited.
8
+ """
9
+
10
+ from collections import deque
11
+ from typing import Deque, Optional
12
+
13
+
14
+ # pylint: disable=too-many-instance-attributes
15
+ class SilenceDetector:
16
+ """Edge-triggered VAD wrapper around py-webrtcvad.
17
+
18
+ Streaming contract: feed PCM16 mono audio one chunk at a time via
19
+ ``process()``. The method returns:
20
+
21
+ - ``True`` once on the speech-start edge
22
+ - ``False`` once on the speech-end edge
23
+ - ``None`` while no edge has been detected on this call
24
+
25
+ Preroll buffer (FIX for head clipping)
26
+ --------------------------------------
27
+ By design, ``speech_frame_threshold`` consecutive speech frames must
28
+ elapse before the detector declares speech-start. Those trigger
29
+ frames carry the actual onset of the user's utterance (e.g. the "h"
30
+ in "haan") and previously had no public surface for callers to
31
+ recover them — at best the caller kept a tiny rolling preroll that
32
+ was smaller than the trigger window, and the very first speech frame
33
+ was always lost.
34
+
35
+ The detector now maintains its own ``preroll`` deque that captures
36
+ every processed frame. When speech-start fires, the caller MUST call
37
+ ``consume_preroll()`` to retrieve those frames and prepend them to
38
+ the audio it forwards to ASR. ``consume_preroll`` returns the bytes
39
+ AND clears the buffer, so subsequent calls within the same speech
40
+ segment are safe (return empty).
41
+
42
+ The preroll size is configurable but defaults to a slightly larger
43
+ window than ``speech_frame_threshold`` so the caller also receives a
44
+ small amount of audio captured just BEFORE the trigger window — this
45
+ catches consonant onsets that webrtcvad sometimes fails to flag as
46
+ speech (e.g. "h", "f", "s") but still belong to the utterance.
47
+ """
48
+
49
+ # pylint: disable=too-many-positional-arguments
50
+ def __init__(
51
+ self,
52
+ sample_rate: int = 8000,
53
+ frame_duration_ms: int = 20,
54
+ aggressiveness: int = 2,
55
+ silence_frame_threshold: int = 12, # ~240ms
56
+ speech_frame_threshold: int = 5, # ~100ms
57
+ preroll_frames: Optional[int] = None,
58
+ ):
59
+ import webrtcvad
60
+
61
+ self.sample_rate = sample_rate
62
+ self.frame_duration_ms = frame_duration_ms
63
+ self.aggressiveness = aggressiveness
64
+ self.frame_size = int(self.sample_rate * self.frame_duration_ms / 1000)
65
+ self.vad = webrtcvad.Vad(self.aggressiveness)
66
+
67
+ self.speech_frame_threshold = speech_frame_threshold
68
+ self.silence_frame_threshold = silence_frame_threshold
69
+
70
+ # Preroll covers AT LEAST the trigger window plus a few extra frames
71
+ # of pre-trigger audio, so the caller never sees a truncated onset.
72
+ # Five extra frames (~100ms) is enough to recover voiceless
73
+ # consonants that VAD initially missed while keeping memory tiny.
74
+ if preroll_frames is None:
75
+ preroll_frames = speech_frame_threshold + 5
76
+
77
+ self.is_talking = False
78
+ self.speech_frames = 0
79
+ self.silence_frames = 0
80
+ self.buffer = b""
81
+ self.last_frame_is_speech: Optional[bool] = None
82
+ self._preroll: Deque[bytes] = deque(maxlen=preroll_frames)
83
+
84
+ def process(self, slin16: bytes) -> Optional[bool]:
85
+ """Process PCM16 mono audio. Returns:
86
+ - True once on speech start
87
+ - False once on speech end
88
+ - None if no change
89
+
90
+ Side-effect: every fully formed frame is appended to the preroll
91
+ buffer. On the speech-start edge the caller should call
92
+ ``consume_preroll()`` to retrieve the trigger frames.
93
+ """
94
+ self.buffer += slin16
95
+ change = None
96
+
97
+ while len(self.buffer) >= self.frame_size * 2:
98
+ frame, self.buffer = (
99
+ self.buffer[: self.frame_size * 2],
100
+ self.buffer[self.frame_size * 2 :],
101
+ )
102
+
103
+ # Always preserve the frame in the preroll. Bounded by maxlen,
104
+ # so memory is O(preroll_frames) regardless of call duration.
105
+ self._preroll.append(frame)
106
+
107
+ is_speech = self.vad.is_speech(frame, self.sample_rate)
108
+ self.last_frame_is_speech = is_speech
109
+
110
+ if is_speech:
111
+ self.speech_frames += 1
112
+ self.silence_frames = 0
113
+ if (
114
+ not self.is_talking
115
+ and self.speech_frames >= self.speech_frame_threshold
116
+ ):
117
+ change = True # silence -> speech
118
+ self.is_talking = True
119
+ else:
120
+ self.silence_frames += 1
121
+ self.speech_frames = 0
122
+ if (
123
+ self.is_talking
124
+ and self.silence_frames >= self.silence_frame_threshold
125
+ ):
126
+ change = False # speech -> silence
127
+ self.is_talking = False
128
+
129
+ return change
130
+
131
+ def consume_preroll(self) -> bytes:
132
+ """Return the preroll buffer as concatenated PCM16 bytes and clear it.
133
+
134
+ Call this immediately after ``process()`` returns ``True`` (the
135
+ speech-start edge). The returned bytes contain the frames that
136
+ triggered the start plus a few preceding frames for context, and
137
+ should be prepended to the audio stream forwarded to ASR.
138
+
139
+ Subsequent calls within the same speech segment return ``b""``.
140
+ """
141
+ out = b"".join(self._preroll)
142
+ self._preroll.clear()
143
+ return out
144
+
145
+ def reset(self) -> None:
146
+ self.buffer = b""
147
+ self.silence_frames = 0
148
+ self.speech_frames = 0
149
+ self.is_talking = False
150
+ self.last_frame_is_speech = None
151
+ self._preroll.clear()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaxl-python
3
- Version: 0.0.38
3
+ Version: 0.0.39
4
4
  Summary: Official Python SDK and CLI for interfacing with the Jaxl API. Manage calls, messages, IVRs, devices, teams, payments, campaigns, streaming transcriptions and AI Agents. Built on OpenAPI, it offers both generated API clients and Pythonic wrappers for ease of use.
5
5
  Home-page: https://github.com/jaxl-innovations-private-limited/jaxl-python
6
6
  Download-URL: https://github.com/jaxl-innovations-private-limited/jaxl-python/archive/main.zip
@@ -22,6 +22,7 @@ examples/streaming_aiagent.py
22
22
  examples/streaming_audio_chunks.py
23
23
  examples/streaming_speech_segments.py
24
24
  examples/streaming_transcriptions.py
25
+ examples/streaming_wav_file.py
25
26
  examples/prompts/example.txt
26
27
  jaxl/api/__init__.py
27
28
  jaxl/api/_client.py
@@ -73,6 +73,11 @@ class StreamingVadTest(unittest.TestCase):
73
73
  self.assertTrue(change)
74
74
 
75
75
  def test_streaming_vad_preserves_preroll_with_earlier_speech_start(self) -> None:
76
+ """When speech-start fires the pipeline must forward at least the
77
+ speech_frame_threshold trigger frames. Previously a rolling
78
+ deque(maxlen=4) clipped the very first speech frame; the silence
79
+ detector now exposes its own preroll covering ALL trigger frames.
80
+ """
76
81
  app = _StreamingCaptureApp()
77
82
  server = _start_server(app, vad_speech_frame_threshold=5)
78
83
  state = {
@@ -103,9 +108,19 @@ class StreamingVadTest(unittest.TestCase):
103
108
 
104
109
  self.assertEqual(app.speech_detection_events[:2], [True, False])
105
110
  self.assertGreaterEqual(len(app.speech_chunk_batches), 1)
106
- self.assertEqual(len(app.speech_chunk_batches[0]), 4)
111
+ # The first batch is the silence detector's preroll (concatenated
112
+ # PCM16 bytes covering all 5 trigger frames). It is delivered as a
113
+ # single bytes-list entry rather than per-frame, but must contain
114
+ # ALL 5 trigger frames worth of audio.
115
+ first_batch = app.speech_chunk_batches[0]
116
+ first_batch_bytes = b"".join(first_batch)
117
+ # 5 frames * 320 bytes/frame = 1600 bytes — the full trigger window.
118
+ self.assertGreaterEqual(len(first_batch_bytes), 5 * 320)
119
+ # Every byte in the preroll must come from the speech tone (0x01),
120
+ # not from earlier silence frames.
107
121
  self.assertTrue(
108
- all(chunk == b"\x01" * 320 for chunk in app.speech_chunk_batches[0])
122
+ all(byte == 0x01 for byte in first_batch_bytes),
123
+ f"preroll contained non-speech bytes: {first_batch_bytes[:40]!r}",
109
124
  )
110
125
 
111
126
 
@@ -1,86 +0,0 @@
1
- """
2
- Copyright (c) 2010-present by Jaxl Innovations Private Limited.
3
-
4
- All rights reserved.
5
-
6
- Redistribution and use in source and binary forms,
7
- with or without modification, is strictly prohibited.
8
- """
9
-
10
- from typing import Optional
11
-
12
-
13
- # pylint: disable=too-many-instance-attributes
14
- class SilenceDetector:
15
- """Edge-triggered VAD wrapper around py-webrtcvad."""
16
-
17
- # pylint: disable=too-many-positional-arguments
18
- def __init__(
19
- self,
20
- sample_rate: int = 8000,
21
- frame_duration_ms: int = 20,
22
- aggressiveness: int = 2,
23
- silence_frame_threshold: int = 12, # ~240ms
24
- speech_frame_threshold: int = 5, # ~100ms
25
- ):
26
- import webrtcvad
27
-
28
- self.sample_rate = sample_rate
29
- self.frame_duration_ms = frame_duration_ms
30
- self.aggressiveness = aggressiveness
31
- self.frame_size = int(self.sample_rate * self.frame_duration_ms / 1000)
32
- self.vad = webrtcvad.Vad(self.aggressiveness)
33
-
34
- self.speech_frame_threshold = speech_frame_threshold
35
- self.silence_frame_threshold = silence_frame_threshold
36
- self.is_talking = False
37
- self.speech_frames = 0
38
- self.silence_frames = 0
39
- self.buffer = b""
40
- self.last_frame_is_speech: Optional[bool] = None
41
-
42
- def process(self, slin16: bytes) -> Optional[bool]:
43
- """Process PCM16 mono audio. Returns:
44
- - True once on speech start
45
- - False once on speech end
46
- - None if no change
47
- """
48
- self.buffer += slin16
49
- change = None
50
-
51
- while len(self.buffer) >= self.frame_size * 2:
52
- frame, self.buffer = (
53
- self.buffer[: self.frame_size * 2],
54
- self.buffer[self.frame_size * 2 :],
55
- )
56
-
57
- is_speech = self.vad.is_speech(frame, self.sample_rate)
58
- self.last_frame_is_speech = is_speech
59
-
60
- if is_speech:
61
- self.speech_frames += 1
62
- self.silence_frames = 0
63
- if (
64
- not self.is_talking
65
- and self.speech_frames >= self.speech_frame_threshold
66
- ):
67
- change = True # silence -> speech
68
- self.is_talking = True
69
- else:
70
- self.silence_frames += 1
71
- self.speech_frames = 0
72
- if (
73
- self.is_talking
74
- and self.silence_frames >= self.silence_frame_threshold
75
- ):
76
- change = False # speech -> silence
77
- self.is_talking = False
78
-
79
- return change
80
-
81
- def reset(self) -> None:
82
- self.buffer = b""
83
- self.silence_frames = 0
84
- self.speech_frames = 0
85
- self.is_talking = False
86
- self.last_frame_is_speech = None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes