smallestai 4.0.0__py3-none-any.whl → 4.1.0__py3-none-any.whl

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 smallestai might be problematic. Click here for more details.

Files changed (135) hide show
  1. smallestai/__init__.py +34 -44
  2. smallestai/atoms/__init__.py +249 -123
  3. smallestai/atoms/api/__init__.py +0 -1
  4. smallestai/atoms/api/agent_templates_api.py +26 -26
  5. smallestai/atoms/api/agents_api.py +1316 -190
  6. smallestai/atoms/api/calls_api.py +29 -29
  7. smallestai/atoms/api/campaigns_api.py +165 -165
  8. smallestai/atoms/api/knowledge_base_api.py +290 -290
  9. smallestai/atoms/api/logs_api.py +13 -13
  10. smallestai/atoms/api/organization_api.py +13 -13
  11. smallestai/atoms/api/user_api.py +13 -13
  12. smallestai/atoms/atoms_client.py +77 -49
  13. smallestai/atoms/models/__init__.py +103 -43
  14. smallestai/atoms/models/agent_agent_id_webhook_subscriptions_delete200_response.py +89 -0
  15. smallestai/atoms/models/{get_agent_templates200_response.py → agent_agent_id_webhook_subscriptions_get200_response.py} +7 -7
  16. smallestai/atoms/models/agent_agent_id_webhook_subscriptions_get404_response.py +89 -0
  17. smallestai/atoms/models/agent_agent_id_webhook_subscriptions_post201_response.py +89 -0
  18. smallestai/atoms/models/agent_agent_id_webhook_subscriptions_post400_response.py +89 -0
  19. smallestai/atoms/models/agent_agent_id_webhook_subscriptions_post_request.py +97 -0
  20. smallestai/atoms/models/agent_dto.py +8 -6
  21. smallestai/atoms/models/agent_dto_language.py +17 -3
  22. smallestai/atoms/models/agent_dto_language_switching.py +95 -0
  23. smallestai/atoms/models/agent_dto_synthesizer.py +1 -1
  24. smallestai/atoms/models/{create_agent_from_template200_response.py → agent_from_template_post200_response.py} +4 -4
  25. smallestai/atoms/models/{get_agents200_response.py → agent_get200_response.py} +7 -7
  26. smallestai/atoms/models/{get_agents200_response_data.py → agent_get200_response_data.py} +9 -13
  27. smallestai/atoms/models/{delete_agent200_response.py → agent_id_delete200_response.py} +4 -4
  28. smallestai/atoms/models/{get_agent_by_id200_response.py → agent_id_get200_response.py} +4 -4
  29. smallestai/atoms/models/{update_agent200_response.py → agent_id_patch200_response.py} +4 -4
  30. smallestai/atoms/models/{update_agent_request.py → agent_id_patch_request.py} +17 -15
  31. smallestai/atoms/models/{update_agent_request_language.py → agent_id_patch_request_language.py} +14 -10
  32. smallestai/atoms/models/agent_id_patch_request_language_switching.py +96 -0
  33. smallestai/atoms/models/{update_agent_request_synthesizer.py → agent_id_patch_request_synthesizer.py} +6 -6
  34. smallestai/atoms/models/{update_agent_request_synthesizer_voice_config.py → agent_id_patch_request_synthesizer_voice_config.py} +27 -27
  35. smallestai/atoms/models/{update_agent_request_synthesizer_voice_config_one_of.py → agent_id_patch_request_synthesizer_voice_config_one_of.py} +4 -4
  36. smallestai/atoms/models/{update_agent_request_synthesizer_voice_config_one_of1.py → agent_id_patch_request_synthesizer_voice_config_one_of1.py} +4 -4
  37. smallestai/atoms/models/{get_campaign_by_id200_response.py → agent_id_workflow_get200_response.py} +7 -7
  38. smallestai/atoms/models/agent_id_workflow_get200_response_data.py +105 -0
  39. smallestai/atoms/models/agent_id_workflow_get200_response_data_edges_inner.py +127 -0
  40. smallestai/atoms/models/agent_id_workflow_get200_response_data_edges_inner_data.py +91 -0
  41. smallestai/atoms/models/agent_id_workflow_get200_response_data_edges_inner_marker_end.py +91 -0
  42. smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner.py +114 -0
  43. smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_data.py +115 -0
  44. smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_data_variables.py +97 -0
  45. smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_data_variables_data_inner.py +91 -0
  46. smallestai/atoms/models/agent_id_workflow_get200_response_data_nodes_inner_position.py +89 -0
  47. smallestai/atoms/models/agent_id_workflow_get404_response.py +89 -0
  48. smallestai/atoms/models/agent_template_get200_response.py +97 -0
  49. smallestai/atoms/models/{get_agent_templates200_response_data_inner.py → agent_template_get200_response_data_inner.py} +6 -6
  50. smallestai/atoms/models/{get_campaigns200_response.py → audience_get200_response.py} +7 -7
  51. smallestai/atoms/models/{create_campaign201_response_data.py → audience_get200_response_data_inner.py} +16 -18
  52. smallestai/atoms/models/audience_id_delete200_response.py +89 -0
  53. smallestai/atoms/models/audience_id_delete400_response.py +89 -0
  54. smallestai/atoms/models/{get_current_user200_response.py → audience_id_get200_response.py} +7 -7
  55. smallestai/atoms/models/audience_id_get400_response.py +89 -0
  56. smallestai/atoms/models/audience_id_get403_response.py +89 -0
  57. smallestai/atoms/models/audience_id_get404_response.py +89 -0
  58. smallestai/atoms/models/audience_id_members_delete200_response.py +93 -0
  59. smallestai/atoms/models/audience_id_members_delete200_response_data.py +87 -0
  60. smallestai/atoms/models/audience_id_members_delete_request.py +87 -0
  61. smallestai/atoms/models/audience_id_members_get200_response.py +93 -0
  62. smallestai/atoms/models/audience_id_members_get200_response_data.py +101 -0
  63. smallestai/atoms/models/{get_campaigns200_response_data_inner_audience.py → audience_id_members_get200_response_data_members_inner.py} +8 -8
  64. smallestai/atoms/models/audience_id_members_get400_response.py +89 -0
  65. smallestai/atoms/models/audience_id_members_get500_response.py +89 -0
  66. smallestai/atoms/models/audience_id_members_post200_response.py +97 -0
  67. smallestai/atoms/models/audience_id_members_post200_response_data_inner.py +93 -0
  68. smallestai/atoms/models/audience_id_members_post200_response_data_inner_data.py +89 -0
  69. smallestai/atoms/models/audience_id_members_post400_response.py +89 -0
  70. smallestai/atoms/models/audience_id_members_post403_response.py +89 -0
  71. smallestai/atoms/models/audience_id_members_post_request.py +87 -0
  72. smallestai/atoms/models/audience_id_members_search_get200_response.py +93 -0
  73. smallestai/atoms/models/audience_id_members_search_get200_response_data.py +101 -0
  74. smallestai/atoms/models/audience_id_members_search_get200_response_data_search_info.py +103 -0
  75. smallestai/atoms/models/audience_id_members_search_get400_response.py +89 -0
  76. smallestai/atoms/models/audience_id_members_search_get500_response.py +89 -0
  77. smallestai/atoms/models/{create_campaign201_response.py → audience_post200_response.py} +7 -7
  78. smallestai/atoms/models/audience_post200_response_data.py +104 -0
  79. smallestai/atoms/models/audience_post400_response.py +89 -0
  80. smallestai/atoms/models/campaign_get200_response.py +93 -0
  81. smallestai/atoms/models/campaign_get200_response_data.py +87 -0
  82. smallestai/atoms/models/{get_campaigns_request.py → campaign_get_request.py} +4 -4
  83. smallestai/atoms/models/campaign_id_get200_response.py +93 -0
  84. smallestai/atoms/models/{get_campaign_by_id200_response_data.py → campaign_id_get200_response_data.py} +4 -4
  85. smallestai/atoms/models/campaign_post201_response.py +89 -0
  86. smallestai/atoms/models/{create_campaign_request.py → campaign_post_request.py} +4 -4
  87. smallestai/atoms/models/{start_outbound_call200_response.py → conversation_id_get200_response.py} +7 -7
  88. smallestai/atoms/models/{get_conversation_logs200_response_data.py → conversation_id_get200_response_data.py} +4 -4
  89. smallestai/atoms/models/conversation_outbound_post200_response.py +93 -0
  90. smallestai/atoms/models/{start_outbound_call200_response_data.py → conversation_outbound_post200_response_data.py} +4 -4
  91. smallestai/atoms/models/{start_outbound_call_request.py → conversation_outbound_post_request.py} +4 -4
  92. smallestai/atoms/models/create_agent_request.py +10 -6
  93. smallestai/atoms/models/create_agent_request_language.py +11 -7
  94. smallestai/atoms/models/create_agent_request_language_synthesizer_voice_config.py +24 -24
  95. smallestai/atoms/models/{knowledge_base_dto.py → knowledge_base.py} +15 -8
  96. smallestai/atoms/models/{knowledge_base_item_dto.py → knowledge_base_item.py} +19 -17
  97. smallestai/atoms/models/{get_knowledge_bases200_response.py → knowledgebase_get200_response.py} +7 -7
  98. smallestai/atoms/models/{get_knowledge_base_by_id200_response.py → knowledgebase_id_get200_response.py} +7 -7
  99. smallestai/atoms/models/{get_knowledge_base_items200_response.py → knowledgebase_id_items_get200_response.py} +7 -7
  100. smallestai/atoms/models/{upload_text_to_knowledge_base_request.py → knowledgebase_id_items_upload_text_post_request.py} +4 -4
  101. smallestai/atoms/models/{create_knowledge_base201_response.py → knowledgebase_post201_response.py} +4 -4
  102. smallestai/atoms/models/{create_knowledge_base_request.py → knowledgebase_post_request.py} +4 -4
  103. smallestai/atoms/models/{get_organization200_response.py → organization_get200_response.py} +7 -7
  104. smallestai/atoms/models/{get_organization200_response_data.py → organization_get200_response_data.py} +10 -10
  105. smallestai/atoms/models/{get_organization200_response_data_members_inner.py → organization_get200_response_data_members_inner.py} +4 -4
  106. smallestai/atoms/models/{get_organization200_response_data_subscription.py → organization_get200_response_data_subscription.py} +4 -4
  107. smallestai/atoms/models/product_phone_numbers_get200_response.py +97 -0
  108. smallestai/atoms/models/product_phone_numbers_get200_response_data_inner.py +100 -0
  109. smallestai/atoms/models/product_phone_numbers_get200_response_data_inner_attributes.py +89 -0
  110. smallestai/atoms/models/user_get200_response.py +93 -0
  111. smallestai/atoms/models/{get_current_user200_response_data.py → user_get200_response_data.py} +4 -4
  112. smallestai/atoms/models/webhook.py +124 -0
  113. smallestai/atoms/models/{get_campaigns200_response_data_inner_agent.py → webhook_agent.py} +8 -6
  114. smallestai/atoms/models/webhook_event.py +98 -0
  115. smallestai/atoms/models/webhook_get200_response.py +93 -0
  116. smallestai/atoms/models/webhook_get200_response_data.py +140 -0
  117. smallestai/atoms/models/webhook_id_delete404_response.py +89 -0
  118. smallestai/atoms/models/webhook_post201_response.py +89 -0
  119. smallestai/atoms/models/webhook_post_request.py +99 -0
  120. smallestai/atoms/models/webhook_post_request_events_inner.py +99 -0
  121. smallestai/atoms/models/webhook_subscription.py +108 -0
  122. smallestai/atoms/models/webhook_subscription_populated.py +112 -0
  123. smallestai/waves/async_waves_client.py +63 -10
  124. smallestai/waves/exceptions.py +4 -4
  125. smallestai/waves/models.py +8 -0
  126. smallestai/waves/utils.py +19 -1
  127. smallestai/waves/waves_client.py +47 -10
  128. {smallestai-4.0.0.dist-info → smallestai-4.1.0.dist-info}/METADATA +2 -2
  129. smallestai-4.1.0.dist-info/RECORD +147 -0
  130. smallestai/atoms/models/get_campaigns200_response_data_inner.py +0 -118
  131. smallestai/atoms/models/get_conversation_logs200_response.py +0 -93
  132. smallestai-4.0.0.dist-info/RECORD +0 -87
  133. {smallestai-4.0.0.dist-info → smallestai-4.1.0.dist-info}/WHEEL +0 -0
  134. {smallestai-4.0.0.dist-info → smallestai-4.1.0.dist-info}/licenses/LICENSE +0 -0
  135. {smallestai-4.0.0.dist-info → smallestai-4.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,99 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ Agent Management API
5
+
6
+ API for managing agents, their templates, and call logs
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ import pprint
17
+ import re # noqa: F401
18
+ import json
19
+
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator
21
+ from typing import Any, ClassVar, Dict, List, Optional
22
+ from typing import Optional, Set
23
+ from typing_extensions import Self
24
+
25
+ class WebhookPostRequestEventsInner(BaseModel):
26
+ """
27
+ WebhookPostRequestEventsInner
28
+ """ # noqa: E501
29
+ agent_id: Optional[StrictStr] = Field(default=None, description="The ID of the agent", alias="agentId")
30
+ event_type: Optional[StrictStr] = Field(default=None, description="The type of event to subscribe to", alias="eventType")
31
+ __properties: ClassVar[List[str]] = ["agentId", "eventType"]
32
+
33
+ @field_validator('event_type')
34
+ def event_type_validate_enum(cls, value):
35
+ """Validates the enum"""
36
+ if value is None:
37
+ return value
38
+
39
+ if value not in set(['pre-conversation', 'post-conversation']):
40
+ raise ValueError("must be one of enum values ('pre-conversation', 'post-conversation')")
41
+ return value
42
+
43
+ model_config = ConfigDict(
44
+ populate_by_name=True,
45
+ validate_assignment=True,
46
+ protected_namespaces=(),
47
+ )
48
+
49
+
50
+ def to_str(self) -> str:
51
+ """Returns the string representation of the model using alias"""
52
+ return pprint.pformat(self.model_dump(by_alias=True))
53
+
54
+ def to_json(self) -> str:
55
+ """Returns the JSON representation of the model using alias"""
56
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
57
+ return json.dumps(self.to_dict())
58
+
59
+ @classmethod
60
+ def from_json(cls, json_str: str) -> Optional[Self]:
61
+ """Create an instance of WebhookPostRequestEventsInner from a JSON string"""
62
+ return cls.from_dict(json.loads(json_str))
63
+
64
+ def to_dict(self) -> Dict[str, Any]:
65
+ """Return the dictionary representation of the model using alias.
66
+
67
+ This has the following differences from calling pydantic's
68
+ `self.model_dump(by_alias=True)`:
69
+
70
+ * `None` is only added to the output dict for nullable fields that
71
+ were set at model initialization. Other fields with value `None`
72
+ are ignored.
73
+ """
74
+ excluded_fields: Set[str] = set([
75
+ ])
76
+
77
+ _dict = self.model_dump(
78
+ by_alias=True,
79
+ exclude=excluded_fields,
80
+ exclude_none=True,
81
+ )
82
+ return _dict
83
+
84
+ @classmethod
85
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
86
+ """Create an instance of WebhookPostRequestEventsInner from a dict"""
87
+ if obj is None:
88
+ return None
89
+
90
+ if not isinstance(obj, dict):
91
+ return cls.model_validate(obj)
92
+
93
+ _obj = cls.model_validate({
94
+ "agentId": obj.get("agentId"),
95
+ "eventType": obj.get("eventType")
96
+ })
97
+ return _obj
98
+
99
+
@@ -0,0 +1,108 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ Agent Management API
5
+
6
+ API for managing agents, their templates, and call logs
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ import pprint
17
+ import re # noqa: F401
18
+ import json
19
+
20
+ from datetime import datetime
21
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator
22
+ from typing import Any, ClassVar, Dict, List, Optional
23
+ from typing import Optional, Set
24
+ from typing_extensions import Self
25
+
26
+ class WebhookSubscription(BaseModel):
27
+ """
28
+ WebhookSubscription
29
+ """ # noqa: E501
30
+ id: Optional[StrictStr] = Field(default=None, description="The unique identifier for the subscription", alias="_id")
31
+ webhook_id: Optional[StrictStr] = Field(default=None, description="The ID of the webhook", alias="webhookId")
32
+ agent_id: Optional[StrictStr] = Field(default=None, description="The ID of the agent", alias="agentId")
33
+ event_type: Optional[StrictStr] = Field(default=None, description="The type of event subscribed to", alias="eventType")
34
+ created_at: Optional[datetime] = Field(default=None, description="The date and time when the subscription was created", alias="createdAt")
35
+ updated_at: Optional[datetime] = Field(default=None, description="The date and time when the subscription was last updated", alias="updatedAt")
36
+ __properties: ClassVar[List[str]] = ["_id", "webhookId", "agentId", "eventType", "createdAt", "updatedAt"]
37
+
38
+ @field_validator('event_type')
39
+ def event_type_validate_enum(cls, value):
40
+ """Validates the enum"""
41
+ if value is None:
42
+ return value
43
+
44
+ if value not in set(['pre-conversation', 'post-conversation']):
45
+ raise ValueError("must be one of enum values ('pre-conversation', 'post-conversation')")
46
+ return value
47
+
48
+ model_config = ConfigDict(
49
+ populate_by_name=True,
50
+ validate_assignment=True,
51
+ protected_namespaces=(),
52
+ )
53
+
54
+
55
+ def to_str(self) -> str:
56
+ """Returns the string representation of the model using alias"""
57
+ return pprint.pformat(self.model_dump(by_alias=True))
58
+
59
+ def to_json(self) -> str:
60
+ """Returns the JSON representation of the model using alias"""
61
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
62
+ return json.dumps(self.to_dict())
63
+
64
+ @classmethod
65
+ def from_json(cls, json_str: str) -> Optional[Self]:
66
+ """Create an instance of WebhookSubscription from a JSON string"""
67
+ return cls.from_dict(json.loads(json_str))
68
+
69
+ def to_dict(self) -> Dict[str, Any]:
70
+ """Return the dictionary representation of the model using alias.
71
+
72
+ This has the following differences from calling pydantic's
73
+ `self.model_dump(by_alias=True)`:
74
+
75
+ * `None` is only added to the output dict for nullable fields that
76
+ were set at model initialization. Other fields with value `None`
77
+ are ignored.
78
+ """
79
+ excluded_fields: Set[str] = set([
80
+ ])
81
+
82
+ _dict = self.model_dump(
83
+ by_alias=True,
84
+ exclude=excluded_fields,
85
+ exclude_none=True,
86
+ )
87
+ return _dict
88
+
89
+ @classmethod
90
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
91
+ """Create an instance of WebhookSubscription from a dict"""
92
+ if obj is None:
93
+ return None
94
+
95
+ if not isinstance(obj, dict):
96
+ return cls.model_validate(obj)
97
+
98
+ _obj = cls.model_validate({
99
+ "_id": obj.get("_id"),
100
+ "webhookId": obj.get("webhookId"),
101
+ "agentId": obj.get("agentId"),
102
+ "eventType": obj.get("eventType"),
103
+ "createdAt": obj.get("createdAt"),
104
+ "updatedAt": obj.get("updatedAt")
105
+ })
106
+ return _obj
107
+
108
+
@@ -0,0 +1,112 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ Agent Management API
5
+
6
+ API for managing agents, their templates, and call logs
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ import pprint
17
+ import re # noqa: F401
18
+ import json
19
+
20
+ from datetime import datetime
21
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator
22
+ from typing import Any, ClassVar, Dict, List, Optional
23
+ from smallestai.atoms.models.webhook_agent import WebhookAgent
24
+ from typing import Optional, Set
25
+ from typing_extensions import Self
26
+
27
+ class WebhookSubscriptionPopulated(BaseModel):
28
+ """
29
+ WebhookSubscriptionPopulated
30
+ """ # noqa: E501
31
+ id: Optional[StrictStr] = Field(default=None, description="The unique identifier for the subscription", alias="_id")
32
+ webhook_id: Optional[StrictStr] = Field(default=None, description="The ID of the webhook", alias="webhookId")
33
+ agent_id: Optional[WebhookAgent] = Field(default=None, alias="agentId")
34
+ event_type: Optional[StrictStr] = Field(default=None, description="The type of event subscribed to", alias="eventType")
35
+ created_at: Optional[datetime] = Field(default=None, description="The date and time when the subscription was created", alias="createdAt")
36
+ updated_at: Optional[datetime] = Field(default=None, description="The date and time when the subscription was last updated", alias="updatedAt")
37
+ __properties: ClassVar[List[str]] = ["_id", "webhookId", "agentId", "eventType", "createdAt", "updatedAt"]
38
+
39
+ @field_validator('event_type')
40
+ def event_type_validate_enum(cls, value):
41
+ """Validates the enum"""
42
+ if value is None:
43
+ return value
44
+
45
+ if value not in set(['pre-conversation', 'post-conversation']):
46
+ raise ValueError("must be one of enum values ('pre-conversation', 'post-conversation')")
47
+ return value
48
+
49
+ model_config = ConfigDict(
50
+ populate_by_name=True,
51
+ validate_assignment=True,
52
+ protected_namespaces=(),
53
+ )
54
+
55
+
56
+ def to_str(self) -> str:
57
+ """Returns the string representation of the model using alias"""
58
+ return pprint.pformat(self.model_dump(by_alias=True))
59
+
60
+ def to_json(self) -> str:
61
+ """Returns the JSON representation of the model using alias"""
62
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
63
+ return json.dumps(self.to_dict())
64
+
65
+ @classmethod
66
+ def from_json(cls, json_str: str) -> Optional[Self]:
67
+ """Create an instance of WebhookSubscriptionPopulated from a JSON string"""
68
+ return cls.from_dict(json.loads(json_str))
69
+
70
+ def to_dict(self) -> Dict[str, Any]:
71
+ """Return the dictionary representation of the model using alias.
72
+
73
+ This has the following differences from calling pydantic's
74
+ `self.model_dump(by_alias=True)`:
75
+
76
+ * `None` is only added to the output dict for nullable fields that
77
+ were set at model initialization. Other fields with value `None`
78
+ are ignored.
79
+ """
80
+ excluded_fields: Set[str] = set([
81
+ ])
82
+
83
+ _dict = self.model_dump(
84
+ by_alias=True,
85
+ exclude=excluded_fields,
86
+ exclude_none=True,
87
+ )
88
+ # override the default output from pydantic by calling `to_dict()` of agent_id
89
+ if self.agent_id:
90
+ _dict['agentId'] = self.agent_id.to_dict()
91
+ return _dict
92
+
93
+ @classmethod
94
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
95
+ """Create an instance of WebhookSubscriptionPopulated from a dict"""
96
+ if obj is None:
97
+ return None
98
+
99
+ if not isinstance(obj, dict):
100
+ return cls.model_validate(obj)
101
+
102
+ _obj = cls.model_validate({
103
+ "_id": obj.get("_id"),
104
+ "webhookId": obj.get("webhookId"),
105
+ "agentId": WebhookAgent.from_dict(obj["agentId"]) if obj.get("agentId") is not None else None,
106
+ "eventType": obj.get("eventType"),
107
+ "createdAt": obj.get("createdAt"),
108
+ "updatedAt": obj.get("updatedAt")
109
+ })
110
+ return _obj
111
+
112
+
@@ -6,8 +6,8 @@ import aiofiles
6
6
  import requests
7
7
  from typing import Optional, Union, List
8
8
 
9
- from smallestai.waves.exceptions import TTSError, APIError
10
- from smallestai.waves.utils import (TTSOptions, validate_input,
9
+ from smallestai.waves.exceptions import InvalidError, APIError
10
+ from smallestai.waves.utils import (TTSOptions, validate_input, validate_asr_input,
11
11
  get_smallest_languages, get_smallest_models, ALLOWED_AUDIO_EXTENSIONS, API_BASE_URL)
12
12
 
13
13
 
@@ -52,7 +52,7 @@ class AsyncWavesClient:
52
52
  """
53
53
  self.api_key = api_key or os.environ.get("SMALLEST_API_KEY")
54
54
  if not self.api_key:
55
- raise TTSError()
55
+ raise InvalidError()
56
56
  if model == "lightning-large" and voice_id is None:
57
57
  voice_id = "lakshya"
58
58
 
@@ -150,7 +150,7 @@ class AsyncWavesClient:
150
150
  - Otherwise, returns the synthesized audio content as bytes.
151
151
 
152
152
  Raises:
153
- - TTSError: If the provided file name does not have a .wav or .mp3 extension when `save_as` is specified.
153
+ - InvalidError: If the provided file name does not have a .wav or .mp3 extension when `save_as` is specified.
154
154
  - APIError: If the API request fails or returns an error.
155
155
  - ValueError: If an unexpected parameter is passed in `kwargs`.
156
156
  """
@@ -223,17 +223,17 @@ class AsyncWavesClient:
223
223
  - str: The response from the API as a formatted JSON string.
224
224
 
225
225
  Raises:
226
- - TTSError: If the file does not exist or is not a valid audio file.
226
+ - InvalidError: If the file does not exist or is not a valid audio file.
227
227
  - APIError: If the API request fails or returns an error.
228
228
  """
229
229
  url = f"{API_BASE_URL}/lightning-large/add_voice"
230
230
 
231
231
  if not os.path.exists(file_path):
232
- raise TTSError("Invalid file path. File does not exist.")
232
+ raise InvalidError("Invalid file path. File does not exist.")
233
233
 
234
234
  file_extension = os.path.splitext(file_path)[1].lower()
235
235
  if file_extension not in ALLOWED_AUDIO_EXTENSIONS:
236
- raise TTSError(f"Invalid file type. Supported formats are: {ALLOWED_AUDIO_EXTENSIONS}")
236
+ raise InvalidError(f"Invalid file type. Supported formats are: {ALLOWED_AUDIO_EXTENSIONS}")
237
237
 
238
238
  headers = {
239
239
  'Authorization': f"Bearer {self.api_key}",
@@ -255,7 +255,7 @@ class AsyncWavesClient:
255
255
  if res.status != 200:
256
256
  raise APIError(f"Failed to add voice: {await res.text()}. For more information, visit https://waves.smallest.ai/")
257
257
 
258
- return json.dumps(await res.json(), indent=4, ensure_ascii=False)
258
+ return await res.json()
259
259
 
260
260
  finally:
261
261
  if should_cleanup and self.session:
@@ -290,8 +290,61 @@ class AsyncWavesClient:
290
290
  if res.status != 200:
291
291
  raise APIError(f"Failed to delete voice: {await res.text()}. For more information, visit https://waves.smallest.ai/")
292
292
 
293
- return json.dumps(await res.json(), indent=4, ensure_ascii=False)
293
+ return await res.json()
294
294
  finally:
295
295
  if should_cleanup and self.session:
296
296
  await self.session.close()
297
- self.session = None
297
+ self.session = None
298
+
299
+ async def transcribe(
300
+ self,
301
+ file_path: str,
302
+ language: Optional[str] = "en",
303
+ word_timestamps: Optional[bool] = False,
304
+ age_detection: Optional[bool] = False,
305
+ gender_detection: Optional[bool] = False,
306
+ emotion_detection: Optional[bool] = False,
307
+ model: Optional[str] = "lightning"
308
+ ) -> dict:
309
+ validate_asr_input(file_path, model, language)
310
+
311
+ url = f"{API_BASE_URL}/speech-to-text"
312
+ headers = {
313
+ 'Authorization': f"Bearer {self.api_key}",
314
+ }
315
+
316
+ should_cleanup = await self._ensure_session()
317
+
318
+ try:
319
+ file_extension = os.path.splitext(file_path)[1].lower()
320
+ content_type = f"audio/{file_extension[1:]}" if file_extension else "application/octet-stream"
321
+
322
+ async with aiofiles.open(file_path, 'rb') as f:
323
+ file_data = await f.read()
324
+
325
+ form = aiohttp.FormData()
326
+ form.add_field(
327
+ 'file',
328
+ file_data,
329
+ filename=os.path.basename(file_path),
330
+ content_type=content_type
331
+ )
332
+ # Send options as multipart form fields (not query params)
333
+ form.add_field('model', model)
334
+ form.add_field('language', language)
335
+ form.add_field('word_timestamps', str(bool(word_timestamps)).lower())
336
+ form.add_field('age_detection', str(bool(age_detection)).lower())
337
+ form.add_field('gender_detection', str(bool(gender_detection)).lower())
338
+ form.add_field('emotion_detection', str(bool(emotion_detection)).lower())
339
+
340
+ async with self.session.post(url, headers=headers, data=form) as res:
341
+ if res.status != 200:
342
+ raise APIError(
343
+ f"Failed to transcribe audio: {await res.text()}. "
344
+ "For more information, visit https://waves-docs.smallest.ai/v4.0.0/content/api-references/asr-post-api"
345
+ )
346
+ return await res.json()
347
+ finally:
348
+ if should_cleanup and self.session:
349
+ await self.session.close()
350
+ self.session = None
@@ -1,18 +1,18 @@
1
- class TTSError(Exception):
1
+ class InvalidError(Exception):
2
2
  """Base exception for TTS SDK"""
3
3
  default_message = "API key is required. Please set the `SMALLEST_API_KEY` environment variable or visit https://waves.smallest.ai/ to obtain your API key."
4
4
 
5
5
  def __init__(self, message=None):
6
6
  super().__init__(message or self.default_message)
7
7
 
8
- class APIError(TTSError):
8
+ class APIError(InvalidError):
9
9
  """Raised when the API returns an error"""
10
10
  pass
11
11
 
12
- class ValidationError(TTSError):
12
+ class ValidationError(InvalidError):
13
13
  """Raised when input validation fails"""
14
14
  pass
15
15
 
16
- class AuthenticationError(TTSError):
16
+ class AuthenticationError(InvalidError):
17
17
  """Raised when authentication fails"""
18
18
  pass
@@ -6,3 +6,11 @@ TTSModels = [
6
6
  "lightning-large",
7
7
  "lightning-v2"
8
8
  ]
9
+ ASRLanguages_lightning = [
10
+ "it", "es", "en", "pt", "hi", "de", "fr", "uk", "ru", "kn", "ml", "pl",
11
+ "mr", "gu", "cs", "sk", "te", "or", "nl", "bn", "lv", "et", "ro", "pa",
12
+ "fi", "sv", "bg", "ta", "hu", "da", "lt", "mt", "multi"
13
+ ]
14
+ ASRModels = [
15
+ "lightning"
16
+ ]
smallestai/waves/utils.py CHANGED
@@ -1,9 +1,10 @@
1
+ import os
1
2
  from typing import List
2
3
  from typing import Optional
3
4
  from dataclasses import dataclass
4
5
 
5
6
  from smallestai.waves.exceptions import ValidationError
6
- from smallestai.waves.models import TTSModels, TTSLanguages_lightning, TTSLanguages_lightning_large, TTSLanguages_lightning_v2
7
+ from smallestai.waves.models import TTSModels, TTSLanguages_lightning, TTSLanguages_lightning_large, TTSLanguages_lightning_v2, ASRModels, ASRLanguages_lightning
7
8
 
8
9
 
9
10
  API_BASE_URL = "https://waves-api.smallest.ai/api/v1"
@@ -25,7 +26,24 @@ class TTSOptions:
25
26
  enhancement: int
26
27
  language: str
27
28
  output_format: str
29
+
30
+ @dataclass
31
+ class ASROptions:
32
+ model: str
33
+ api_key: str
34
+ language: str
35
+ word_timestamps: bool
36
+ age_detection: bool
37
+ gender_detection: bool
38
+ emotion_detection: bool
28
39
 
40
+ def validate_asr_input(file_path: str, model: str, language: str):
41
+ if not os.path.isfile(file_path):
42
+ raise ValidationError("Invalid file path. File does not exist.")
43
+ if model not in ASRModels:
44
+ raise ValidationError(f"Invalid model: {model}. Must be one of {ASRModels}")
45
+ if language not in ASRLanguages_lightning:
46
+ raise ValidationError(f"Invalid language: {language}. Must be one of {ASRLanguages_lightning}")
29
47
 
30
48
  def validate_input(text: str, model: str, sample_rate: int, speed: float, consistency: Optional[float] = None, similarity: Optional[float] = None, enhancement: Optional[int] = None):
31
49
  if not text:
@@ -4,8 +4,8 @@ import copy
4
4
  import requests
5
5
  from typing import Optional, Union, List
6
6
 
7
- from smallestai.waves.exceptions import TTSError, APIError
8
- from smallestai.waves.utils import (TTSOptions, validate_input,
7
+ from smallestai.waves.exceptions import InvalidError, APIError
8
+ from smallestai.waves.utils import (TTSOptions, validate_input, validate_asr_input,
9
9
  get_smallest_languages, get_smallest_models, ALLOWED_AUDIO_EXTENSIONS, API_BASE_URL)
10
10
 
11
11
  class WavesClient:
@@ -48,7 +48,7 @@ class WavesClient:
48
48
  """
49
49
  self.api_key = api_key or os.environ.get("SMALLEST_API_KEY")
50
50
  if not self.api_key:
51
- raise TTSError()
51
+ raise InvalidError()
52
52
  if model == "lightning-large" and voice_id is None:
53
53
  voice_id = "lakshya"
54
54
 
@@ -125,7 +125,7 @@ class WavesClient:
125
125
  - Otherwise, returns the synthesized audio content as bytes.
126
126
 
127
127
  Raises:
128
- - TTSError: If the provided file name does not have a .wav or .mp3 extension when `save_as` is specified.
128
+ - InvalidError: If the provided file name does not have a .wav or .mp3 extension when `save_as` is specified.
129
129
  - APIError: If the API request fails or returns an error.
130
130
  """
131
131
  opts = copy.deepcopy(self.opts)
@@ -184,15 +184,15 @@ class WavesClient:
184
184
  - str: The response from the API as a formatted JSON string.
185
185
 
186
186
  Raises:
187
- - TTSError: If the file does not exist or is not a valid audio file.
187
+ - InvalidError: If the file does not exist or is not a valid audio file.
188
188
  - APIError: If the API request fails or returns an error.
189
189
  """
190
190
  if not os.path.isfile(file_path):
191
- raise TTSError("Invalid file path. File does not exist.")
191
+ raise InvalidError("Invalid file path. File does not exist.")
192
192
 
193
193
  file_extension = os.path.splitext(file_path)[1].lower()
194
194
  if file_extension not in ALLOWED_AUDIO_EXTENSIONS:
195
- raise TTSError(f"Invalid file type. Supported formats are: {ALLOWED_AUDIO_EXTENSIONS}")
195
+ raise InvalidError(f"Invalid file type. Supported formats are: {ALLOWED_AUDIO_EXTENSIONS}")
196
196
 
197
197
  url = f"{API_BASE_URL}/lightning-large/add_voice"
198
198
  payload = {'displayName': display_name}
@@ -206,8 +206,8 @@ class WavesClient:
206
206
  response = requests.post(url, headers=headers, data=payload, files=files)
207
207
  if response.status_code != 200:
208
208
  raise APIError(f"Failed to add voice: {response.text}. For more information, visit https://waves.smallest.ai/")
209
-
210
- return json.dumps(response.json(), indent=4, ensure_ascii=False)
209
+
210
+ return response.json()
211
211
 
212
212
 
213
213
  def delete_voice(self, voice_id: str) -> str:
@@ -234,4 +234,41 @@ class WavesClient:
234
234
  if response.status_code != 200:
235
235
  raise APIError(f"Failed to delete voice: {response.text}. For more information, visit https://waves.smallest.ai/")
236
236
 
237
- return json.dumps(response.json(), indent=4, ensure_ascii=False)
237
+ return response.json()
238
+
239
+ def transcribe(
240
+ self,
241
+ file_path: str,
242
+ language: Optional[str] = "en",
243
+ word_timestamps: Optional[bool] = False,
244
+ age_detection: Optional[bool] = False,
245
+ gender_detection: Optional[bool] = False,
246
+ emotion_detection: Optional[bool] = False,
247
+ model: Optional[str] = "lightning"
248
+ ) -> dict:
249
+ validate_asr_input(file_path, model, language)
250
+
251
+ url = f"{API_BASE_URL}/speech-to-text"
252
+ headers = {
253
+ 'Authorization': f"Bearer {self.api_key}",
254
+ }
255
+ payload = {
256
+ 'model': model,
257
+ 'language': language,
258
+ 'word_timestamps': str(bool(word_timestamps)).lower(),
259
+ 'age_detection': str(bool(age_detection)).lower(),
260
+ 'gender_detection': str(bool(gender_detection)).lower(),
261
+ 'emotion_detection': str(bool(emotion_detection)).lower()
262
+ }
263
+
264
+ file_extension = os.path.splitext(file_path)[1].lower()
265
+ content_type = f"audio/{file_extension[1:]}" if file_extension else "application/octet-stream"
266
+
267
+ with open(file_path, 'rb') as f:
268
+ files = {'file': (os.path.basename(file_path), f, content_type)}
269
+ response = requests.post(url, headers=headers, files=files, data=payload)
270
+
271
+ if response.status_code != 200:
272
+ raise APIError(f"Failed to transcribe audio: {response.text}. For more information, visit https://waves-docs.smallest.ai/v4.0.0/content/api-references/asr-post-api")
273
+
274
+ return response.json()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smallestai
3
- Version: 4.0.0
3
+ Version: 4.1.0
4
4
  Summary: Official Python client for the Smallest AI API
5
5
  Author-email: Smallest <support@smallest.ai>
6
6
  License: MIT
@@ -142,7 +142,7 @@ def main():
142
142
  "similarity": 0,
143
143
  "enhancement": 1
144
144
  },
145
- "slmModel": "electron-v1",
145
+ "slmModel": "electron",
146
146
  }
147
147
  ).data
148
148