smallestai 1.3.0__tar.gz → 4.2.2__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 (200) hide show
  1. smallestai-4.2.2/PKG-INFO +548 -0
  2. smallestai-4.2.2/README.md +500 -0
  3. smallestai-4.2.2/pyproject.toml +73 -0
  4. smallestai-4.2.2/smallestai/__init__.py +85 -0
  5. smallestai-4.2.2/smallestai/atoms/__init__.py +319 -0
  6. smallestai-4.2.2/smallestai/atoms/agent/clients/__init__.py +21 -0
  7. smallestai-4.2.2/smallestai/atoms/agent/clients/base.py +41 -0
  8. smallestai-4.2.2/smallestai/atoms/agent/clients/openai.py +327 -0
  9. smallestai-4.2.2/smallestai/atoms/agent/clients/types.py +62 -0
  10. smallestai-4.2.2/smallestai/atoms/agent/context.py +16 -0
  11. smallestai-4.2.2/smallestai/atoms/agent/events.py +262 -0
  12. smallestai-4.2.2/smallestai/atoms/agent/nodes/__init__.py +9 -0
  13. smallestai-4.2.2/smallestai/atoms/agent/nodes/background_agent.py +57 -0
  14. smallestai-4.2.2/smallestai/atoms/agent/nodes/base.py +196 -0
  15. smallestai-4.2.2/smallestai/atoms/agent/nodes/output_agent.py +148 -0
  16. smallestai-4.2.2/smallestai/atoms/agent/server.py +163 -0
  17. smallestai-4.2.2/smallestai/atoms/agent/session.py +438 -0
  18. smallestai-4.2.2/smallestai/atoms/agent/task_manager.py +232 -0
  19. smallestai-4.2.2/smallestai/atoms/agent/tools/__init__.py +21 -0
  20. smallestai-4.2.2/smallestai/atoms/agent/tools/decorator.py +130 -0
  21. smallestai-4.2.2/smallestai/atoms/agent/tools/registry.py +267 -0
  22. smallestai-4.2.2/smallestai/atoms/agent/tools/schema.py +181 -0
  23. smallestai-4.2.2/smallestai/atoms/agent/type.py +56 -0
  24. smallestai-4.2.2/smallestai/atoms/api/__init__.py +11 -0
  25. smallestai-4.2.2/smallestai/atoms/api/agent_templates_api.py +573 -0
  26. smallestai-4.2.2/smallestai/atoms/api/agents_api.py +2591 -0
  27. smallestai-4.2.2/smallestai/atoms/api/calls_api.py +320 -0
  28. smallestai-4.2.2/smallestai/atoms/api/campaigns_api.py +1689 -0
  29. smallestai-4.2.2/smallestai/atoms/api/knowledge_base_api.py +2271 -0
  30. smallestai-4.2.2/smallestai/atoms/api/logs_api.py +305 -0
  31. smallestai-4.2.2/smallestai/atoms/api/organization_api.py +285 -0
  32. smallestai-4.2.2/smallestai/atoms/api/user_api.py +285 -0
  33. smallestai-4.2.2/smallestai/atoms/api_client.py +797 -0
  34. smallestai-4.2.2/smallestai/atoms/api_response.py +21 -0
  35. smallestai-4.2.2/smallestai/atoms/atoms_client.py +1096 -0
  36. smallestai-4.2.2/smallestai/atoms/audience.py +175 -0
  37. smallestai-4.2.2/smallestai/atoms/call.py +126 -0
  38. smallestai-4.2.2/smallestai/atoms/campaign.py +128 -0
  39. smallestai-4.2.2/smallestai/atoms/configuration.py +582 -0
  40. smallestai-4.2.2/smallestai/atoms/exceptions.py +216 -0
  41. smallestai-4.2.2/smallestai/atoms/kb.py +165 -0
  42. smallestai-4.2.2/smallestai/atoms/models/__init__.py +132 -0
  43. smallestai-4.2.2/smallestai/atoms/models/agent_agent_id_webhook_subscriptions_delete200_response.py +89 -0
  44. smallestai-4.2.2/smallestai/atoms/models/agent_agent_id_webhook_subscriptions_get200_response.py +97 -0
  45. smallestai-4.2.2/smallestai/atoms/models/agent_agent_id_webhook_subscriptions_get404_response.py +89 -0
  46. smallestai-4.2.2/smallestai/atoms/models/agent_agent_id_webhook_subscriptions_post201_response.py +89 -0
  47. smallestai-4.2.2/smallestai/atoms/models/agent_agent_id_webhook_subscriptions_post400_response.py +89 -0
  48. smallestai-4.2.2/smallestai/atoms/models/agent_agent_id_webhook_subscriptions_post_request.py +97 -0
  49. smallestai-4.2.2/smallestai/atoms/models/agent_dto.py +132 -0
  50. smallestai-4.2.2/smallestai/atoms/models/agent_dto_language.py +105 -0
  51. smallestai-4.2.2/smallestai/atoms/models/agent_dto_language_switching.py +95 -0
  52. smallestai-4.2.2/smallestai/atoms/models/agent_dto_synthesizer.py +99 -0
  53. smallestai-4.2.2/smallestai/atoms/models/agent_dto_synthesizer_voice_config.py +111 -0
  54. smallestai-4.2.2/smallestai/atoms/models/agent_from_template_post200_response.py +89 -0
  55. smallestai-4.2.2/smallestai/atoms/models/agent_get200_response.py +93 -0
  56. smallestai-4.2.2/smallestai/atoms/models/agent_get200_response_data.py +97 -0
  57. smallestai-4.2.2/smallestai/atoms/models/agent_id_delete200_response.py +87 -0
  58. smallestai-4.2.2/smallestai/atoms/models/agent_id_get200_response.py +93 -0
  59. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch200_response.py +89 -0
  60. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch_request.py +121 -0
  61. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch_request_language.py +103 -0
  62. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch_request_language_switching.py +96 -0
  63. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch_request_synthesizer.py +110 -0
  64. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch_request_synthesizer_voice_config.py +137 -0
  65. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch_request_synthesizer_voice_config_one_of.py +111 -0
  66. smallestai-4.2.2/smallestai/atoms/models/agent_id_patch_request_synthesizer_voice_config_one_of1.py +99 -0
  67. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response.py +93 -0
  68. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data.py +105 -0
  69. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_edges_inner.py +127 -0
  70. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_edges_inner_data.py +91 -0
  71. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_edges_inner_marker_end.py +91 -0
  72. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner.py +114 -0
  73. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_data.py +115 -0
  74. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_data_variables.py +97 -0
  75. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_data_variables_data_inner.py +91 -0
  76. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_position.py +89 -0
  77. smallestai-4.2.2/smallestai/atoms/models/agent_id_workflow_get404_response.py +89 -0
  78. smallestai-4.2.2/smallestai/atoms/models/agent_template_get200_response.py +97 -0
  79. smallestai-4.2.2/smallestai/atoms/models/agent_template_get200_response_data_inner.py +97 -0
  80. smallestai-4.2.2/smallestai/atoms/models/api_response.py +89 -0
  81. smallestai-4.2.2/smallestai/atoms/models/audience_get200_response.py +97 -0
  82. smallestai-4.2.2/smallestai/atoms/models/audience_get200_response_data_inner.py +102 -0
  83. smallestai-4.2.2/smallestai/atoms/models/audience_id_delete200_response.py +89 -0
  84. smallestai-4.2.2/smallestai/atoms/models/audience_id_delete400_response.py +89 -0
  85. smallestai-4.2.2/smallestai/atoms/models/audience_id_get200_response.py +93 -0
  86. smallestai-4.2.2/smallestai/atoms/models/audience_id_get400_response.py +89 -0
  87. smallestai-4.2.2/smallestai/atoms/models/audience_id_get403_response.py +89 -0
  88. smallestai-4.2.2/smallestai/atoms/models/audience_id_get404_response.py +89 -0
  89. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_delete200_response.py +93 -0
  90. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_delete200_response_data.py +87 -0
  91. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_delete_request.py +87 -0
  92. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_get200_response.py +93 -0
  93. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_get200_response_data.py +101 -0
  94. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_get200_response_data_members_inner.py +89 -0
  95. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_get400_response.py +89 -0
  96. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_get500_response.py +89 -0
  97. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_post200_response.py +97 -0
  98. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_post200_response_data_inner.py +93 -0
  99. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_post200_response_data_inner_data.py +89 -0
  100. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_post400_response.py +89 -0
  101. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_post403_response.py +89 -0
  102. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_post_request.py +87 -0
  103. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_search_get200_response.py +93 -0
  104. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_search_get200_response_data.py +101 -0
  105. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_search_get200_response_data_search_info.py +103 -0
  106. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_search_get400_response.py +89 -0
  107. smallestai-4.2.2/smallestai/atoms/models/audience_id_members_search_get500_response.py +89 -0
  108. smallestai-4.2.2/smallestai/atoms/models/audience_post200_response.py +93 -0
  109. smallestai-4.2.2/smallestai/atoms/models/audience_post200_response_data.py +104 -0
  110. smallestai-4.2.2/smallestai/atoms/models/audience_post400_response.py +89 -0
  111. smallestai-4.2.2/smallestai/atoms/models/bad_request_error_response.py +89 -0
  112. smallestai-4.2.2/smallestai/atoms/models/campaign_get200_response.py +93 -0
  113. smallestai-4.2.2/smallestai/atoms/models/campaign_get200_response_data.py +87 -0
  114. smallestai-4.2.2/smallestai/atoms/models/campaign_get_request.py +89 -0
  115. smallestai-4.2.2/smallestai/atoms/models/campaign_id_get200_response.py +93 -0
  116. smallestai-4.2.2/smallestai/atoms/models/campaign_id_get200_response_data.py +110 -0
  117. smallestai-4.2.2/smallestai/atoms/models/campaign_post201_response.py +89 -0
  118. smallestai-4.2.2/smallestai/atoms/models/campaign_post_request.py +93 -0
  119. smallestai-4.2.2/smallestai/atoms/models/conversation_id_get200_response.py +93 -0
  120. smallestai-4.2.2/smallestai/atoms/models/conversation_id_get200_response_data.py +125 -0
  121. smallestai-4.2.2/smallestai/atoms/models/conversation_outbound_post200_response.py +93 -0
  122. smallestai-4.2.2/smallestai/atoms/models/conversation_outbound_post200_response_data.py +87 -0
  123. smallestai-4.2.2/smallestai/atoms/models/conversation_outbound_post_request.py +89 -0
  124. smallestai-4.2.2/smallestai/atoms/models/create_agent_from_template_request.py +91 -0
  125. smallestai-4.2.2/smallestai/atoms/models/create_agent_request.py +117 -0
  126. smallestai-4.2.2/smallestai/atoms/models/create_agent_request_language.py +128 -0
  127. smallestai-4.2.2/smallestai/atoms/models/create_agent_request_language_synthesizer.py +110 -0
  128. smallestai-4.2.2/smallestai/atoms/models/create_agent_request_language_synthesizer_voice_config.py +137 -0
  129. smallestai-4.2.2/smallestai/atoms/models/internal_server_error_response.py +89 -0
  130. smallestai-4.2.2/smallestai/atoms/models/knowledge_base.py +100 -0
  131. smallestai-4.2.2/smallestai/atoms/models/knowledge_base_item.py +126 -0
  132. smallestai-4.2.2/smallestai/atoms/models/knowledgebase_get200_response.py +97 -0
  133. smallestai-4.2.2/smallestai/atoms/models/knowledgebase_id_get200_response.py +93 -0
  134. smallestai-4.2.2/smallestai/atoms/models/knowledgebase_id_items_get200_response.py +97 -0
  135. smallestai-4.2.2/smallestai/atoms/models/knowledgebase_id_items_upload_text_post_request.py +89 -0
  136. smallestai-4.2.2/smallestai/atoms/models/knowledgebase_post201_response.py +89 -0
  137. smallestai-4.2.2/smallestai/atoms/models/knowledgebase_post_request.py +89 -0
  138. smallestai-4.2.2/smallestai/atoms/models/organization_get200_response.py +93 -0
  139. smallestai-4.2.2/smallestai/atoms/models/organization_get200_response_data.py +105 -0
  140. smallestai-4.2.2/smallestai/atoms/models/organization_get200_response_data_members_inner.py +89 -0
  141. smallestai-4.2.2/smallestai/atoms/models/organization_get200_response_data_subscription.py +87 -0
  142. smallestai-4.2.2/smallestai/atoms/models/product_phone_numbers_get200_response.py +97 -0
  143. smallestai-4.2.2/smallestai/atoms/models/product_phone_numbers_get200_response_data_inner.py +100 -0
  144. smallestai-4.2.2/smallestai/atoms/models/product_phone_numbers_get200_response_data_inner_attributes.py +89 -0
  145. smallestai-4.2.2/smallestai/atoms/models/unauthorized_error_reponse.py +89 -0
  146. smallestai-4.2.2/smallestai/atoms/models/user_get200_response.py +93 -0
  147. smallestai-4.2.2/smallestai/atoms/models/user_get200_response_data.py +99 -0
  148. smallestai-4.2.2/smallestai/atoms/models/webhook.py +124 -0
  149. smallestai-4.2.2/smallestai/atoms/models/webhook_agent.py +91 -0
  150. smallestai-4.2.2/smallestai/atoms/models/webhook_event.py +98 -0
  151. smallestai-4.2.2/smallestai/atoms/models/webhook_get200_response.py +93 -0
  152. smallestai-4.2.2/smallestai/atoms/models/webhook_get200_response_data.py +140 -0
  153. smallestai-4.2.2/smallestai/atoms/models/webhook_id_delete404_response.py +89 -0
  154. smallestai-4.2.2/smallestai/atoms/models/webhook_post201_response.py +89 -0
  155. smallestai-4.2.2/smallestai/atoms/models/webhook_post_request.py +99 -0
  156. smallestai-4.2.2/smallestai/atoms/models/webhook_post_request_events_inner.py +99 -0
  157. smallestai-4.2.2/smallestai/atoms/models/webhook_subscription.py +108 -0
  158. smallestai-4.2.2/smallestai/atoms/models/webhook_subscription_populated.py +112 -0
  159. smallestai-4.2.2/smallestai/atoms/py.typed +0 -0
  160. smallestai-4.2.2/smallestai/atoms/rest.py +258 -0
  161. smallestai-4.2.2/smallestai/cli/agent.py +454 -0
  162. smallestai-4.2.2/smallestai/cli/auth.py +43 -0
  163. smallestai-4.2.2/smallestai/cli/lib/atoms.py +337 -0
  164. smallestai-4.2.2/smallestai/cli/lib/auth.py +48 -0
  165. smallestai-4.2.2/smallestai/cli/lib/chat.py +603 -0
  166. smallestai-4.2.2/smallestai/cli/lib/project_config.py +39 -0
  167. smallestai-4.2.2/smallestai/cli/main.py +32 -0
  168. smallestai-4.2.2/smallestai/cli/utils.py +185 -0
  169. smallestai-4.2.2/smallestai/waves/__init__.py +5 -0
  170. smallestai-4.2.2/smallestai/waves/async_waves_client.py +350 -0
  171. smallestai-4.2.2/smallestai/waves/exceptions.py +18 -0
  172. smallestai-4.2.2/smallestai/waves/models.py +16 -0
  173. smallestai-4.2.2/smallestai/waves/stream_tts.py +207 -0
  174. smallestai-4.2.2/smallestai/waves/utils.py +76 -0
  175. smallestai-4.2.2/smallestai/waves/waves_client.py +274 -0
  176. smallestai-4.2.2/smallestai.egg-info/PKG-INFO +548 -0
  177. smallestai-4.2.2/smallestai.egg-info/SOURCES.txt +181 -0
  178. smallestai-4.2.2/smallestai.egg-info/entry_points.txt +2 -0
  179. smallestai-4.2.2/smallestai.egg-info/requires.txt +35 -0
  180. smallestai-4.2.2/smallestai.egg-info/top_level.txt +1 -0
  181. smallestai-1.3.0/PKG-INFO +0 -262
  182. smallestai-1.3.0/README.md +0 -235
  183. smallestai-1.3.0/pyproject.toml +0 -44
  184. smallestai-1.3.0/smallest/__init__.py +0 -5
  185. smallestai-1.3.0/smallest/async_tts.py +0 -197
  186. smallestai-1.3.0/smallest/exceptions.py +0 -15
  187. smallestai-1.3.0/smallest/models.py +0 -7
  188. smallestai-1.3.0/smallest/stream_tts.py +0 -136
  189. smallestai-1.3.0/smallest/tts.py +0 -191
  190. smallestai-1.3.0/smallest/utils.py +0 -70
  191. smallestai-1.3.0/smallestai.egg-info/PKG-INFO +0 -262
  192. smallestai-1.3.0/smallestai.egg-info/SOURCES.txt +0 -18
  193. smallestai-1.3.0/smallestai.egg-info/requires.txt +0 -13
  194. smallestai-1.3.0/smallestai.egg-info/top_level.txt +0 -1
  195. smallestai-1.3.0/tests/test_async.py +0 -103
  196. smallestai-1.3.0/tests/test_sync.py +0 -98
  197. smallestai-1.3.0/tests/test_utils.py +0 -40
  198. {smallestai-1.3.0 → smallestai-4.2.2}/LICENSE +0 -0
  199. {smallestai-1.3.0 → smallestai-4.2.2}/setup.cfg +0 -0
  200. {smallestai-1.3.0 → smallestai-4.2.2}/smallestai.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,548 @@
1
+ Metadata-Version: 2.4
2
+ Name: smallestai
3
+ Version: 4.2.2
4
+ Summary: Official Python client for the Smallest AI API
5
+ Author-email: Smallest <support@smallest.ai>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/smallest-inc/smallest-python-sdk
8
+ Keywords: smallest,smallest.ai,tts,text-to-speech,waves,atoms
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: aiohttp
16
+ Requires-Dist: aiofiles
17
+ Requires-Dist: requests
18
+ Requires-Dist: pydub
19
+ Requires-Dist: websocket-client
20
+ Requires-Dist: urllib3<3.0.0,>=1.25.3
21
+ Requires-Dist: python-dateutil>=2.8.2
22
+ Requires-Dist: pydantic>=2
23
+ Requires-Dist: typing-extensions>=4.7.1
24
+ Requires-Dist: audioop-lts; python_version >= "3.13"
25
+ Requires-Dist: loguru>=0.7.0
26
+ Requires-Dist: fastapi>=0.115.0
27
+ Requires-Dist: httpx>=0.28.1
28
+ Requires-Dist: python-dotenv>=1.2.1
29
+ Requires-Dist: questionary>=2.1.1
30
+ Requires-Dist: rich>=14.2.0
31
+ Requires-Dist: tomli>=2.3.0
32
+ Requires-Dist: tomli-w>=1.2.0
33
+ Requires-Dist: typer>=0.20.0
34
+ Requires-Dist: uvicorn>=0.32.0
35
+ Requires-Dist: websockets>=14.0
36
+ Requires-Dist: openai>=2.14.0
37
+ Provides-Extra: test
38
+ Requires-Dist: jiwer; extra == "test"
39
+ Requires-Dist: pytest>=7.2.1; extra == "test"
40
+ Requires-Dist: pytest-asyncio; extra == "test"
41
+ Requires-Dist: deepgram-sdk; extra == "test"
42
+ Requires-Dist: pytest-cov>=2.8.1; extra == "test"
43
+ Requires-Dist: tox>=3.9.0; extra == "test"
44
+ Requires-Dist: flake8>=4.0.0; extra == "test"
45
+ Requires-Dist: types-python-dateutil>=2.8.19.14; extra == "test"
46
+ Requires-Dist: mypy>=1.5; extra == "test"
47
+ Dynamic: license-file
48
+
49
+ ![image](https://i.imgur.com/TJ2tT4g.png)
50
+
51
+
52
+ <div align="center">
53
+ <a href="https://twitter.com/smallest_AI">
54
+ <img src="https://img.shields.io/twitter/url/https/twitter.com/smallest_AI.svg?style=social&label=Follow%20smallest_AI" alt="Twitter">
55
+ <a href="https://discord.gg/ywShEyXHBW">
56
+ <img src="https://dcbadge.vercel.app/api/server/ywShEyXHBW?style=flat" alt="Discord">
57
+ </a>
58
+ <a href="https://www.linkedin.com/company/smallest">
59
+ <img src="https://img.shields.io/badge/LinkedIn-Connect-blue" alt="Linkedin">
60
+ </a>
61
+ <a href="https://www.youtube.com/@smallest_ai">
62
+ <img src="https://img.shields.io/static/v1?message=smallest_ai&logo=youtube&label=&color=FF0000&logoColor=white&labelColor=&style=for-the-badge" height=20 alt="Youtube">
63
+ </a>
64
+ </div>
65
+
66
+ ## Official Python Client for Smallest AI API
67
+
68
+ Smallest AI offers an end to end Voice AI suite for developers trying to build real-time voice agents. You can either directly use our Text to Speech APIs through the Waves Client or use the Atoms Client to build and operate end to end enterprise ready Voice Agents.
69
+
70
+ With this sdk, you can easily interact with both Waves and Atoms from any Python 3.9+ application, by utilising WavesClient and AtomsClient classes respectively. Currently, the WavesClient supports direct synthesis and the ability to synthesize streamed LLM output, both synchronously and asynchronously. AtomsClient provides a simpler way of interacting with all our API's to develop and run agentic workflows.
71
+
72
+ To learn how to use our API's, check out our documentation for [Atoms](https://atoms-docs.smallest.ai/introduction) and [Waves](https://waves-docs.smallest.ai/content/introduction/introduction)
73
+
74
+ ## Table of Contents
75
+
76
+ - [Installation](#installation)
77
+ - [Get the API Key](#get-the-api-key)
78
+ - [What are Atoms?](#what-are-atoms)
79
+ - [Creating your first Agent](#creating-your-first-agent)
80
+ - [Placing an outbound call](#placing-an-outbound-call)
81
+ - [Providing context to the agent](#providing-context-to-the-agent)
82
+ - [Configuring workflows to drive conversations](#configuring-workflows-to-drive-conversations)
83
+ - [Provisioning bulk calling using campaigns](#provisioning-bulk-calling-using-campaigns)
84
+ - [Getting started with Waves](#getting-started-with-waves)
85
+ - [Best Practices for Input Text](#best-practices-for-input-text)
86
+ - [Examples](#examples)
87
+ - [Synchronous](#synchronous)
88
+ - [Asynchronous](#asynchronous)
89
+ - [LLM to Speech](#llm-to-speech)
90
+ - [Add your Voice](#add-your-voice)
91
+ - [Synchronously](#add-synchronously)
92
+ - [Asynchronously](#add-asynchronously)
93
+ - [Delete your Voice](#delete-your-voice)
94
+ - [Synchronously](#delete-synchronously)
95
+ - [Asynchronously](#delete-asynchronously)
96
+ - [Available Methods](#available-methods)
97
+ - [Technical Note: WAV Headers in Streaming Audio](#technical-note-wav-headers-in-streaming-audio)
98
+
99
+ ## Installation
100
+
101
+ To install the latest version available
102
+ ```bash
103
+ pip install smallestai
104
+ ```
105
+ When using an SDK in your application, make sure to pin to at least the major version (e.g., ==1.*). This helps ensure your application remains stable and avoids potential issues from breaking changes in future updates.
106
+
107
+
108
+ ## Get the API Key
109
+
110
+ 1. Visit [console.smallest.ai](https://console.smallest.ai//) and sign up for an account or log in if you already have an account.
111
+ 2. Navigate to `API Keys` tab in your account dashboard.
112
+ 3. Create a new API Key and copy it.
113
+ 4. Export the API Key in your environment with the name `SMALLEST_API_KEY`, ensuring that your application can access it securely for authentication.
114
+
115
+
116
+ ## What are Atoms
117
+
118
+ Atoms are agents that can talk to anyone on voice or text in any language, in any voice. Imagine an AI that you can hire to perform end-to-end tasks for your business. The following examples give an overview of how AtomsClient leverages abstractions such as KnowledgeBase, Campaigns and graph-based Workflows to let you build the smartest voice agent for your usecase.
119
+
120
+ You can find the full reference for Atoms [here](./docs/atoms/Api.md).
121
+
122
+ ### Creating your first Agent
123
+
124
+ ```python
125
+ from smallestai.atoms import AtomsClient
126
+
127
+ TARGET_PHONE_NUMBER = "+919666666666"
128
+
129
+ def main():
130
+ # alternatively, you can export API Key as environment variable SMALLEST_API_KEY.
131
+ config = Configuration(
132
+ access_token = 'SMALLEST_API_KEY'
133
+ )
134
+
135
+ atoms_client = AtomsClient(config)
136
+
137
+ agent_id = atoms_client.create_agent(
138
+ create_agent_request={
139
+ "name": "Atoms Multi-Modal Agent",
140
+ "description": "My first atoms agent",
141
+ "language": {
142
+ "enabled": "en",
143
+ "switching": False
144
+ },
145
+ "synthesizer": {
146
+ "voiceConfig": {
147
+ "model": "waves_lightning_large",
148
+ "voiceId": "nyah"
149
+ },
150
+ "speed": 1.2,
151
+ "consistency": 0.5,
152
+ "similarity": 0,
153
+ "enhancement": 1
154
+ },
155
+ "slmModel": "electron",
156
+ }
157
+ ).data
158
+
159
+ print(f"Successfully created agent with id: {agent_id}")
160
+
161
+ if __name__ == "__main__":
162
+ main()
163
+ ```
164
+
165
+ ### Placing an outbound call
166
+
167
+ ```python
168
+ from smallestai.atoms import AtomsClient
169
+ from smallestai.atoms import Configuration
170
+
171
+ TARGET_PHONE_NUMBER = "+919666666666"
172
+ MY_AGENT_ID = "67e****ff*ec***82*3c9e**"
173
+
174
+ def main():
175
+ # assumes you have exported API_KEY in SMALLEST_API_KEY environment variable
176
+ atoms_client = AtomsClient()
177
+
178
+ call_response = atoms_client.start_outbound_call(
179
+ start_outbound_call_request={
180
+ "agent_id": MY_AGENT_ID,
181
+ "phone_number": TARGET_PHONE_NUMBER,
182
+ }
183
+ )
184
+ print(f"Successfully placed call with id: {call_response.conversation_id}")
185
+
186
+ if __name__ == "__main__":
187
+ main()
188
+ ```
189
+ ### Providing context to the agent
190
+
191
+ An agent can be attached to a knowledge base, which it can look up during conversations. Here is how you can do it:
192
+
193
+ ```python
194
+ from smallestai.atoms import AtomsClient
195
+
196
+ def main():
197
+ # assumes you have exported API_KEY in SMALLEST_API_KEY environment variable
198
+ atoms_client = AtomsClient()
199
+
200
+ # Create a new knowledge base
201
+ knowledge_base = atoms_client.create_knowledge_base(
202
+ create_knowledge_base_request={
203
+ "name": "Customer Support Knowledge Base",
204
+ "description": "Contains FAQs and product information"
205
+ }
206
+ )
207
+ knowledge_base_id = knowledge_base.data
208
+
209
+ with open("product_manual.pdf", "rb") as f:
210
+ media_content = f.read()
211
+ media_response = atoms_client.upload_media_to_knowledge_base(
212
+ id=knowledge_base_id,
213
+ media=media_content
214
+ )
215
+ print("Added product_manual.pdf to knowledge base")
216
+
217
+ if __name__ == "__main__":
218
+ main()
219
+ ```
220
+
221
+ ### Configuring workflows to drive conversations
222
+
223
+ An agent can be configured with a graph-based workflow to help it drive meaningful conversations. You can explore making one on our [platform](https://atoms.smallest.ai/dashboard/agents). Refer to our [documentation](https://atoms-docs.smallest.ai/deep-dive/workflow/what-is-a-workflow) for learning more extensively.
224
+
225
+ ![image](https://i.imgur.com/kRs53zV.png)
226
+
227
+ ### Provisioning bulk calling using campaigns
228
+
229
+ To manage bulk calls, you can use [Atoms platform](https://atoms.smallest.ai/dashboard/audience) to create [audience](https://atoms-docs.smallest.ai/deep-dive/audience/audience) (collection of contacts) and then configure [campaigns](https://atoms-docs.smallest.ai/deep-dive/campaign/campaign) to run.
230
+
231
+ ## Getting started with Waves
232
+
233
+ ### Best Practices for Input Text
234
+
235
+ ### Examples
236
+
237
+ #### Synchronous
238
+ A synchronous text-to-speech synthesis client.
239
+
240
+ **Basic Usage:**
241
+ ```python
242
+
243
+ from smallestai.waves import WavesClient
244
+
245
+ def main():
246
+ waves_client = WavesClient(api_key="SMALLEST_API_KEY")
247
+ waves_client.synthesize(
248
+ text="Hello, this is a test for sync synthesis function.",
249
+ save_as="sync_synthesize.wav"
250
+ )
251
+
252
+ if __name__ == "__main__":
253
+ main()
254
+ ```
255
+
256
+ **Parameters:**
257
+ - `api_key`: Your API key (can be set via SMALLEST_API_KEY environment variable)
258
+ - `model`: TTS model to use (default: "lightning")
259
+ - `sample_rate`: Audio sample rate (default: 24000)
260
+ - `voice_id`: Voice ID (default: "emily")
261
+ - `speed`: Speech speed multiplier (default: 1.0)
262
+ - `consistency`: Controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition. Only supported in `lightning-large` model. (default: 0.5)
263
+ - `similarity`: Controls the similarity between the synthesized audio and the reference audio. Increase it to make the speech more similar to the reference audio. Only supported in `lightning-large` model. (default: 0)
264
+ - `enhancement`: Enhances speech quality at the cost of increased latency. Only supported in `lightning-large` model. (default: False)
265
+ - `add_wav_header`: Whether to add a WAV header to the output audio.
266
+
267
+ These parameters are part of the `Smallest` instance. They can be set when creating the instance (as shown above). However, the `synthesize` function also accepts `kwargs`, allowing you to override these parameters for a specific synthesis request.
268
+
269
+ For example, you can modify the speech speed and sample rate just for a particular synthesis call:
270
+ ```py
271
+ client.synthesize(
272
+ "Hello, this is a test for sync synthesis function.",
273
+ save_as="sync_synthesize.wav",
274
+ speed=1.5, # Overrides default speed
275
+ sample_rate=16000 # Overrides default sample rate
276
+ )
277
+ ```
278
+
279
+
280
+ #### Asynchronous
281
+ Asynchronous text-to-speech synthesis client.
282
+
283
+ **Basic Usage:**
284
+ ```python
285
+ import asyncio
286
+ import aiofiles
287
+ import smallestai
288
+
289
+ async def main():
290
+ client = smallestai.waves.AsyncWavesClient(api_key="SMALLEST_API_KEY")
291
+ async with client as tts:
292
+ audio_bytes = await tts.synthesize("Hello, this is a test of the async synthesis function.")
293
+ async with aiofiles.open("async_synthesize.wav", "wb") as f:
294
+ await f.write(audio_bytes) # alternatively you can use the `save_as` parameter.
295
+
296
+ if __name__ == "__main__":
297
+ asyncio.run(main())
298
+ ```
299
+
300
+ **Running Asynchronously in a Jupyter Notebook**
301
+ If you are using a Jupyter Notebook, use the following approach to execute the asynchronous function within an existing event loop:
302
+ ```python
303
+ import asyncio
304
+ import aiofiles
305
+ from smallest import AsyncSmallest
306
+
307
+ async def main():
308
+ client = AsyncSmallest(api_key="SMALLEST_API_KEY")
309
+ async with client as tts:
310
+ audio_bytes = await tts.synthesize("Hello, this is a test of the async synthesis function.")
311
+ async with aiofiles.open("async_synthesize.wav", "wb") as f:
312
+ await f.write(audio_bytes) # alternatively you can use the `save_as` parameter.
313
+
314
+ await main()
315
+ ```
316
+
317
+ **Parameters:**
318
+ - `api_key`: Your API key (can be set via SMALLEST_API_KEY environment variable)
319
+ - `model`: TTS model to use (default: "lightning")
320
+ - `sample_rate`: Audio sample rate (default: 24000)
321
+ - `voice_id`: Voice ID (default: "emily")
322
+ - `speed`: Speech speed multiplier (default: 1.0)
323
+ - `consistency`: Controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition. Only supported in `lightning-large` model.
324
+ - `similarity`: Controls the similarity between the synthesized audio and the reference audio. Increase it to make the speech more similar to the reference audio. Only supported in `lightning-large` model.
325
+ - `enhancement`: Enhances speech quality at the cost of increased latency. Only supported in `lightning-large` model.
326
+ - `add_wav_header`: Whether to add a WAV header to the output audio.
327
+
328
+ These parameters are part of the `AsyncSmallest` instance. They can be set when creating the instance (as shown above). However, the `synthesize` function also accepts `kwargs`, allowing you to override any of these parameters on a per-request basis.
329
+
330
+ For example, you can modify the speech speed and sample rate just for a particular synthesis request:
331
+ ```py
332
+ audio_bytes = await tts.synthesize(
333
+ "Hello, this is a test of the async synthesis function.",
334
+ speed=1.5, # Overrides default speed
335
+ sample_rate=16000 # Overrides default sample rate
336
+ )
337
+ ```
338
+
339
+ #### LLM to Speech
340
+
341
+ The `TextToAudioStream` class provides real-time text-to-speech processing, converting streaming text into audio output. It's particularly useful for applications like voice assistants, live captioning, or interactive chatbots that require immediate audio feedback from text generation. Supports both synchronous and asynchronous TTS instance.
342
+
343
+ ##### Stream through a WebSocket
344
+
345
+ ```python
346
+ import asyncio
347
+ import websockets
348
+ from groq import Groq
349
+ from smallest import Smallest, TextToAudioStream
350
+
351
+ # Initialize Groq (LLM) and Smallest (TTS) instances
352
+ llm = Groq(api_key="GROQ_API_KEY")
353
+ tts = Smallest(api_key="SMALLEST_API_KEY")
354
+ WEBSOCKET_URL = "wss://echo.websocket.events" # Mock WebSocket server
355
+
356
+ # Async function to stream text generation from LLM
357
+ async def generate_text(prompt):
358
+ completion = llm.chat.completions.create(
359
+ messages=[{"role": "user", "content": prompt}],
360
+ model="llama3-8b-8192",
361
+ stream=True,
362
+ )
363
+
364
+ # Yield text as it is generated
365
+ for chunk in completion:
366
+ text = chunk.choices[0].delta.content
367
+ if text:
368
+ yield text
369
+
370
+ # Main function to run the process
371
+ async def main():
372
+ # Initialize the TTS processor
373
+ processor = TextToAudioStream(tts_instance=tts)
374
+
375
+ # Generate text from LLM
376
+ llm_output = generate_text("Explain text to speech like I am five in 5 sentences.")
377
+
378
+ # Stream the generated speech throught a websocket
379
+ async with websockets.connect(WEBSOCKET_URL) as ws:
380
+ print("Connected to WebSocket server.")
381
+
382
+ # Stream the generated speech
383
+ async for audio_chunk in processor.process(llm_output):
384
+ await ws.send(audio_chunk) # Send audio chunk
385
+ echoed_data = await ws.recv() # Receive the echoed message
386
+ print("Received from server:", echoed_data[:20], "...") # Print first 20 bytes
387
+
388
+ print("WebSocket connection closed.")
389
+
390
+ if __name__ == "__main__":
391
+ asyncio.run(main())
392
+ ```
393
+
394
+ ##### Save to a File
395
+ ```python
396
+ import wave
397
+ import asyncio
398
+ from groq import Groq
399
+ from smallest import Smallest, TextToAudioStream
400
+
401
+ llm = Groq(api_key="GROQ_API_KEY")
402
+ tts = Smallest(api_key="SMALLEST_API_KEY")
403
+
404
+ async def generate_text(prompt):
405
+ """Async generator for streaming text from Groq. You can use any LLM"""
406
+ completion = llm.chat.completions.create(
407
+ messages=[
408
+ {
409
+ "role": "user",
410
+ "content": prompt,
411
+ }
412
+ ],
413
+ model="llama3-8b-8192",
414
+ stream=True,
415
+ )
416
+
417
+ for chunk in completion:
418
+ text = chunk.choices[0].delta.content
419
+ if text is not None:
420
+ yield text
421
+
422
+ async def save_audio_to_wav(file_path, processor, llm_output):
423
+ with wave.open(file_path, "wb") as wav_file:
424
+ wav_file.setnchannels(1)
425
+ wav_file.setsampwidth(2)
426
+ wav_file.setframerate(24000)
427
+
428
+ async for audio_chunk in processor.process(llm_output):
429
+ wav_file.writeframes(audio_chunk)
430
+
431
+ async def main():
432
+ # Initialize the TTS processor with the TTS instance
433
+ processor = TextToAudioStream(tts_instance=tts)
434
+
435
+ # Generate text asynchronously and process it
436
+ llm_output = generate_text("Explain text to speech like I am five in 5 sentences.")
437
+
438
+ # As an example, save the generated audio to a WAV file.
439
+ await save_audio_to_wav("llm_to_speech.wav", processor, llm_output)
440
+
441
+ if __name__ == "__main__":
442
+ asyncio.run(main())
443
+ ```
444
+
445
+ **Parameters:**
446
+
447
+ - `tts_instance`: Text-to-speech engine (Smallest or AsyncSmallest)
448
+ - `queue_timeout`: Wait time for new text (seconds, default: 5.0)
449
+ - `max_retries`: Number of retry attempts for failed synthesis (default: 3)
450
+
451
+ **Output Format:**
452
+ The processor yields raw audio data chunks without WAV headers for streaming efficiency. These chunks can be:
453
+
454
+ - Played directly through an audio device
455
+ - Saved to a file
456
+ - Streamed over a network
457
+ - Further processed as needed
458
+
459
+ #### Add your Voice
460
+ The Smallest AI SDK allows you to clone your voice by uploading an audio file. This feature is available both synchronously and asynchronously, making it flexible for different use cases. Below are examples of how to use this functionality.
461
+
462
+ ##### Add Synchronously
463
+ ```python
464
+ from smallest import Smallest
465
+
466
+ def main():
467
+ client = Smallest(api_key="SMALLEST_API_KEY")
468
+ res = client.add_voice(display_name="My Voice", file_path="my_voice.wav")
469
+ print(res)
470
+
471
+ if __name__ == "__main__":
472
+ main()
473
+ ```
474
+
475
+ ##### Add Asynchronously
476
+ ```python
477
+ import asyncio
478
+ from smallest import AsyncSmallest
479
+
480
+ async def main():
481
+ client = AsyncSmallest(api_key="SMALLEST_API_KEY")
482
+ res = await client.add_voice(display_name="My Voice", file_path="my_voice.wav")
483
+ print(res)
484
+
485
+ if __name__ == "__main__":
486
+ asyncio.run(main())
487
+ ```
488
+
489
+ #### Delete your Voice
490
+ The Smallest AI SDK allows you to delete your cloned voice. This feature is available both synchronously and asynchronously, making it flexible for different use cases. Below are examples of how to use this functionality.
491
+
492
+ ##### Delete Synchronously
493
+ ```python
494
+ from smallest import Smallest
495
+
496
+ def main():
497
+ client = Smallest(api_key="SMALLEST_API_KEY")
498
+ res = client.delete_voice(voice_id="voice_id")
499
+ print(res)
500
+
501
+ if __name__ == "__main__":
502
+ main()
503
+ ```
504
+
505
+ ##### Delete Asynchronously
506
+ ```python
507
+ import asyncio
508
+ from smallest import AsyncSmallest
509
+
510
+ async def main():
511
+ client = AsyncSmallest(api_key="SMALLEST_API_KEY")
512
+ res = await client.delete_voice(voice_id="voice_id")
513
+ print(res)
514
+
515
+ if __name__ == "__main__":
516
+ asyncio.run(main())
517
+ ```
518
+
519
+ #### Available Methods
520
+
521
+ ```python
522
+ from smallest import Smallest
523
+
524
+ client = Smallest(api_key="SMALLEST_API_KEY")
525
+
526
+ print(f"Available Languages: {client.get_languages()}")
527
+ print(f"Available Voices: {client.get_voices(model='lightning')}")
528
+ print(f"Available Voices: {client.get_cloned_voices()}")
529
+ print(f"Available Models: {client.get_models()}")
530
+ ```
531
+
532
+ #### Technical Note: WAV Headers in Streaming Audio
533
+
534
+ When implementing audio streaming with chunks of synthesized speech, WAV headers are omitted from individual chunks because:
535
+
536
+ ##### Technical Issues
537
+ - Each WAV header contains metadata about the entire audio file.
538
+ - Multiple headers would make chunks appear as separate audio files and add redundancy.
539
+ - Headers contain file-specific data (like total size) that's invalid for chunks.
540
+ - Sequential playback of chunks with headers causes audio artifacts (pop sounds) when concatenating or playing audio sequentially.
541
+ - Audio players would try to reinitialize audio settings for each chunk.
542
+
543
+ ##### Best Practices for Audio Streaming
544
+ 1. Stream raw PCM audio data without headers
545
+ 2. Add a single WAV header only when:
546
+ - Saving the complete stream to a file
547
+ - Initializing the audio playback system
548
+ - Converting the stream to a standard audio format