From 2711e014fd4e6e01d8e0569d413a33048008f2a0 Mon Sep 17 00:00:00 2001
From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com>
Date: Fri, 24 Jul 2026 03:08:04 +0000
Subject: [PATCH 1/3] [fern-generated] Update SDK
Generated by Fern
CLI Version: unknown
Generators:
- fernapi/fern-python-sdk: 5.12.12
---
.fern/metadata.json | 8 +-
reference.md | 5744 +++++++++++++----
src/smallestai/.fern/metadata.json | 18 -
src/smallestai/__init__.py | 3 +
.../atoms/agent_templates/__init__.py | 66 +
.../atoms/agent_templates/types/__init__.py | 80 +
.../agent_versioning_branches/__init__.py | 94 +
.../atoms/agent_versioning_branches/client.py | 1379 ++++
.../agent_versioning_branches/raw_client.py | 3101 +++++++++
.../types/__init__.py | 92 +
...hive_agent_versioning_branches_response.py | 22 +
...gent_versioning_branches_response_data.py} | 4 +-
...lish_agent_versioning_branches_response.py | 22 +
...gent_versioning_branches_response_data.py} | 4 +-
...anch_agent_versioning_branches_response.py | 22 +
...raft_agent_versioning_branches_response.py | 22 +
...gent_versioning_branches_response_data.py} | 5 +-
...get_agent_versioning_branches_response.py} | 6 +-
...aft_agent_versioning_branches_response.py} | 6 +-
...ist_agent_versioning_branches_response.py} | 6 +-
...agent_versioning_branches_response_data.py | 21 +
...ive_agent_versioning_branches_response.py} | 6 +-
...raft_agent_versioning_branches_response.py | 22 +
...ame_agent_versioning_branches_response.py} | 5 +-
...call_agent_versioning_branches_response.py | 22 +
.../types/test_call_v2request_mode.py | 5 +
...e_branch_draft_request_background_sound.py | 7 +
.../update_branch_draft_request_slm_model.py | 17 +
...aft_agent_versioning_branches_response.py} | 6 +-
.../atoms/agent_versioning_drafts/__init__.py | 84 +
.../atoms/agent_versioning_drafts/client.py | 112 +-
.../agent_versioning_drafts/raw_client.py | 209 +-
.../agent_versioning_drafts/types/__init__.py | 84 +
..._agent_id_drafts_draft_id_diff_response.py | 6 +-
...t_id_drafts_draft_id_diff_response_data.py | 36 +
...ft_id_diff_response_data_sections_value.py | 33 +
.../agent_versioning_revisions/__init__.py | 64 +
.../agent_versioning_revisions/client.py | 510 ++
.../agent_versioning_revisions/raw_client.py | 1164 ++++
.../types/__init__.py | 64 +
...ff_agent_versioning_revisions_response.py} | 6 +-
...get_agent_versioning_revisions_response.py | 22 +
...gent_versioning_revisions_response_data.py | 28 +
...ory_agent_versioning_revisions_response.py | 22 +
...gent_versioning_revisions_response_data.py | 30 +
...ning_revisions_response_data_trail_item.py | 28 +
...st_agent_versioning_revisions_response.py} | 6 +-
...gent_versioning_revisions_response_data.py | 22 +
...ore_agent_versioning_revisions_response.py | 22 +
.../agent_versioning_versions/__init__.py | 63 +
.../atoms/agent_versioning_versions/client.py | 231 +-
.../agent_versioning_versions/raw_client.py | 377 +-
.../types/__init__.py | 65 +
...rics_agent_versioning_versions_response.py | 22 -
...nt_id_versions_compare_metrics_response.py | 22 -
...d_versions_version_id_activate_response.py | 22 -
src/smallestai/atoms/agents/__init__.py | 189 +
src/smallestai/atoms/agents/client.py | 957 +--
src/smallestai/atoms/agents/raw_client.py | 1711 ++---
src/smallestai/atoms/agents/types/__init__.py | 191 +
.../types/create_agent_request_synthesizer.py | 9 +-
..._agent_request_synthesizer_voice_config.py | 5 -
..._request_synthesizer_voice_config_model.py | 11 +-
.../create_with_ai_agents_request_type.py | 5 -
...eate_with_ai_agents_request_voice_model.py | 18 -
...get_agent_avatar_presigned_url_response.py | 22 +
...ent_avatar_presigned_url_response_data.py} | 19 +-
.../types/get_agent_call_logs_response.py | 22 +
...y => get_agent_call_logs_response_data.py} | 13 +-
...call_logs_response_data_call_logs_item.py} | 31 +-
.../get_agent_id_workflow_response_data.py | 45 -
...et_agent_id_workflow_response_data_data.py | 8 -
...py => get_agent_widget_config_response.py} | 10 +-
.../get_agent_widget_config_response_data.py | 65 +
..._agent_widget_config_response_data_mode.py | 5 +
...nt_widget_config_response_data_position.py | 7 +
..._agent_widget_config_response_data_size.py | 5 +
...agent_widget_config_response_data_theme.py | 5 +
.../get_prompt_config_agents_response_data.py | 29 -
...fig_agents_response_data_questions_item.py | 32 -
...gents_response_data_questions_item_type.py | 7 -
...ent_widget_config_request_widget_config.py | 119 +
...idget_config_request_widget_config_mode.py | 5 +
...t_config_request_widget_config_position.py | 7 +
...idget_config_request_widget_config_size.py | 5 +
...dget_config_request_widget_config_theme.py | 5 +
.../update_agent_widget_config_response.py} | 6 +-
.../update_widget_config_agents_response.py | 22 -
...iguration_agents_request_workflow_graph.py | 25 -
src/smallestai/atoms/analytics/__init__.py | 208 +
src/smallestai/atoms/analytics/client.py | 2163 +++++++
src/smallestai/atoms/analytics/raw_client.py | 3667 +++++++++++
.../atoms/analytics/types/__init__.py | 220 +
...et_agent_performance_request_sort_order.py | 5 +
.../types/get_agent_performance_response.py} | 8 +-
.../get_agent_performance_response_data.py} | 18 +-
...t_performance_response_data_agents_item.py | 41 +
...tics_concurrency_request_include_agents.py | 5 +
.../get_analytics_concurrency_response.py | 22 +
...get_analytics_concurrency_response_data.py | 44 +
...concurrency_response_data_by_agent_item.py | 33 +
...urrency_response_data_data_points_item.py} | 11 +-
.../types/get_analytics_summary_response.py} | 8 +-
.../get_analytics_summary_response_data.py | 59 +
.../types/get_attempt_cohort_response.py | 22 +
.../types/get_attempt_cohort_response_data.py | 37 +
...tempt_cohort_response_data_cohorts_item.py | 42 +
.../types/get_call_counts_by_day_response.py} | 8 +-
.../get_call_counts_by_day_response_data.py} | 18 +-
..._response_data_call_counts_by_day_item.py} | 7 +-
.../types/get_call_counts_log_response.py | 22 +
.../get_call_counts_log_response_data.py | 33 +
...all_counts_log_response_data_calls_item.py | 69 +
.../get_call_outcomes_timeseries_response.py | 22 +
..._call_outcomes_timeseries_response_data.py | 36 +
...eseries_response_data_data_points_item.py} | 16 +-
...tcomes_timeseries_response_data_totals.py} | 15 +-
.../get_call_start_distribution_response.py} | 8 +-
...t_call_start_distribution_response_data.py | 34 +
...ibution_response_data_data_points_item.py} | 12 +-
.../get_call_volume_timeseries_response.py | 22 +
...et_call_volume_timeseries_response_data.py | 32 +
...meseries_response_data_data_points_item.py | 41 +
.../get_conversation_details_response.py | 22 +
.../get_conversation_details_response_data.py | 46 +
...tion_details_response_data_events_item.py} | 15 +-
...n_details_response_data_transcript_item.py | 23 +
.../types/get_daily_call_summary_response.py | 22 +
.../get_daily_call_summary_response_data.py | 47 +
.../types/get_dashboard_response.py} | 8 +-
.../types/get_dashboard_response_data.py | 51 +
.../types/get_duration_stats_response.py | 22 +
.../types/get_duration_stats_response_data.py | 50 +
.../types/get_hourly_performance_response.py | 22 +
.../get_hourly_performance_response_data.py | 28 +
...ly_performance_response_data_hours_item.py | 41 +
.../types/get_phone_number_trends_response.py | 22 +
.../get_phone_number_trends_response_data.py | 28 +
...umber_trends_response_data_trends_item.py} | 18 +-
...ponse_data_trends_item_data_points_item.py | 32 +
.../get_pickup_rate_by_number_response.py} | 8 +-
...get_pickup_rate_by_number_response_data.py | 28 +
...te_by_number_response_data_numbers_item.py | 37 +
.../types/get_usage_timeseries_response.py} | 8 +-
.../get_usage_timeseries_response_data.py | 36 +
...ge_timeseries_response_data_date_range.py} | 8 +-
...ies_response_data_day_wise_credits_item.py | 22 +
.../types/get_weekly_trends_response.py | 22 +
.../types/get_weekly_trends_response_data.py} | 18 +-
..._weekly_trends_response_data_weeks_item.py | 57 +
src/smallestai/atoms/audience/__init__.py | 96 +
.../atoms/audience/types/__init__.py | 100 +
.../types/get_audience_id_response.py | 22 -
.../types/get_audience_id_response_data.py | 64 -
.../types/get_audience_response_data_item.py | 79 -
src/smallestai/atoms/call_actions/__init__.py | 79 +
src/smallestai/atoms/call_actions/client.py | 610 ++
.../atoms/call_actions/raw_client.py | 1079 ++++
.../atoms/call_actions/types/__init__.py | 77 +
.../create_call_action_request_action_type.py | 5 +
.../create_call_action_request_category.py | 5 +
.../create_call_action_request_config.py | 39 +
.../types/create_call_action_response.py} | 4 +-
.../types/delete_call_action_response.py | 22 +
.../delete_call_action_response_data.py} | 4 +-
.../types/get_call_action_response.py} | 5 +-
.../list_call_actions_request_category.py | 5 +
.../types/list_call_actions_response.py | 22 +
.../types/list_call_actions_response_data.py} | 17 +-
.../update_call_action_request_action_type.py | 5 +
.../update_call_action_request_category.py | 5 +
.../update_call_action_request_config.py | 36 +
.../types/update_call_action_response.py | 22 +
src/smallestai/atoms/calls/__init__.py | 150 +
src/smallestai/atoms/calls/types/__init__.py | 168 +
src/smallestai/atoms/campaigns/__init__.py | 108 +
src/smallestai/atoms/campaigns/client.py | 85 -
src/smallestai/atoms/campaigns/raw_client.py | 187 -
.../atoms/campaigns/types/__init__.py | 106 +
.../export_logs_campaigns_response_item.py | 46 -
.../types/get_campaign_id_response.py | 22 -
.../types/get_campaign_id_response_data.py | 32 -
.../get_campaign_id_response_data_campaign.py | 122 -
...mpaign_id_response_data_campaign_status.py | 7 -
...t_campaign_id_response_data_events_item.py | 37 -
.../get_campaign_id_response_data_metrics.py | 37 -
.../types/get_campaign_request_sort_field.py | 5 -
.../types/get_campaign_request_sort_order.py | 5 -
.../types/get_campaign_request_status.py | 7 -
.../campaigns/types/get_campaign_response.py | 22 -
.../types/get_campaign_response_data.py | 32 -
...t_campaign_response_data_campaigns_item.py | 104 -
...ign_response_data_campaigns_item_status.py | 7 -
.../types/post_campaign_response_data.py | 98 -
.../post_campaign_response_data_status.py | 7 -
src/smallestai/atoms/client.py | 157 +
src/smallestai/atoms/compliance/__init__.py | 72 +
.../atoms/compliance/types/__init__.py | 70 +
...liance_applications_request_number_type.py | 5 -
...mpliance_applications_request_user_type.py | 5 -
.../post_compliance_applications_response.py | 22 -
src/smallestai/atoms/concurrency/__init__.py | 55 +
src/smallestai/atoms/concurrency/client.py | 215 +
.../atoms/concurrency/raw_client.py | 365 ++
.../atoms/concurrency/types/__init__.py | 55 +
.../types/get_concurrency_response.py | 22 +
.../types/get_concurrency_response_data.py | 40 +
...t_concurrency_response_data_agents_item.py | 51 +
...reservations_request_reservations_item.py} | 29 +-
...pdate_concurrency_reservations_response.py | 22 +
...concurrency_reservations_response_data.py} | 7 +-
.../atoms/conversations/__init__.py | 57 +
.../atoms/conversations/types/__init__.py | 59 +
.../disposition_metric_templates/__init__.py | 46 +
.../disposition_metric_templates/client.py | 104 +
.../raw_client.py | 151 +
.../types/__init__.py | 46 +
...t_disposition_metric_templates_response.py | 22 +
...ion_metric_templates_response_data_item.py | 45 +
...ponse_data_item_disposition_metric_type.py | 7 +
src/smallestai/atoms/dnc/__init__.py | 51 +
src/smallestai/atoms/dnc/types/__init__.py | 49 +
src/smallestai/atoms/errors/__init__.py | 61 +
src/smallestai/atoms/errors/locked_error.py | 11 +
src/smallestai/atoms/integrations/__init__.py | 37 +
src/smallestai/atoms/integrations/client.py | 215 +
.../atoms/integrations/raw_client.py | 389 ++
.../atoms/integrations/types/__init__.py | 38 +
.../types/get_web_engage_details_response.py} | 8 +-
...modify_web_engage_integration_response.py} | 8 +-
.../atoms/knowledge_base/__init__.py | 84 +
.../atoms/knowledge_base/types/__init__.py | 86 +
.../types/get_knowledgebase_id_response.py | 22 -
.../types/get_knowledgebase_response.py | 22 -
...knowledgebase_get_sitemap_urls_response.py | 22 -
.../types/post_knowledgebase_response.py | 21 -
.../atoms/live_transcripts/__init__.py | 42 +
.../atoms/live_transcripts/types/__init__.py | 44 +
src/smallestai/atoms/organization/__init__.py | 45 +
src/smallestai/atoms/organization/client.py | 155 -
.../atoms/organization/raw_client.py | 312 -
.../atoms/organization/types/__init__.py | 43 +
...t_account_details_organization_response.py | 40 -
.../atoms/phone_numbers/__init__.py | 111 +
src/smallestai/atoms/phone_numbers/client.py | 8 +-
.../atoms/phone_numbers/raw_client.py | 4 +-
.../atoms/phone_numbers/types/__init__.py | 113 +
..._get_available_numbers_request_provider.py | 5 -
..._product_get_available_numbers_response.py | 22 -
...et_available_numbers_response_data_item.py | 41 -
...ble_numbers_response_data_item_provider.py | 5 -
.../get_product_phone_numbers_response.py | 22 -
...roduct_phone_numbers_response_data_item.py | 64 -
...e_numbers_response_data_item_attributes.py | 56 -
..._response_data_item_attributes_provider.py | 5 -
...numbers_response_data_item_product_type.py | 5 -
...oduct_import_phone_number_response_data.py | 61 -
...t_phone_number_response_data_attributes.py | 41 -
...phone_number_response_data_product_type.py | 5 -
...st_product_rent_number_request_provider.py | 5 -
.../post_product_rent_number_response.py | 22 -
.../atoms/prompt_scoring/types/__init__.py | 62 +
src/smallestai/atoms/realtime/__init__.py | 49 +
src/smallestai/atoms/realtime/client.py | 195 +
src/smallestai/atoms/realtime/raw_client.py | 307 +
.../atoms/realtime/types/__init__.py | 47 +
.../register_call_realtime_request_mode.py | 5 +
...r_call_realtime_request_variables_value.py | 5 +
.../types/register_call_realtime_response.py | 22 +
.../register_call_realtime_response_data.py | 36 +
src/smallestai/atoms/types/__init__.py | 540 ++
src/smallestai/atoms/types/agent_dto.py | 5 -
.../agent_dto_synthesizer_voice_config.py | 5 -
...gent_dto_synthesizer_voice_config_model.py | 12 +-
.../atoms/types/analytics_date_range.py | 26 +
.../types/analytics_trend_metric.py} | 15 +-
.../atoms/types/bad_request_error_body.py | 4 +-
src/smallestai/atoms/types/branch.py | 94 +
src/smallestai/atoms/types/branch_status.py | 5 +
src/smallestai/atoms/types/branch_summary.py | 41 +
src/smallestai/atoms/types/call_action.py | 69 +
.../atoms/types/call_action_action_type.py | 5 +
.../atoms/types/call_action_category.py | 5 +
.../atoms/types/call_action_condition.py | 38 +
.../types/call_action_condition_operator.py | 10 +
.../atoms/types/call_action_config.py | 35 +
.../atoms/types/conflict_error_response.py | 26 +
src/smallestai/atoms/types/diff_change.py | 32 +
.../types/diff_result.py} | 19 +-
...thorized_error_body.py => diff_section.py} | 7 +-
.../atoms/types/draft_conflict_error.py | 27 +
.../types/draft_conflict_error_data.py} | 9 +-
.../draft_conflict_error_data_conflict.py | 28 +
src/smallestai/atoms/types/draft_detail.py | 36 +
.../types/draft_detail_edit_history_item.py | 36 +
.../atoms/types/internal_server_error_body.py | 4 +-
.../atoms/types/locked_error_response.py | 27 +
.../types/locked_error_response_error_type.py | 5 +
src/smallestai/atoms/types/prompt_score.py | 35 +
.../types/prompt_score_dimensions_item.py} | 14 +-
src/smallestai/atoms/types/publish_result.py | 33 +
.../atoms/types/publish_result_state.py | 5 +
src/smallestai/atoms/types/revision.py | 145 +
.../atoms/types/revision_pending_publish.py | 34 +
.../types/revision_pending_publish_state.py | 5 +
.../atoms/types/revision_security_check.py | 38 +
src/smallestai/atoms/types/revision_status.py | 5 +
.../atoms/types/test_call_result.py | 43 +
src/smallestai/atoms/types/version_blocks.py | 40 +
...ersioning_v2migration_required_response.py | 27 +
...2migration_required_response_error_type.py | 7 +
.../atoms/types/web_engage_integration_set.py | 38 +
src/smallestai/atoms/user/__init__.py | 30 +
src/smallestai/atoms/user/client.py | 73 -
src/smallestai/atoms/user/raw_client.py | 158 -
src/smallestai/atoms/user/types/__init__.py | 34 +
.../get_subscription_user_response_data.py | 35 -
...ubscription_user_response_data_features.py | 42 -
..._subscription_user_response_data_limits.py | 44 -
.../atoms/webhooks/types/__init__.py | 63 +
..._webhook_request_events_item_event_type.py | 7 -
src/smallestai/core/__init__.py | 116 +-
src/smallestai/waves/errors/__init__.py | 36 +
.../waves/lightning_v2tts/__init__.py | 4 -
.../waves/lightning_v2tts/client.py | 161 -
.../waves/lightning_v2tts/raw_client.py | 134 -
.../waves/lightning_v2tts/socket_client.py | 189 -
.../waves/lightning_v2tts/types/__init__.py | 4 -
.../types/lightning_v2tts_request_message.py | 83 -
.../types/lightning_v2tts_response_message.py | 32 -
.../lightning_v2tts_response_message_data.py | 23 -
...lightning_v2tts_response_message_status.py | 5 -
.../waves/lightning_v31tts/__init__.py | 4 -
.../waves/lightning_v31tts/client.py | 337 -
.../waves/lightning_v31tts/raw_client.py | 310 -
.../waves/lightning_v31tts/socket_client.py | 189 -
.../waves/lightning_v31tts/types/__init__.py | 4 -
.../types/lightning_v31tts_request_message.py | 104 -
...ghtning_v31tts_request_message_language.py | 7 -
.../lightning_v31tts_request_message_model.py | 5 -
.../lightning_v31tts_response_message.py | 53 -
.../lightning_v31tts_response_message_data.py | 47 -
...ightning_v31tts_response_message_status.py | 5 -
.../waves/pulse_stt_streaming/__init__.py | 66 +
.../pulse_stt_streaming/socket_client.py | 6 +-
.../pulse_stt_streaming/types/__init__.py | 64 +
.../types/pulse_speech_ended_event_message.py | 34 +
.../pulse_speech_ended_event_message_type.py | 5 +
.../pulse_speech_started_event_message.py | 34 +
...pulse_speech_started_event_message_type.py | 5 +
.../waves/speech_to_speech/__init__.py | 186 +
.../waves/speech_to_speech/client.py | 4 +-
.../waves/speech_to_speech/raw_client.py | 4 +-
.../waves/speech_to_speech/types/__init__.py | 184 +
.../waves/streaming_tts/__init__.py | 51 +
.../waves/streaming_tts/types/__init__.py | 49 +
src/smallestai/waves/tts/__init__.py | 54 +
src/smallestai/waves/tts/client.py | 46 +-
src/smallestai/waves/tts/raw_client.py | 46 +-
src/smallestai/waves/tts/types/__init__.py | 52 +
.../waves/tts/types/tts_request_message.py | 48 +-
.../tts/types/tts_request_message_language.py | 36 +-
...t_message_number_pronunciation_language.py | 41 +
src/smallestai/waves/types/__init__.py | 237 +
src/smallestai/waves/types/audio_chunk.py | 9 +-
.../waves/types/audio_chunk_data.py | 14 +-
.../waves/types/audio_chunk_status.py | 5 +
.../waves/types/bad_request_error_body.py | 4 +-
.../waves/types/completion_status.py | 23 +-
...loned_voices_waves_response_voices_item.py | 55 -
.../get_voices_waves_response_voices_item.py | 9 +-
..._voices_waves_response_voices_item_tags.py | 6 +-
.../waves/types/internal_server_error_body.py | 4 +-
.../waves/types/lightning_request.py | 50 +
.../waves/types/lightning_v31request.py | 90 -
.../types/lightning_v31request_language.py | 7 -
.../waves/types/lightning_v31request_model.py | 5 -
.../lightning_v31request_output_format.py | 5 -
...tning_large_waves_request_output_format.py | 7 +
...lightning_v2waves_request_output_format.py | 5 +
...e_lightning_waves_request_output_format.py | 5 +
...tning_large_waves_request_output_format.py | 7 +
...lightning_v2waves_request_output_format.py | 7 +
...transcribe_pulse_waves_request_language.py | 1 +
src/smallestai/waves/types/tts_request.py | 52 +-
.../waves/types/tts_request_language.py | 36 +-
...s_request_number_pronunciation_language.py | 41 +
.../test_atoms_agentVersioningBranches.py | 134 +
.../wire/test_atoms_agentVersioningDrafts.py | 36 +-
.../test_atoms_agentVersioningRevisions.py | 60 +
.../test_atoms_agentVersioningVersions.py | 52 +-
tests/wire/test_atoms_agents.py | 109 +-
tests/wire/test_atoms_analytics.py | 155 +
tests/wire/test_atoms_callActions.py | 56 +
tests/wire/test_atoms_campaigns.py | 10 -
tests/wire/test_atoms_concurrency.py | 29 +
.../test_atoms_dispositionMetricTemplates.py | 9 +
tests/wire/test_atoms_integrations.py | 27 +
tests/wire/test_atoms_organization.py | 18 -
tests/wire/test_atoms_phoneNumbers.py | 2 +-
tests/wire/test_atoms_realtime.py | 11 +
tests/wire/test_atoms_user.py | 8 -
tests/wire/test_waves.py | 97 +-
wiremock/wiremock-mappings.json | 2077 ++++--
404 files changed, 33567 insertions(+), 9180 deletions(-)
delete mode 100644 src/smallestai/.fern/metadata.json
create mode 100644 src/smallestai/atoms/agent_versioning_branches/__init__.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/client.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/raw_client.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/__init__.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/archive_agent_versioning_branches_response.py
rename src/smallestai/atoms/{knowledge_base/types/delete_knowledgebase_id_response.py => agent_versioning_branches/types/archive_agent_versioning_branches_response_data.py} (81%)
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response.py
rename src/smallestai/atoms/{campaigns/types/delete_campaign_id_response.py => agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response_data.py} (80%)
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/create_branch_agent_versioning_branches_response.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/discard_draft_agent_versioning_branches_response.py
rename src/smallestai/atoms/{agents/types/create_with_ai_agents_request_questions_item.py => agent_versioning_branches/types/discard_draft_agent_versioning_branches_response_data.py} (81%)
rename src/smallestai/atoms/{agent_versioning_versions/types/patch_agent_id_versions_version_id_response.py => agent_versioning_branches/types/get_agent_versioning_branches_response.py} (77%)
rename src/smallestai/atoms/{agent_versioning_drafts/types/post_agent_id_drafts_draft_id_publish_response.py => agent_versioning_branches/types/get_draft_agent_versioning_branches_response.py} (77%)
rename src/smallestai/atoms/{agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response.py => agent_versioning_branches/types/list_agent_versioning_branches_response.py} (70%)
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/list_agent_versioning_branches_response_data.py
rename src/smallestai/atoms/{agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_config_response.py => agent_versioning_branches/types/make_live_agent_versioning_branches_response.py} (77%)
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/publish_draft_agent_versioning_branches_response.py
rename src/smallestai/atoms/{agent_versioning_drafts/types/delete_agent_id_drafts_draft_id_response.py => agent_versioning_branches/types/rename_agent_versioning_branches_response.py} (79%)
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/test_call_agent_versioning_branches_response.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/test_call_v2request_mode.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_background_sound.py
create mode 100644 src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_slm_model.py
rename src/smallestai/atoms/{agents/types/get_widget_config_agents_response.py => agent_versioning_branches/types/update_draft_agent_versioning_branches_response.py} (78%)
create mode 100644 src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data.py
create mode 100644 src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data_sections_value.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/__init__.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/client.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/raw_client.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/__init__.py
rename src/smallestai/atoms/{agent_versioning_drafts/types/post_agent_id_drafts_response.py => agent_versioning_revisions/types/diff_agent_versioning_revisions_response.py} (78%)
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response_data.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data_trail_item.py
rename src/smallestai/atoms/{phone_numbers/types/post_product_import_phone_number_response.py => agent_versioning_revisions/types/list_agent_versioning_revisions_response.py} (70%)
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/list_agent_versioning_revisions_response_data.py
create mode 100644 src/smallestai/atoms/agent_versioning_revisions/types/restore_agent_versioning_revisions_response.py
delete mode 100644 src/smallestai/atoms/agent_versioning_versions/types/compare_version_metrics_agent_versioning_versions_response.py
delete mode 100644 src/smallestai/atoms/agent_versioning_versions/types/get_agent_id_versions_compare_metrics_response.py
delete mode 100644 src/smallestai/atoms/agent_versioning_versions/types/patch_agent_id_versions_version_id_activate_response.py
delete mode 100644 src/smallestai/atoms/agents/types/create_with_ai_agents_request_type.py
delete mode 100644 src/smallestai/atoms/agents/types/create_with_ai_agents_request_voice_model.py
create mode 100644 src/smallestai/atoms/agents/types/get_agent_avatar_presigned_url_response.py
rename src/smallestai/atoms/{campaigns/types/post_campaign_id_start_response_data.py => agents/types/get_agent_avatar_presigned_url_response_data.py} (54%)
create mode 100644 src/smallestai/atoms/agents/types/get_agent_call_logs_response.py
rename src/smallestai/atoms/agents/types/{list_call_logs_agents_response_data.py => get_agent_call_logs_response_data.py} (59%)
rename src/smallestai/atoms/agents/types/{list_call_logs_agents_response_data_call_logs_item.py => get_agent_call_logs_response_data_call_logs_item.py} (61%)
delete mode 100644 src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data.py
delete mode 100644 src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data_data.py
rename src/smallestai/atoms/agents/types/{get_agent_id_workflow_response.py => get_agent_widget_config_response.py} (61%)
create mode 100644 src/smallestai/atoms/agents/types/get_agent_widget_config_response_data.py
create mode 100644 src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_mode.py
create mode 100644 src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_position.py
create mode 100644 src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_size.py
create mode 100644 src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_theme.py
delete mode 100644 src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data.py
delete mode 100644 src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item.py
delete mode 100644 src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item_type.py
create mode 100644 src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config.py
create mode 100644 src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_mode.py
create mode 100644 src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_position.py
create mode 100644 src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_size.py
create mode 100644 src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_theme.py
rename src/smallestai/atoms/{knowledge_base/types/post_knowledgebase_id_scrape_urls_response.py => agents/types/update_agent_widget_config_response.py} (76%)
delete mode 100644 src/smallestai/atoms/agents/types/update_widget_config_agents_response.py
delete mode 100644 src/smallestai/atoms/agents/types/update_workflow_configuration_agents_request_workflow_graph.py
create mode 100644 src/smallestai/atoms/analytics/__init__.py
create mode 100644 src/smallestai/atoms/analytics/client.py
create mode 100644 src/smallestai/atoms/analytics/raw_client.py
create mode 100644 src/smallestai/atoms/analytics/types/__init__.py
create mode 100644 src/smallestai/atoms/analytics/types/get_agent_performance_request_sort_order.py
rename src/smallestai/atoms/{campaigns/types/post_campaign_id_start_response.py => analytics/types/get_agent_performance_response.py} (67%)
rename src/smallestai/atoms/{campaigns/types/get_campaign_id_response_data_campaign_agent.py => analytics/types/get_agent_performance_response_data.py} (54%)
create mode 100644 src/smallestai/atoms/analytics/types/get_agent_performance_response_data_agents_item.py
create mode 100644 src/smallestai/atoms/analytics/types/get_analytics_concurrency_request_include_agents.py
create mode 100644 src/smallestai/atoms/analytics/types/get_analytics_concurrency_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data_by_agent_item.py
rename src/smallestai/atoms/{knowledge_base/types/post_knowledgebase_get_sitemap_urls_response_data.py => analytics/types/get_analytics_concurrency_response_data_data_points_item.py} (62%)
rename src/smallestai/atoms/{user/types/get_subscription_user_response.py => analytics/types/get_analytics_summary_response.py} (67%)
create mode 100644 src/smallestai/atoms/analytics/types/get_analytics_summary_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_attempt_cohort_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data_cohorts_item.py
rename src/smallestai/atoms/{compliance/types/patch_compliance_applications_id_response.py => analytics/types/get_call_counts_by_day_response.py} (67%)
rename src/smallestai/atoms/{organization/types/get_account_details_organization_response_organizations_item.py => analytics/types/get_call_counts_by_day_response_data.py} (55%)
rename src/smallestai/atoms/{agents/types/get_prompt_config_agents_response_data_questions_item_options_item.py => analytics/types/get_call_counts_by_day_response_data_call_counts_by_day_item.py} (73%)
create mode 100644 src/smallestai/atoms/analytics/types/get_call_counts_log_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_call_counts_log_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_call_counts_log_response_data_calls_item.py
create mode 100644 src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data.py
rename src/smallestai/atoms/{audience/types/get_audience_response_data_item_campaigns_item.py => analytics/types/get_call_outcomes_timeseries_response_data_data_points_item.py} (58%)
rename src/smallestai/atoms/{phone_numbers/types/post_product_rent_number_response_data.py => analytics/types/get_call_outcomes_timeseries_response_data_totals.py} (60%)
rename src/smallestai/atoms/{phone_numbers/types/post_product_release_number_response.py => analytics/types/get_call_start_distribution_response.py} (66%)
create mode 100644 src/smallestai/atoms/analytics/types/get_call_start_distribution_response_data.py
rename src/smallestai/atoms/{campaigns/types/get_campaign_id_response_data_campaign_audience.py => analytics/types/get_call_start_distribution_response_data_data_points_item.py} (64%)
create mode 100644 src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data_data_points_item.py
create mode 100644 src/smallestai/atoms/analytics/types/get_conversation_details_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_conversation_details_response_data.py
rename src/smallestai/atoms/{campaigns/types/get_campaign_response_data_campaigns_item_audience.py => analytics/types/get_conversation_details_response_data_events_item.py} (66%)
create mode 100644 src/smallestai/atoms/analytics/types/get_conversation_details_response_data_transcript_item.py
create mode 100644 src/smallestai/atoms/analytics/types/get_daily_call_summary_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_daily_call_summary_response_data.py
rename src/smallestai/atoms/{campaigns/types/post_campaign_response.py => analytics/types/get_dashboard_response.py} (70%)
create mode 100644 src/smallestai/atoms/analytics/types/get_dashboard_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_duration_stats_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_duration_stats_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_hourly_performance_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_hourly_performance_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_hourly_performance_response_data_hours_item.py
create mode 100644 src/smallestai/atoms/analytics/types/get_phone_number_trends_response.py
create mode 100644 src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data.py
rename src/smallestai/atoms/{campaigns/types/get_campaign_response_data_pagination.py => analytics/types/get_phone_number_trends_response_data_trends_item.py} (50%)
create mode 100644 src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data_trends_item_data_points_item.py
rename src/smallestai/atoms/{agents/types/get_prompt_config_agents_response.py => analytics/types/get_pickup_rate_by_number_response.py} (67%)
create mode 100644 src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data.py
create mode 100644 src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data_numbers_item.py
rename src/smallestai/atoms/{agents/types/list_call_logs_agents_response.py => analytics/types/get_usage_timeseries_response.py} (68%)
create mode 100644 src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data.py
rename src/smallestai/atoms/{agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response_data.py => analytics/types/get_usage_timeseries_response_data_date_range.py} (69%)
create mode 100644 src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data_day_wise_credits_item.py
create mode 100644 src/smallestai/atoms/analytics/types/get_weekly_trends_response.py
rename src/smallestai/atoms/{campaigns/types/get_campaign_response_data_campaigns_item_agent.py => analytics/types/get_weekly_trends_response_data.py} (55%)
create mode 100644 src/smallestai/atoms/analytics/types/get_weekly_trends_response_data_weeks_item.py
delete mode 100644 src/smallestai/atoms/audience/types/get_audience_id_response.py
delete mode 100644 src/smallestai/atoms/audience/types/get_audience_id_response_data.py
delete mode 100644 src/smallestai/atoms/audience/types/get_audience_response_data_item.py
create mode 100644 src/smallestai/atoms/call_actions/__init__.py
create mode 100644 src/smallestai/atoms/call_actions/client.py
create mode 100644 src/smallestai/atoms/call_actions/raw_client.py
create mode 100644 src/smallestai/atoms/call_actions/types/__init__.py
create mode 100644 src/smallestai/atoms/call_actions/types/create_call_action_request_action_type.py
create mode 100644 src/smallestai/atoms/call_actions/types/create_call_action_request_category.py
create mode 100644 src/smallestai/atoms/call_actions/types/create_call_action_request_config.py
rename src/smallestai/atoms/{phone_numbers/types/post_product_release_number_response_data.py => call_actions/types/create_call_action_response.py} (79%)
create mode 100644 src/smallestai/atoms/call_actions/types/delete_call_action_response.py
rename src/smallestai/atoms/{campaigns/types/post_campaign_id_pause_response.py => call_actions/types/delete_call_action_response_data.py} (83%)
rename src/smallestai/atoms/{organization/types/update_name_organization_response.py => call_actions/types/get_call_action_response.py} (79%)
create mode 100644 src/smallestai/atoms/call_actions/types/list_call_actions_request_category.py
create mode 100644 src/smallestai/atoms/call_actions/types/list_call_actions_response.py
rename src/smallestai/atoms/{agents/types/list_call_logs_agents_response_data_pagination.py => call_actions/types/list_call_actions_response_data.py} (55%)
create mode 100644 src/smallestai/atoms/call_actions/types/update_call_action_request_action_type.py
create mode 100644 src/smallestai/atoms/call_actions/types/update_call_action_request_category.py
create mode 100644 src/smallestai/atoms/call_actions/types/update_call_action_request_config.py
create mode 100644 src/smallestai/atoms/call_actions/types/update_call_action_response.py
delete mode 100644 src/smallestai/atoms/campaigns/types/export_logs_campaigns_response_item.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_id_response.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_id_response_data.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_status.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_events_item.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_metrics.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_request_sort_field.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_request_sort_order.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_request_status.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_response.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_response_data.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item.py
delete mode 100644 src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_status.py
delete mode 100644 src/smallestai/atoms/campaigns/types/post_campaign_response_data.py
delete mode 100644 src/smallestai/atoms/campaigns/types/post_campaign_response_data_status.py
delete mode 100644 src/smallestai/atoms/compliance/types/post_compliance_applications_request_number_type.py
delete mode 100644 src/smallestai/atoms/compliance/types/post_compliance_applications_request_user_type.py
delete mode 100644 src/smallestai/atoms/compliance/types/post_compliance_applications_response.py
create mode 100644 src/smallestai/atoms/concurrency/__init__.py
create mode 100644 src/smallestai/atoms/concurrency/client.py
create mode 100644 src/smallestai/atoms/concurrency/raw_client.py
create mode 100644 src/smallestai/atoms/concurrency/types/__init__.py
create mode 100644 src/smallestai/atoms/concurrency/types/get_concurrency_response.py
create mode 100644 src/smallestai/atoms/concurrency/types/get_concurrency_response_data.py
create mode 100644 src/smallestai/atoms/concurrency/types/get_concurrency_response_data_agents_item.py
rename src/smallestai/atoms/{webhooks/types/post_webhook_request_events_item.py => concurrency/types/update_concurrency_reservations_request_reservations_item.py} (60%)
create mode 100644 src/smallestai/atoms/concurrency/types/update_concurrency_reservations_response.py
rename src/smallestai/atoms/{webhooks/types/delete_webhook_id_response.py => concurrency/types/update_concurrency_reservations_response_data.py} (74%)
create mode 100644 src/smallestai/atoms/disposition_metric_templates/__init__.py
create mode 100644 src/smallestai/atoms/disposition_metric_templates/client.py
create mode 100644 src/smallestai/atoms/disposition_metric_templates/raw_client.py
create mode 100644 src/smallestai/atoms/disposition_metric_templates/types/__init__.py
create mode 100644 src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response.py
create mode 100644 src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item.py
create mode 100644 src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item_disposition_metric_type.py
create mode 100644 src/smallestai/atoms/errors/locked_error.py
create mode 100644 src/smallestai/atoms/integrations/__init__.py
create mode 100644 src/smallestai/atoms/integrations/client.py
create mode 100644 src/smallestai/atoms/integrations/raw_client.py
create mode 100644 src/smallestai/atoms/integrations/types/__init__.py
rename src/smallestai/atoms/{agents/types/create_with_ai_agents_response.py => integrations/types/get_web_engage_details_response.py} (71%)
rename src/smallestai/atoms/{webhooks/types/post_webhook_response.py => integrations/types/modify_web_engage_integration_response.py} (70%)
delete mode 100644 src/smallestai/atoms/knowledge_base/types/get_knowledgebase_id_response.py
delete mode 100644 src/smallestai/atoms/knowledge_base/types/get_knowledgebase_response.py
delete mode 100644 src/smallestai/atoms/knowledge_base/types/post_knowledgebase_get_sitemap_urls_response.py
delete mode 100644 src/smallestai/atoms/knowledge_base/types/post_knowledgebase_response.py
delete mode 100644 src/smallestai/atoms/organization/types/get_account_details_organization_response.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_request_provider.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item_provider.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes_provider.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_product_type.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_attributes.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_product_type.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/post_product_rent_number_request_provider.py
delete mode 100644 src/smallestai/atoms/phone_numbers/types/post_product_rent_number_response.py
create mode 100644 src/smallestai/atoms/realtime/__init__.py
create mode 100644 src/smallestai/atoms/realtime/client.py
create mode 100644 src/smallestai/atoms/realtime/raw_client.py
create mode 100644 src/smallestai/atoms/realtime/types/__init__.py
create mode 100644 src/smallestai/atoms/realtime/types/register_call_realtime_request_mode.py
create mode 100644 src/smallestai/atoms/realtime/types/register_call_realtime_request_variables_value.py
create mode 100644 src/smallestai/atoms/realtime/types/register_call_realtime_response.py
create mode 100644 src/smallestai/atoms/realtime/types/register_call_realtime_response_data.py
create mode 100644 src/smallestai/atoms/types/analytics_date_range.py
rename src/smallestai/{waves/types/delete_voice_waves_response.py => atoms/types/analytics_trend_metric.py} (60%)
create mode 100644 src/smallestai/atoms/types/branch.py
create mode 100644 src/smallestai/atoms/types/branch_status.py
create mode 100644 src/smallestai/atoms/types/branch_summary.py
create mode 100644 src/smallestai/atoms/types/call_action.py
create mode 100644 src/smallestai/atoms/types/call_action_action_type.py
create mode 100644 src/smallestai/atoms/types/call_action_category.py
create mode 100644 src/smallestai/atoms/types/call_action_condition.py
create mode 100644 src/smallestai/atoms/types/call_action_condition_operator.py
create mode 100644 src/smallestai/atoms/types/call_action_config.py
create mode 100644 src/smallestai/atoms/types/conflict_error_response.py
create mode 100644 src/smallestai/atoms/types/diff_change.py
rename src/smallestai/{waves/types/add_voice_waves_response_data.py => atoms/types/diff_result.py} (60%)
rename src/smallestai/atoms/types/{unauthorized_error_body.py => diff_section.py} (74%)
create mode 100644 src/smallestai/atoms/types/draft_conflict_error.py
rename src/smallestai/{waves/types/get_cloned_voices_waves_response.py => atoms/types/draft_conflict_error_data.py} (61%)
create mode 100644 src/smallestai/atoms/types/draft_conflict_error_data_conflict.py
create mode 100644 src/smallestai/atoms/types/draft_detail.py
create mode 100644 src/smallestai/atoms/types/draft_detail_edit_history_item.py
create mode 100644 src/smallestai/atoms/types/locked_error_response.py
create mode 100644 src/smallestai/atoms/types/locked_error_response_error_type.py
create mode 100644 src/smallestai/atoms/types/prompt_score.py
rename src/smallestai/{waves/types/add_voice_waves_response.py => atoms/types/prompt_score_dimensions_item.py} (61%)
create mode 100644 src/smallestai/atoms/types/publish_result.py
create mode 100644 src/smallestai/atoms/types/publish_result_state.py
create mode 100644 src/smallestai/atoms/types/revision.py
create mode 100644 src/smallestai/atoms/types/revision_pending_publish.py
create mode 100644 src/smallestai/atoms/types/revision_pending_publish_state.py
create mode 100644 src/smallestai/atoms/types/revision_security_check.py
create mode 100644 src/smallestai/atoms/types/revision_status.py
create mode 100644 src/smallestai/atoms/types/test_call_result.py
create mode 100644 src/smallestai/atoms/types/version_blocks.py
create mode 100644 src/smallestai/atoms/types/versioning_v2migration_required_response.py
create mode 100644 src/smallestai/atoms/types/versioning_v2migration_required_response_error_type.py
create mode 100644 src/smallestai/atoms/types/web_engage_integration_set.py
delete mode 100644 src/smallestai/atoms/user/types/get_subscription_user_response_data.py
delete mode 100644 src/smallestai/atoms/user/types/get_subscription_user_response_data_features.py
delete mode 100644 src/smallestai/atoms/user/types/get_subscription_user_response_data_limits.py
delete mode 100644 src/smallestai/atoms/webhooks/types/post_webhook_request_events_item_event_type.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/__init__.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/client.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/raw_client.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/socket_client.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/types/__init__.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_request_message.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_data.py
delete mode 100644 src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_status.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/__init__.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/client.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/raw_client.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/socket_client.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/types/__init__.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_language.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_model.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_data.py
delete mode 100644 src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_status.py
create mode 100644 src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message.py
create mode 100644 src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message_type.py
create mode 100644 src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message.py
create mode 100644 src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message_type.py
create mode 100644 src/smallestai/waves/tts/types/tts_request_message_number_pronunciation_language.py
create mode 100644 src/smallestai/waves/types/audio_chunk_status.py
delete mode 100644 src/smallestai/waves/types/get_cloned_voices_waves_response_voices_item.py
create mode 100644 src/smallestai/waves/types/lightning_request.py
delete mode 100644 src/smallestai/waves/types/lightning_v31request.py
delete mode 100644 src/smallestai/waves/types/lightning_v31request_language.py
delete mode 100644 src/smallestai/waves/types/lightning_v31request_model.py
delete mode 100644 src/smallestai/waves/types/lightning_v31request_output_format.py
create mode 100644 src/smallestai/waves/types/synthesize_lightning_large_waves_request_output_format.py
create mode 100644 src/smallestai/waves/types/synthesize_lightning_v2waves_request_output_format.py
create mode 100644 src/smallestai/waves/types/synthesize_lightning_waves_request_output_format.py
create mode 100644 src/smallestai/waves/types/synthesize_sse_lightning_large_waves_request_output_format.py
create mode 100644 src/smallestai/waves/types/synthesize_sse_lightning_v2waves_request_output_format.py
create mode 100644 src/smallestai/waves/types/tts_request_number_pronunciation_language.py
create mode 100644 tests/wire/test_atoms_agentVersioningBranches.py
create mode 100644 tests/wire/test_atoms_agentVersioningRevisions.py
create mode 100644 tests/wire/test_atoms_analytics.py
create mode 100644 tests/wire/test_atoms_callActions.py
create mode 100644 tests/wire/test_atoms_concurrency.py
create mode 100644 tests/wire/test_atoms_dispositionMetricTemplates.py
create mode 100644 tests/wire/test_atoms_integrations.py
create mode 100644 tests/wire/test_atoms_realtime.py
diff --git a/.fern/metadata.json b/.fern/metadata.json
index e18fcc0d..a4acd2a8 100644
--- a/.fern/metadata.json
+++ b/.fern/metadata.json
@@ -12,7 +12,7 @@
"pydantic_config": {
"skip_validation": true
},
- "exclude_types_from_init_exports": true,
+ "exclude_types_from_init_exports": false,
"should_generate_websocket_clients": true,
"extra_dependencies": {
"fastapi": ">=0.115.0",
@@ -45,10 +45,10 @@
}
]
},
- "originGitCommit": "9b5e3dc88b2258bd955f7570d3c406f8a62d7a7c",
+ "originGitCommit": "ae4a1383eb215784f2c7a4caf3b1c42b6428c516",
"originGitCommitIsDirty": false,
"invokedBy": "ci",
- "requestedVersion": "5.1.0",
+ "requestedVersion": "5.2.0",
"ciProvider": "github",
- "sdkVersion": "5.1.0"
+ "sdkVersion": "5.2.0"
}
\ No newline at end of file
diff --git a/reference.md b/reference.md
index 31e1f395..ae522a60 100644
--- a/reference.md
+++ b/reference.md
@@ -45,73 +45,6 @@ client.atoms.user.get_user_details()
-
-
-
-
-client.atoms.user.get_subscription() -> GetSubscriptionUserResponse
-
--
-
-#### ๐ Description
-
-
--
-
-
--
-
-Returns the caller's subscription details (plan ID + plan-tier limits) and
-feature-flag map. Useful for client-side gating of paid features.
-
-**`limits` is omitted** for the `ENTERPRISE` plan (and for unknown plan IDs) โ
-only `features` is returned in those cases.
-
-
-
-
-
-#### ๐ Usage
-
-
--
-
-
--
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
-
-client.atoms.user.get_subscription()
-
-```
-
-
-
-
-
-#### โ๏ธ Parameters
-
-
--
-
-
--
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
-
-
-
-
-
-
-
@@ -162,148 +95,6 @@ client.atoms.organization.get_organization_details()
-
-
-
-
-client.atoms.organization.get_account_details() -> GetAccountDetailsOrganizationResponse
-
--
-
-#### ๐ Description
-
-
--
-
-
--
-
-Returns the authenticated user's profile and the organizations they belong to.
-
-**Response envelope is non-standard** โ this endpoint returns the account object
-directly (no `{status, data}` wrapper).
-
-
-
-
-
-#### ๐ Usage
-
-
--
-
-
--
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
-
-client.atoms.organization.get_account_details()
-
-```
-
-
-
-
-
-#### โ๏ธ Parameters
-
-
--
-
-
--
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
-
-
-
-
-
-
-
-
-
-
-
-client.atoms.organization.update_name(...) -> UpdateNameOrganizationResponse
-
--
-
-#### ๐ Description
-
-
--
-
-
--
-
-Update the display name of the caller's current organization. Requires admin role.
-
-**Response envelope is non-standard** โ returns `{success: true, name}` instead
-of the usual `{status, data}` wrapper.
-
-
-
-
-
-#### ๐ Usage
-
-
--
-
-
--
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
-
-client.atoms.organization.update_name(
- name="name",
-)
-
-```
-
-
-
-
-
-#### โ๏ธ Parameters
-
-
--
-
-
--
-
-**name:** `str` โ New organization name (trimmed).
-
-
-
-
-
--
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
-
-
-
-
-
-
-
@@ -456,7 +247,7 @@ client.atoms.agent_templates.create_agent_from_template(
-## Atoms Agents
+## Agents
client.atoms.agents.list_agents(...) -> ListAgentsAgentsResponse
-
@@ -678,10 +469,11 @@ Tamil (`ta`) cannot be combined with other languages in `supported`.
**synthesizer:** `typing.Optional[CreateAgentRequestSynthesizer]`
-Synthesizer (TTS) configuration for the agent.
-Models `waves`, `waves_lightning_large`, `waves_lightning_v2`, and `waves_lightning_v3_1`
-validate `voiceId` against the Waves API. All other models accept any voiceId.
-Cloned voices are regular voiceIds โ use them with any compatible Waves model.
+Synthesizer (TTS) configuration for the agent. Model
+`waves_lightning_v3_1` validates `voiceId` against the Waves
+API. `gpt-realtime` and `gpt-realtime-mini` accept any voiceId.
+Cloned voices are regular voiceIds. Use them with a compatible
+Waves model.
@@ -908,7 +700,7 @@ Note: Only used for workflow_graph agents. Maximum 4000 characters.
-client.atoms.agents.get_agent(...) -> GetAgentAgentsResponse
+client.atoms.agents.duplicate_agent(...) -> DuplicateAgentAgentsResponse
-
@@ -920,34 +712,17 @@ Note: Only used for workflow_graph agents. Maximum 4000 characters.
-
-Returns the agent document merged with the resolved config of the active version
-under `_resolvedConfig`. Non-versioned fields (name, telephonyProductId, allowInboundCall,
-etc.) sit at the top level; versioned fields (prompt, tools, language, synthesizer,
-post-call analytics, โฆ) are resolved from the target version and exposed under `_resolvedConfig`.
-
-**Previewing a draft or specific version**
-
-Pass `?draftId=` to resolve config from a specific draft instead of the active version.
-Pass `?versionId=` to resolve config from a specific published version.
-When either param is used, the response includes `_configSource: "draft" | "version" | "active"`
-indicating which config was resolved.
-
-Notable resolved fields in `_resolvedConfig`:
-
-- `prompt` โ active version's single-prompt text
-- `tools` โ configured tools on the resolved version
-- `postCallAnalyticsConfig` โ disposition metrics + analytics model flags
-- `modelName` โ LLM model name on the resolved version
-- `defaultLanguage`, `supportedLanguages` โ active language config
-- `firstMessage`, `globalPrompt` โ active messaging config
-- `workflowGraph` โ full node graph for `workflow_graph` agents
-
-To read prompt + tools alone, use `GET /agent/{id}/workflow` (deprecated for
-new integrations but still live). To inspect a specific non-active version,
-use `GET /agent/{id}/versions/{versionId}`.
+Duplicates a SINGLE_PROMPT agent's live active version into a target organization
+(can also be the same organization). Copies all versioned configuration but strips
+organization-specific resources: knowledge base tools are removed, default variable
+values are blanked, and a new avatar is generated. The duplicate starts with a
+published V1 as its active version.
-**400 โ also used for "not found":** if the agent ID does not exist in the
-organization, the API returns 400 with `errors: ["No agent found"]` rather than 404.
+**400 is returned when:**
+- The source agent is archived (`"Cannot duplicate an archived agent"`)
+- The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
+- The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
+- The agent is not `SINGLE_PROMPT` workflow type
@@ -970,8 +745,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.get_agent(
+client.atoms.agents.duplicate_agent(
id="id",
+ target_organization_id="60d0fe4f5311236168a109ca",
)
```
@@ -988,7 +764,7 @@ client.atoms.agents.get_agent(
-
-**id:** `str`
+**id:** `str` โ The ID of the source agent to duplicate
@@ -996,15 +772,10 @@ client.atoms.agents.get_agent(
-
-**draft_id:** `typing.Optional[str]` โ Resolve `_resolvedConfig` from this draft instead of the active version. Sets `_configSource` to `"draft"` in the response.
-
-
-
-
-
--
+**target_organization_id:** `str`
-**version_id:** `typing.Optional[str]` โ Resolve `_resolvedConfig` from this published version instead of the active version. Sets `_configSource` to `"version"` in the response.
+MongoDB ObjectId of the target organization. Must be a 24-character hex string.
+The authenticated user must be a member of this organization.
@@ -1024,7 +795,7 @@ client.atoms.agents.get_agent(
-client.atoms.agents.update_agent(...) -> UpdateAgentAgentsResponse
+client.atoms.agents.get_agent(...) -> GetAgentAgentsResponse
-
@@ -1036,23 +807,34 @@ client.atoms.agents.get_agent(
-
-Update agent fields. Behavior depends on whether the agent has versioning enabled:
+Returns the agent document merged with the resolved config of the active version
+under `_resolvedConfig`. Non-versioned fields (name, telephonyProductId, allowInboundCall,
+etc.) sit at the top level; versioned fields (prompt, tools, language, synthesizer,
+post-call analytics, โฆ) are resolved from the target version and exposed under `_resolvedConfig`.
-**Versioned agents** (have an active published version): only metadata fields are accepted โ
-`name`, `description`, `avatarUrl`, `telephonyProductId`, `allowInboundCall`, `visibleToEveryone`.
-Submitting any config-level field returns 400 with
-`"Agent has versioning enabled. Config changes must be made through drafts."`.
-Use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
+**Previewing a draft or specific version**
-**Non-versioned agents** (no active version): all configuration fields are accepted,
-the same full set as `POST /agent`.
+Pass `?draftId=` to resolve config from a specific draft instead of the active version.
+Pass `?versionId=` to resolve config from a specific published version.
+When either param is used, the response includes `_configSource: "draft" | "version" | "active"`
+indicating which config was resolved.
-**400 is also returned when:**
-- The agent is locked (`"Agent is locked, please unlock it to update"`)
-- Cross-field constraint violated (e.g. `north_indic` language requires `transcriberType: pulse`)
+Notable resolved fields in `_resolvedConfig`:
-**403** is returned when selecting a gated model (`gpt-5.2`, `electron-kogta`, `electron-kogta-v2`)
-without org-level access.
+- `prompt` โ active version's single-prompt text
+- `tools` โ configured tools on the resolved version
+- `postCallAnalyticsConfig` โ disposition metrics + analytics model flags
+- `modelName` โ LLM model name on the resolved version
+- `defaultLanguage`, `supportedLanguages` โ active language config
+- `firstMessage`, `globalPrompt` โ active messaging config
+- `workflowGraph` โ full node graph for `workflow_graph` agents
+
+To read prompt + tools alone, use `GET /agent/{id}/workflow` (deprecated for
+new integrations but still live). To inspect a specific non-active version,
+use `GET /agent/{id}/versions/{versionId}`.
+
+**400 โ also used for "not found":** if the agent ID does not exist in the
+organization, the API returns 400 with `errors: ["No agent found"]` rather than 404.
@@ -1075,7 +857,7 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.update_agent(
+client.atoms.agents.get_agent(
id="id",
)
@@ -1101,39 +883,7 @@ client.atoms.agents.update_agent(
-
-**name:** `typing.Optional[str]` โ Name of the agent.
-
-
-
-
-
--
-
-**description:** `typing.Optional[str]` โ Description of the agent.
-
-
-
-
-
--
-
-**avatar_url:** `typing.Optional[str]` โ URL of the agent's avatar image.
-
-
-
-
-
--
-
-**telephony_product_id:** `typing.Optional[typing.List[str]]` โ IDs of telephony products (phone numbers) to associate with the agent.
-
-
-
-
-
--
-
-**allow_inbound_call:** `typing.Optional[bool]` โ Whether the agent accepts inbound calls.
+**draft_id:** `typing.Optional[str]` โ Resolve `_resolvedConfig` from this draft instead of the active version. Sets `_configSource` to `"draft"` in the response.
@@ -1141,7 +891,7 @@ client.atoms.agents.update_agent(
-
-**visible_to_everyone:** `typing.Optional[bool]` โ Whether the agent is visible to all members of the organization.
+**version_id:** `typing.Optional[str]` โ Resolve `_resolvedConfig` from this published version instead of the active version. Sets `_configSource` to `"version"` in the response.
@@ -1161,7 +911,7 @@ client.atoms.agents.update_agent(
-client.atoms.agents.archive_agent(...) -> ArchiveAgentAgentsResponse
+client.atoms.agents.update_agent(...) -> UpdateAgentAgentsResponse
-
@@ -1173,14 +923,21 @@ client.atoms.agents.update_agent(
-
-Soft-archives the agent โ it is excluded from listings and stops accepting calls,
-but all data is preserved and the operation is fully reversible.
+Update agent fields. Behavior depends on whether the agent has versioning enabled:
-Pass `?on=false` to unarchive (restore) a previously archived agent.
+**Versioned agents** (have an active published version): only metadata fields are accepted โ
+`name`, `description`, `avatarUrl`, `telephonyProductId`, `allowInboundCall`, `visibleToEveryone`.
+Submitting any config-level field returns 400 with
+`"Agent has versioning enabled. Config changes must be made through drafts."`.
+Use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
-**409 is returned when:**
-- The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
-- The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
+**Non-versioned agents** (no active version): all configuration fields are accepted,
+the same full set as `POST /agent`.
+
+**400 is also returned when a cross-field constraint is violated** (for example, `north_indic` language requires `transcriberType: pulse`).
+
+**403** is returned when selecting a gated model (`gpt-5.2`, `electron-kogta`, `electron-kogta-v2`)
+without org-level access.
@@ -1203,7 +960,7 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.archive_agent(
+client.atoms.agents.update_agent(
id="id",
)
@@ -1229,10 +986,7 @@ client.atoms.agents.archive_agent(
-
-**on:** `typing.Optional[bool]`
-
-`true` (default) โ archive the agent.
-`false` โ unarchive (restore) a previously archived agent.
+**name:** `typing.Optional[str]` โ Name of the agent.
@@ -1240,83 +994,31 @@ client.atoms.agents.archive_agent(
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+**description:** `typing.Optional[str]` โ Description of the agent.
-
-
-
-
-
-
-
-
-client.atoms.agents.duplicate_agent(...) -> DuplicateAgentAgentsResponse
-
--
-
-#### ๐ Description
-
-
--
-
-Duplicates a SINGLE_PROMPT agent's live active version into a target organization
-(can also be the same organization). Copies all versioned configuration but strips
-organization-specific resources: knowledge base tools are removed, default variable
-values are blanked, and a new avatar is generated. The duplicate starts with a
-published V1 as its active version.
-
-**400 is returned when:**
-- The source agent is archived (`"Cannot duplicate an archived agent"`)
-- The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
-- The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
-- The agent is not `SINGLE_PROMPT` workflow type
-
-
+**avatar_url:** `typing.Optional[str]` โ URL of the agent's avatar image.
+
-#### ๐ Usage
-
-
--
-
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
-
-client.atoms.agents.duplicate_agent(
- id="id",
- target_organization_id="60d0fe4f5311236168a109ca",
-)
-
-```
-
-
+**telephony_product_id:** `typing.Optional[typing.List[str]]` โ IDs of telephony products (phone numbers) to associate with the agent.
+
-#### โ๏ธ Parameters
-
-
--
-
-
-**id:** `str` โ The ID of the source agent to duplicate
+**allow_inbound_call:** `typing.Optional[bool]` โ Whether the agent accepts inbound calls.
@@ -1324,10 +1026,7 @@ client.atoms.agents.duplicate_agent(
-
-**target_organization_id:** `str`
-
-MongoDB ObjectId of the target organization. Must be a 24-character hex string.
-The authenticated user must be a member of this organization.
+**visible_to_everyone:** `typing.Optional[bool]` โ Whether the agent is visible to all members of the organization.
@@ -1347,7 +1046,7 @@ The authenticated user must be a member of this organization.
-client.atoms.agents.get_agent_workflow(...) -> GetAgentIdWorkflowResponse
+client.atoms.agents.get_agent_widget_config(...) -> GetAgentWidgetConfigResponse
-
@@ -1359,16 +1058,7 @@ The authenticated user must be a member of this organization.
-
-**Deprecated** โ prefer `GET /agent/{id}` (config is resolved into `_resolvedConfig`
-including prompt, tools, and post-call analytics).
-
-Returns the active version's prompt and tools for single-prompt agents, or the
-workflow graph data for workflow_graph agents. Customers still rely on this to
-fetch their current prompt + tools โ endpoint is kept live for now.
-
-**Caveat:** the `versionId` query param (if passed) is silently ignored.
-The response always reflects the currently-active version. To inspect a
-non-active version, use `GET /agent/{id}/versions/{versionId}`.
+Returns the current web widget configuration for the agent. Also includes `assistantId` (same as the agent ID) as a convenience field for the widget embed code.
@@ -1391,7 +1081,7 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.get_agent_workflow(
+client.atoms.agents.get_agent_widget_config(
id="id",
)
@@ -1409,7 +1099,7 @@ client.atoms.agents.get_agent_workflow(
-
-**id:** `str` โ The ID of the agent
+**id:** `str` โ Agent ObjectId
@@ -1429,7 +1119,7 @@ client.atoms.agents.get_agent_workflow(
-client.atoms.agents.update_workflow_configuration(...)
+client.atoms.agents.update_agent_widget_config(...) -> UpdateAgentWidgetConfigResponse
-
@@ -1441,17 +1131,7 @@ client.atoms.agents.get_agent_workflow(
-
-**Deprecated** โ use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
-
-Directly mutates the legacy workflow document for an agent. This write path
-bypasses the versioning system entirely: the change is not captured as a
-new version, and future version activations may overwrite the legacy doc
-back to whatever the version snapshot contains.
-
-โ **Writing here on a versioned agent can silently wipe tools, prompt, or
-other fields that were missing from the PATCH payload.** Only use this if
-you know the agent is not using versioning, or if you are intentionally
-hot-patching the legacy doc.
+Updates the web widget configuration for the agent. Only provided fields are updated (partial update). When `avatarUrl` is changed, the old CDN avatar is automatically deleted from S3. The `avatarUrl` must be a URL from the platform's CDN domain โ use `POST /agent/{id}/avatar/presigned-url` to upload first.
@@ -1474,9 +1154,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.update_workflow_configuration(
- id="60d0fe4f5311236168a109ca",
- type="workflow_graph",
+client.atoms.agents.update_agent_widget_config(
+ id="id",
)
```
@@ -1493,23 +1172,7 @@ client.atoms.agents.update_workflow_configuration(
-
-**id:** `str` โ The workflow ID (found at `agent.workflowId` on the agent document).
-
-
-
-
-
--
-
-**type:** `WorkflowType`
-
-
-
-
-
--
-
-**workflow_graph:** `typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph]` โ Required when `type = workflow_graph`. Exactly one of `workflowGraph` or `singlePromptConfig` must be provided.
+**id:** `str` โ Agent ObjectId
@@ -1517,7 +1180,7 @@ client.atoms.agents.update_workflow_configuration(
-
-**single_prompt_config:** `typing.Optional[SinglePromptConfig]`
+**widget_config:** `typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig]` โ All fields are optional โ only provided fields are updated
@@ -1537,7 +1200,7 @@ client.atoms.agents.update_workflow_configuration(
-client.atoms.agents.create_with_ai(...) -> CreateWithAiAgentsResponse
+client.atoms.agents.get_agent_avatar_presigned_url(...) -> GetAgentAvatarPresignedUrlResponse
-
@@ -1549,12 +1212,7 @@ client.atoms.agents.update_workflow_configuration(
-
-Create a new single-prompt agent from a natural-language brief or structured
-question/answer pairs. Atoms generates the system prompt for you.
-
-Provide either `description` (free-form brief) or a non-empty `questions` array,
-but not both. The `emotiveToggle`, `voiceId`, and `voiceModel` fields must be
-supplied as a 3-tuple or omitted entirely.
+Generates a pre-signed S3 upload URL for the agent's widget avatar image. Upload the image directly to S3 using the returned `presignedUrl`, then save `cdnUrl` as the agent's avatar via `PATCH /agent/{id}/widget-config`.
@@ -1577,7 +1235,12 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.create_with_ai()
+client.atoms.agents.get_agent_avatar_presigned_url(
+ id="id",
+ file_name="fileName",
+ content_type="contentType",
+ file_size=1.1,
+)
```
@@ -1593,45 +1256,7 @@ client.atoms.agents.create_with_ai()
-
-**name:** `typing.Optional[str]` โ Agent name (trimmed). Auto-generated when omitted.
-
-
-
-
-
--
-
-**description:** `typing.Optional[str]`
-
-Free-form natural-language description of what the agent should do.
-Atoms turns this into the system prompt. Use this OR `questions`, not both.
-
-
-
-
-
--
-
-**questions:** `typing.Optional[typing.List[CreateWithAiAgentsRequestQuestionsItem]]`
-
-Structured question/answer pairs. Atoms uses these to compose the
-system prompt. Use this OR `description`, not both.
-
-
-
-
-
--
-
-**type:** `typing.Optional[CreateWithAiAgentsRequestType]` โ Currently the only supported agent type.
-
-
-
-
-
--
-
-**emotive_toggle:** `typing.Optional[bool]` โ Enable emotive synthesis. Must be paired with `voiceId` + `voiceModel`.
+**id:** `str` โ Agent ObjectId
@@ -1639,7 +1264,7 @@ system prompt. Use this OR `description`, not both.
-
-**voice_id:** `typing.Optional[str]` โ Voice ID for synthesis. Must be paired with `emotiveToggle` + `voiceModel`.
+**file_name:** `str` โ Original file name (used to construct the S3 key)
@@ -1647,7 +1272,7 @@ system prompt. Use this OR `description`, not both.
-
-**voice_model:** `typing.Optional[CreateWithAiAgentsRequestVoiceModel]` โ Synthesizer to use. Must be paired with `emotiveToggle` + `voiceId`.
+**content_type:** `str` โ MIME type โ must start with `image/`
@@ -1655,7 +1280,7 @@ system prompt. Use this OR `description`, not both.
-
-**knowledge_base_id:** `typing.Optional[str]` โ Optional knowledge-base ID to attach to the new agent.
+**file_size:** `float` โ File size in bytes โ must be > 0 and โค 2 MB (2,097,152 bytes)
@@ -1675,7 +1300,7 @@ system prompt. Use this OR `description`, not both.
-client.atoms.agents.list_call_logs(...) -> ListCallLogsAgentsResponse
+client.atoms.agents.get_agent_call_logs(...) -> GetAgentCallLogsResponse
-
@@ -1687,9 +1312,7 @@ system prompt. Use this OR `description`, not both.
-
-Returns paginated conversation logs (calls) for a specific agent in the caller's
-organization. Use `GET /conversation` for cross-agent log listing; use this when
-you already have an agent ID.
+Returns paginated call logs for a specific agent.
@@ -1712,8 +1335,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.list_call_logs(
- id="60d0fe4f5311236168a109ca",
+client.atoms.agents.get_agent_call_logs(
+ id="id",
)
```
@@ -1730,7 +1353,7 @@ client.atoms.agents.list_call_logs(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ Agent ObjectId
@@ -1738,7 +1361,7 @@ client.atoms.agents.list_call_logs(
-
-**page:** `typing.Optional[str]` โ Page number (string-encoded positive integer).
+**page:** `typing.Optional[int]` โ Page number (default 1)
@@ -1746,7 +1369,7 @@ client.atoms.agents.list_call_logs(
-
-**offset:** `typing.Optional[str]` โ Page size (string-encoded positive integer).
+**offset:** `typing.Optional[int]` โ Records per page (default 10)
@@ -1766,7 +1389,7 @@ client.atoms.agents.list_call_logs(
-client.atoms.agents.get_widget_config(...) -> GetWidgetConfigAgentsResponse
+client.atoms.agents.archive_agent(...) -> ArchiveAgentAgentsResponse
-
@@ -1778,9 +1401,14 @@ client.atoms.agents.list_call_logs(
-
-Returns the embeddable web widget configuration for the agent (theme, copy,
-consent prompts, branding, voice/chat mode, allowlist). The response merges the
-stored config with `assistantId: ` injected.
+Soft-archives the agent โ it is excluded from listings and stops accepting calls,
+but all data is preserved and the operation is fully reversible.
+
+Pass `?on=false` to unarchive (restore) a previously archived agent.
+
+**409 is returned when:**
+- The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
+- The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
@@ -1803,8 +1431,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.get_widget_config(
- id="60d0fe4f5311236168a109ca",
+client.atoms.agents.archive_agent(
+ id="id",
)
```
@@ -1821,7 +1449,18 @@ client.atoms.agents.get_widget_config(
-
-**id:** `str` โ The agent ID
+**id:** `str`
+
+
+
+
+
+-
+
+**on:** `typing.Optional[bool]`
+
+`true` (default) โ archive the agent.
+`false` โ unarchive (restore) a previously archived agent.
@@ -1841,7 +1480,8 @@ client.atoms.agents.get_widget_config(
-client.atoms.agents.update_widget_config(...) -> UpdateWidgetConfigAgentsResponse
+## Atoms Realtime
+client.atoms.realtime.register_call(...) -> RegisterCallRealtimeResponse
-
@@ -1853,9 +1493,24 @@ client.atoms.agents.get_widget_config(
-
-Merge updates into the agent's embeddable widget config. Only the fields in the
-request body are overwritten; everything else is preserved. Returns the full
-widget config after merge.
+Mint a **short-lived, single-use access token** for opening a realtime
+[Agent WebSocket](/atoms/api-reference/realtime-agent/realtime-agent)
+connection. This is the **recommended** way to start a session from a
+browser or other client-side app: your API key stays server-side, and
+the browser only ever sees the short-lived token. (Server-side or
+trusted clients may instead connect to the WebSocket with a raw API key
+directly.)
+
+Flow:
+1. Call this endpoint with your API key and the `agent_id` (plus optional
+ `mode` and per-call `variables`). All session configuration is fixed
+ here โ it is baked into the returned token.
+2. Open a WebSocket to `wss://api.smallest.ai/atoms/v1/agent/connect?token=`.
+ No `agent_id`, `mode`, or `variables` query params are needed on the
+ WebSocket โ they come from the token.
+
+The token is valid for `expires_in` seconds (30) and can be used for a
+single connection. Request a fresh token for each connection.
@@ -1872,16 +1527,14 @@ widget config after merge.
```python
from smallestai import SmallestAI
from smallestai.environment import SmallestAIEnvironment
-from smallestai.atoms import WidgetConfig
client = SmallestAI(
api_key="",
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agents.update_widget_config(
- id="60d0fe4f5311236168a109ca",
- widget_config=WidgetConfig(),
+client.atoms.realtime.register_call(
+ agent_id="69da0b4c20c0e03cfa4ee258",
)
```
@@ -1898,7 +1551,18 @@ client.atoms.agents.update_widget_config(
-
-**id:** `str` โ The agent ID
+**agent_id:** `str` โ The Atoms agent to connect to.
+
+
+
+
+
+-
+
+**mode:** `typing.Optional[RegisterCallRealtimeRequestMode]`
+
+Session mode. `webcall` = full voice pipeline (audio in +
+audio out). `chat` = text-only pipeline. Defaults to `webcall`.
@@ -1906,7 +1570,16 @@ client.atoms.agents.update_widget_config(
-
-**widget_config:** `WidgetConfig`
+**variables:** `typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]]`
+
+Per-call prompt variables that override the agent's
+`defaultVariables` for this session only. Values must be
+`string`, `number`, or `boolean`. Reserved system-variable
+keys (`call_id`, `conversation_type`, `agent_number`,
+`user_number`, `current_date`, `current_time`, `current_day`,
+`agent_gender`, `default_language`, `supported_languages`,
+`timezone`) are populated by the server and stripped if
+supplied.
@@ -1926,75 +1599,8 @@ client.atoms.agents.update_widget_config(
-client.atoms.agents.get_prompt_config() -> GetPromptConfigAgentsResponse
-
--
-
-#### ๐ Description
-
-
--
-
-
--
-
-Returns the canonical question definitions, option choices, and example labels
-used by the agent-builder UI when collecting input for `POST /agent/with-ai`.
-
-Use this to programmatically discover what questions to ask end-users when
-building agent-creation UIs.
-
-
-
-
-
-#### ๐ Usage
-
-
--
-
-
--
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
-
-client.atoms.agents.get_prompt_config()
-
-```
-
-
-
-
-
-#### โ๏ธ Parameters
-
-
--
-
-
--
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
-
-
-
-
-
-
-
-
-
-
-
-## Atoms Calls
-client.atoms.calls.list(...) -> ListCallsResponse
+## Atoms Calls
+client.atoms.calls.list(...) -> ListCallsResponse
-
@@ -3517,84 +3123,6 @@ client.atoms.campaigns.pause(
-
-
-
-
-client.atoms.campaigns.export_logs(...) -> typing.List[ExportLogsCampaignsResponseItem]
-
--
-
-#### ๐ Description
-
-
--
-
-
--
-
-Streams a JSON file containing every call log for a campaign.
-
-Response is a **file download** (`Content-Disposition: attachment`), not the
-standard `{status, data}` envelope. Body is a raw JSON array of log objects.
-When the relay-service is configured and reachable, each row also includes an
-`events` array; otherwise the field is omitted.
-
-
-
-
-
-#### ๐ Usage
-
-
--
-
-
--
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
-
-client.atoms.campaigns.export_logs(
- id="id",
-)
-
-```
-
-
-
-
-
-#### โ๏ธ Parameters
-
-
--
-
-
--
-
-**id:** `str` โ Campaign ID.
-
-
-
-
-
--
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
-
-
-
-
-
-
-
@@ -5395,7 +4923,7 @@ client = SmallestAI(
client.atoms.phone_numbers.import_sip(
phone_number="+14155551234",
- sip_termination_url="sip:trunk.your-provider.com",
+ sip_termination_url="trunk.your-provider.com",
name="Main Support Line",
sip_username="",
sip_password="",
@@ -5423,7 +4951,7 @@ client.atoms.phone_numbers.import_sip(
-
-**sip_termination_url:** `str` โ The SIP URI where calls should be routed to your infrastructure
+**sip_termination_url:** `str` โ Your SIP provider's termination host โ a hostname or IP address, optionally with a port (e.g. "sip.your-provider.com:5060"). Full SIP URIs ("sip:" / "sips:") are also accepted and automatically normalized to the bare host.
@@ -7178,6 +6706,8 @@ client.atoms.audience.search_audience_members(
-
+**Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches (`openDraftId` / `hasOpenDraft`)`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
List all active (non-discarded) drafts for an agent.
@@ -7202,7 +6732,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.list_active_drafts(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
```
@@ -7219,7 +6749,7 @@ client.atoms.agent_versioning_drafts.list_active_drafts(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7251,6 +6781,8 @@ client.atoms.agent_versioning_drafts.list_active_drafts(
-
+**Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Create a new draft from an existing published version or another draft. At least one of sourceVersionId or sourceDraftId is required (both may be sent simultaneously).
@@ -7275,7 +6807,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.create_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
```
@@ -7292,7 +6824,7 @@ client.atoms.agent_versioning_drafts.create_draft(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7351,6 +6883,8 @@ Sending a non-ObjectId format returns 400.
-
+**Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `draftId` remains resolvable. Migrate to `GET /agent/{id}/branches/{branchId}/draft`. Will be removed at the sunset date.
+
Returns the latest revision of a draft along with its edit history.
@@ -7375,7 +6909,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.get_draft_detail(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -7393,7 +6927,7 @@ client.atoms.agent_versioning_drafts.get_draft_detail(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7441,6 +6975,8 @@ client.atoms.agent_versioning_drafts.get_draft_detail(
-
+**Deprecated on the v2 branch model.** Migrate to `DELETE /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Discard (soft-delete) a draft. Only the draft creator or an admin can discard.
@@ -7465,7 +7001,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.discard_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -7483,7 +7019,7 @@ client.atoms.agent_versioning_drafts.discard_draft(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7523,6 +7059,9 @@ client.atoms.agent_versioning_drafts.discard_draft(
-
+**Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+|
Rename a draft. For config changes, use PATCH /agent/{id}/drafts/{draftId}/config instead.
@@ -7547,7 +7086,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.rename_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
draft_name="draftName",
)
@@ -7566,7 +7105,7 @@ client.atoms.agent_versioning_drafts.rename_draft(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7614,6 +7153,8 @@ client.atoms.agent_versioning_drafts.rename_draft(
-
+**Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=:draft&b=`. Will be removed at the sunset date.
+
Compare a draft against its source version or another specified version.
@@ -7638,7 +7179,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.get_draft_diff(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -7656,7 +7197,7 @@ client.atoms.agent_versioning_drafts.get_draft_diff(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7704,6 +7245,8 @@ client.atoms.agent_versioning_drafts.get_draft_diff(
-
+**Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/draft/publish`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Publish a draft as a new versioned release. Optionally activate it immediately.
@@ -7728,7 +7271,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.publish_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -7746,7 +7289,7 @@ client.atoms.agent_versioning_drafts.publish_draft(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7810,6 +7353,8 @@ client.atoms.agent_versioning_drafts.publish_draft(
-
+**Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `includeDraft: true`. Will be removed at the sunset date.
+
Initiate a test call using the draft's resolved configuration.
@@ -7834,7 +7379,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.test_call_with_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -7852,7 +7397,7 @@ client.atoms.agent_versioning_drafts.test_call_with_draft_config(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -7900,6 +7445,9 @@ client.atoms.agent_versioning_drafts.test_call_with_draft_config(
-
+**Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+|
Update the configuration of a draft. This single endpoint is how every
agent-level config field is changed: prompt, tools, voice, language,
**post-call analytics (disposition metrics)**, and more. There is no
@@ -7952,7 +7500,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_drafts.update_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -7970,7 +7518,7 @@ client.atoms.agent_versioning_drafts.update_draft_config(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -8259,6 +7807,9 @@ client.atoms.agent_versioning_drafts.update_draft_config(
-
+**Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches/{branchId}/revisions`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+|
List published versions for an agent with pagination and optional pin filter.
The `total` value currently represents the total number of published versions
for the agent, not necessarily the filtered count when `isPinned` is used.
@@ -8285,7 +7836,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_versions.list_published_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
```
@@ -8302,7 +7853,7 @@ client.atoms.agent_versioning_versions.list_published_versions(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -8358,6 +7909,8 @@ client.atoms.agent_versioning_versions.list_published_versions(
-
+**Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=&b=`. Will be removed at the sunset date.
+
Compare two version or draft revision records side-by-side by their IDs. The implementation tries published versions first and can fall back to the latest draft revision.
@@ -8382,7 +7935,7 @@ client = SmallestAI(
)
client.atoms.agent_versioning_versions.diff_two_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_a="versionA",
version_b="versionB",
)
@@ -8401,7 +7954,7 @@ client.atoms.agent_versioning_versions.diff_two_versions(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -8437,7 +7990,7 @@ client.atoms.agent_versioning_versions.diff_two_versions(
-client.atoms.agent_versioning_versions.compare_version_metrics(...) -> CompareVersionMetricsAgentVersioningVersionsResponse
+client.atoms.agent_versioning_versions.get_version_detail(...) -> GetAgentIdVersionsVersionIdResponse
-
@@ -8449,7 +8002,12 @@ client.atoms.agent_versioning_versions.diff_two_versions(
-
-Compare analytics/call metrics between two published versions over an optional date range.
+**Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `versionId` equals its migrated `revisionId`, so this continues to resolve across branches. Migrate to `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}`. Will be removed at the sunset date.
+
+|
+Returns the full detail of a specific published version (read-only).
+Published versions are config-immutable โ to modify config, create a draft from
+this version and publish it as a new version.
@@ -8466,19 +8024,15 @@ Compare analytics/call metrics between two published versions over an optional d
```python
from smallestai import SmallestAI
from smallestai.environment import SmallestAIEnvironment
-import datetime
client = SmallestAI(
api_key="",
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agent_versioning_versions.compare_version_metrics(
- id="60d0fe4f5311236168a109ca",
- version_a="versionA",
- version_b="versionB",
- date_from=datetime.date.fromisoformat("2026-05-01"),
- date_to=datetime.date.fromisoformat("2026-05-31"),
+client.atoms.agent_versioning_versions.get_version_detail(
+ id="id",
+ version_id="versionId",
)
```
@@ -8495,31 +8049,7 @@ client.atoms.agent_versioning_versions.compare_version_metrics(
-
-**id:** `str` โ The agent ID
-
-
-
-
-
--
-
-**version_a:** `str` โ ID of the first version
-
-
-
-
-
--
-
-**version_b:** `str` โ ID of the second version
-
-
-
-
-
--
-
-**date_from:** `typing.Optional[datetime.date]` โ Start date for the comparison range in YYYY-MM-DD format.
+**id:** `str` โ The agent ID.
@@ -8527,7 +8057,7 @@ client.atoms.agent_versioning_versions.compare_version_metrics(
-
-**date_to:** `typing.Optional[datetime.date]` โ End date for the comparison range in YYYY-MM-DD format.
+**version_id:** `str` โ The published version ID
@@ -8547,7 +8077,7 @@ client.atoms.agent_versioning_versions.compare_version_metrics(
-client.atoms.agent_versioning_versions.get_version_detail(...) -> GetAgentIdVersionsVersionIdResponse
+client.atoms.agent_versioning_versions.update_version_metadata(...) -> UpdateVersionMetadataAgentVersioningVersionsResponse
-
@@ -8559,9 +8089,13 @@ client.atoms.agent_versioning_versions.compare_version_metrics(
-
-Returns the full detail of a specific published version (read-only).
-Published versions are config-immutable โ to modify config, create a draft from
-this version and publish it as a new version.
+**Deprecated on the v2 branch model.** Migrate to `Metadata edits removed on v2. Set `label` at publish via `POST /agent/{id}/branches/{branchId}/draft/publish`.`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+|
+Update a published version's label, description, or pinned status. At least one field is required.
+Published versions (both active and inactive) are config-immutable โ their agent
+configuration cannot be changed. To modify config, create a new draft from the version,
+edit the draft, and publish it as a new version.
@@ -8584,8 +8118,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agent_versioning_versions.get_version_detail(
- id="60d0fe4f5311236168a109ca",
+client.atoms.agent_versioning_versions.update_version_metadata(
+ id="id",
version_id="versionId",
)
@@ -8603,7 +8137,7 @@ client.atoms.agent_versioning_versions.get_version_detail(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -8619,6 +8153,30 @@ client.atoms.agent_versioning_versions.get_version_detail(
-
+**label:** `typing.Optional[str]` โ Version label
+
+
+
+
+
+-
+
+**description:** `typing.Optional[str]` โ Version description
+
+
+
+
+
+-
+
+**is_pinned:** `typing.Optional[bool]` โ Pin or unpin the version
+
+
+
+
+
+-
+
**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
@@ -8631,7 +8189,7 @@ client.atoms.agent_versioning_versions.get_version_detail(
-client.atoms.agent_versioning_versions.update_version_metadata(...) -> UpdateVersionMetadataAgentVersioningVersionsResponse
+client.atoms.agent_versioning_versions.activate_version(...) -> ActivateVersionAgentVersioningVersionsResponse
-
@@ -8643,10 +8201,14 @@ client.atoms.agent_versioning_versions.get_version_detail(
-
-Update a published version's label, description, or pinned status. At least one field is required.
-Published versions (both active and inactive) are config-immutable โ their agent
-configuration cannot be changed. To modify config, create a new draft from the version,
-edit the draft, and publish it as a new version.
+**Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/live or POST /agent/{id}/branches/{branchId}/revisions/{revisionId}/restore`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+|
+Set a published version as the active version for the agent. The previously
+active version is deactivated. This does not modify the version's config โ it
+only changes which version serves live traffic. Activation is idempotent: if
+the version is already active, the endpoint returns that version without
+changing config.
@@ -8669,8 +8231,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agent_versioning_versions.update_version_metadata(
- id="60d0fe4f5311236168a109ca",
+client.atoms.agent_versioning_versions.activate_version(
+ id="id",
version_id="versionId",
)
@@ -8688,7 +8250,7 @@ client.atoms.agent_versioning_versions.update_version_metadata(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -8704,30 +8266,6 @@ client.atoms.agent_versioning_versions.update_version_metadata(
-
-**label:** `typing.Optional[str]` โ Version label
-
-
-
-
-
--
-
-**description:** `typing.Optional[str]` โ Version description
-
-
-
-
-
--
-
-**is_pinned:** `typing.Optional[bool]` โ Pin or unpin the version
-
-
-
-
-
--
-
**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
@@ -8740,7 +8278,7 @@ client.atoms.agent_versioning_versions.update_version_metadata(
-client.atoms.agent_versioning_versions.activate_version(...) -> ActivateVersionAgentVersioningVersionsResponse
+client.atoms.agent_versioning_versions.test_call_with_version_config(...) -> PostAgentIdVersionsVersionIdTestCallResponse
-
@@ -8752,11 +8290,13 @@ client.atoms.agent_versioning_versions.update_version_metadata(
-
-Set a published version as the active version for the agent. The previously
-active version is deactivated. This does not modify the version's config โ it
-only changes which version serves live traffic. Activation is idempotent: if
-the version is already active, the endpoint returns that version without
-changing config.
+**Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `revisionId`. Will be removed at the sunset date.
+
+|
+Initiate a test call using a specific published version's configuration.
+The response always includes `conversationId` and `callId`. For `webcall`
+and `chat`, it also includes `token`, `roomName`, and `host`. Those fields
+are omitted for `telephony`.
@@ -8779,8 +8319,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agent_versioning_versions.activate_version(
- id="60d0fe4f5311236168a109ca",
+client.atoms.agent_versioning_versions.test_call_with_version_config(
+ id="id",
version_id="versionId",
)
@@ -8798,7 +8338,7 @@ client.atoms.agent_versioning_versions.activate_version(
-
-**id:** `str` โ The agent ID
+**id:** `str` โ The agent ID.
@@ -8814,6 +8354,14 @@ client.atoms.agent_versioning_versions.activate_version(
-
+**request:** `TestCallRequest`
+
+
+
+
+
+-
+
**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
@@ -8826,7 +8374,8 @@ client.atoms.agent_versioning_versions.activate_version(
-client.atoms.agent_versioning_versions.test_call_with_version_config(...) -> PostAgentIdVersionsVersionIdTestCallResponse
+## Atoms AgentVersioningBranches
+client.atoms.agent_versioning_branches.list(...) -> ListAgentVersioningBranchesResponse
-
@@ -8838,10 +8387,7 @@ client.atoms.agent_versioning_versions.activate_version(
-
-Initiate a test call using a specific published version's configuration.
-The response always includes `conversationId` and `callId`. For `webcall`
-and `chat`, it also includes `token`, `roomName`, and `host`. Those fields
-are omitted for `telephony`.
+List all non-archived branches for an agent, with per-branch draft and revision counts.
@@ -8864,9 +8410,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.agent_versioning_versions.test_call_with_version_config(
- id="60d0fe4f5311236168a109ca",
- version_id="versionId",
+client.atoms.agent_versioning_branches.list(
+ id="id",
)
```
@@ -8883,23 +8428,7 @@ client.atoms.agent_versioning_versions.test_call_with_version_config(
-
-**id:** `str` โ The agent ID
-
-
-
-
-
--
-
-**version_id:** `str` โ The published version ID
-
-
-
-
-
--
-
-**request:** `TestCallRequest`
+**id:** `str` โ The agent ID.
@@ -8919,8 +8448,7 @@ client.atoms.agent_versioning_versions.test_call_with_version_config(
-## Atoms PromptScoring
-client.atoms.prompt_scoring.score_a_prompt(...) -> PostPromptScoringScoreResponse
+client.atoms.agent_versioning_branches.create_branch(...) -> CreateBranchAgentVersioningBranchesResponse
-
@@ -8932,33 +8460,7 @@ client.atoms.agent_versioning_versions.test_call_with_version_config(
-
-Scores an agent's prompt across 11 quality dimensions using Gemini-based analysis. Requires the prompt to have changed since the last scoring.
-
-**Input:** Provide exactly one of `versionId` (published agent version) or `draftId` (agent draft). Providing both or neither returns a 400.
-
-**Credit usage:** 1 credit is deducted per successful call.
-
-**Idempotency:** Re-submitting the same prompt without changes returns a 400 โ retrieve the cached score via the GET agent endpoint instead.
-
-**Supported agent types:** Only `single_prompt` agents are supported. Workflow-graph agents return a 400.
-
-**Scoring model:** Two sequential Gemini calls โ a Platform Analyst pass followed by a Rubric Judge pass.
-
-### Scored Dimensions
-
-| Tier | Dimension | Notes |
-|------|-----------|-------|
-| 1 | Role & Objective | |
-| 1 | Personality & Voice | |
-| 1 | Conversation Structure | |
-| 1 | Tool Integration | |
-| 1 | Constraints & Safety | |
-| 2 | Conversational Naturalness | |
-| 2 | Failure-Mode Coverage | |
-| 3 | Information Integrity | Gating โ if Weak/Missing, score capped at 70 |
-| 3 | Variable & Tool Hygiene | Gating โ if Weak/Missing, score capped at 50 |
-| 3 | Internal Consistency | |
-| 3 | Density | Computed from token analysis |
+Fork a new branch from an existing branch. The source branch must have at least one committed revision. Branch names are unique per agent; the name `Main` is reserved for the default branch. Creating from a branch whose latest draft is still `scanning` returns `409 source_scanning`.
@@ -8975,17 +8477,16 @@ Scores an agent's prompt across 11 quality dimensions using Gemini-based analysi
```python
from smallestai import SmallestAI
from smallestai.environment import SmallestAIEnvironment
-from smallestai.atoms.prompt_scoring import PostPromptScoringScoreRequestVersionId
client = SmallestAI(
api_key="",
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.prompt_scoring.score_a_prompt(
- request=PostPromptScoringScoreRequestVersionId(
- version_id="6a1589b75e048394eb37bc47",
- ),
+client.atoms.agent_versioning_branches.create_branch(
+ id="id",
+ source_branch_id="sourceBranchId",
+ name="name",
)
```
@@ -9002,7 +8503,23 @@ client.atoms.prompt_scoring.score_a_prompt(
-
-**request:** `PostPromptScoringScoreRequest` โ Exactly one of `versionId` or `draftId` must be provided.
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**source_branch_id:** `str` โ Branch to fork from. Its head revision must exist.
+
+
+
+
+
+-
+
+**name:** `str` โ New branch name. Unique per agent among active branches. `main` is reserved.
@@ -9022,8 +8539,7 @@ client.atoms.prompt_scoring.score_a_prompt(
-## Atoms Dnc
-client.atoms.dnc.list(...) -> ListDncResponse
+client.atoms.agent_versioning_branches.get(...) -> GetAgentVersioningBranchesResponse
-
@@ -9035,11 +8551,7 @@ client.atoms.prompt_scoring.score_a_prompt(
-
-Lists Do-Not-Call entries for the caller's organization with pagination, search,
-and sort. Optionally scope to a single agent via `agentId`.
-
-Each entry records a phone number that was flagged (via call outcome or manual
-upload) as not-to-be-called for either the org or a specific agent.
+Return a single branch summary, including draft state (`openDraftId`, `hasOpenDraft`) and head revision.
@@ -9062,7 +8574,10 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.atoms.dnc.list()
+client.atoms.agent_versioning_branches.get(
+ id="id",
+ branch_id="branchId",
+)
```
@@ -9078,43 +8593,7 @@ client.atoms.dnc.list()
-
-**agent_id:** `typing.Optional[str]`
-
-Optional 24-character hex agent ID. When present, restricts results to entries
-for this agent. Returns 400/404 if the ID isn't valid or doesn't belong to
-the caller's org.
-
-
-
-
-
--
-
-**search:** `typing.Optional[str]` โ Free-text search across phone numbers.
-
-
-
-
-
--
-
-**sort_field:** `typing.Optional[ListDncRequestSortField]`
-
-
-
-
-
--
-
-**sort_order:** `typing.Optional[ListDncRequestSortOrder]`
-
-
-
-
-
--
-
-**page:** `typing.Optional[str]` โ Page number (string-encoded positive integer, โฅ 1).
+**id:** `str` โ The agent ID.
@@ -9122,7 +8601,7 @@ the caller's org.
-
-**offset:** `typing.Optional[str]` โ Page size (string-encoded; server clamps to 1โ500).
+**branch_id:** `str` โ The branch ID.
@@ -9142,8 +8621,7 @@ the caller's org.
-## Waves
-client.waves.get_pronunciation_dicts() -> typing.List[PronunciationDict]
+client.atoms.agent_versioning_branches.rename(...) -> RenameAgentVersioningBranchesResponse
-
@@ -9155,7 +8633,7 @@ the caller's org.
-
-Retrieve all pronunciation dictionaries for the authenticated user
+Rename a non-default, non-archived branch. `Main` cannot be renamed; a name that is already in use on this agent returns `409 branch_name_exists`.
@@ -9178,7 +8656,11 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.get_pronunciation_dicts()
+client.atoms.agent_versioning_branches.rename(
+ id="id",
+ branch_id="branchId",
+ name="name",
+)
```
@@ -9194,6 +8676,30 @@ client.waves.get_pronunciation_dicts()
-
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
+
+
+
+
+
+-
+
+**name:** `str`
+
+
+
+
+
+-
+
**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
@@ -9206,7 +8712,7 @@ client.waves.get_pronunciation_dicts()
-client.waves.create_pronunciation_dict(...) -> PronunciationDict
+client.atoms.agent_versioning_branches.archive(...) -> ArchiveAgentVersioningBranchesResponse
-
@@ -9218,7 +8724,7 @@ client.waves.get_pronunciation_dicts()
-
-Create a new pronunciation dictionary for the authenticated user
+Archive a branch. `Main` cannot be archived. The live branch cannot be archived; make another branch live first. Archived branches are hidden from list views but their revisions remain queryable by ID.
@@ -9235,20 +8741,15 @@ Create a new pronunciation dictionary for the authenticated user
```python
from smallestai import SmallestAI
from smallestai.environment import SmallestAIEnvironment
-from smallestai.waves import PronunciationItem
client = SmallestAI(
api_key="",
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.create_pronunciation_dict(
- items=[
- PronunciationItem(
- word="mysql",
- pronunciation="my-sequel",
- )
- ],
+client.atoms.agent_versioning_branches.archive(
+ id="id",
+ branch_id="branchId",
)
```
@@ -9265,7 +8766,15 @@ client.waves.create_pronunciation_dict(
-
-**items:** `typing.List[PronunciationItem]` โ List of word-pronunciation pairs to create
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
@@ -9285,7 +8794,7 @@ client.waves.create_pronunciation_dict(
-client.waves.update_pronunciation_dict(...) -> UpdatePronunciationDictResponse
+client.atoms.agent_versioning_branches.make_live(...) -> MakeLiveAgentVersioningBranchesResponse
-
@@ -9297,7 +8806,7 @@ client.waves.create_pronunciation_dict(
-
-Update an existing pronunciation dictionary for the authenticated user
+Make this branch the live branch. Its `headRevisionId` becomes the config that serves production traffic. The previously-live branch becomes not-live automatically. The branch must be non-archived, must have at least one `committed` (security-passed) revision, and its head revision must have passed the security scan.
@@ -9314,21 +8823,15 @@ Update an existing pronunciation dictionary for the authenticated user
```python
from smallestai import SmallestAI
from smallestai.environment import SmallestAIEnvironment
-from smallestai.waves import PronunciationItem
client = SmallestAI(
api_key="",
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.update_pronunciation_dict(
- id="64f1234567890abcdef12345",
- items=[
- PronunciationItem(
- word="mysql",
- pronunciation="my-sequel",
- )
- ],
+client.atoms.agent_versioning_branches.make_live(
+ id="id",
+ branch_id="branchId",
)
```
@@ -9345,7 +8848,7 @@ client.waves.update_pronunciation_dict(
-
-**id:** `str` โ ID of the pronunciation dictionary to update
+**id:** `str` โ The agent ID.
@@ -9353,7 +8856,7 @@ client.waves.update_pronunciation_dict(
-
-**items:** `typing.List[PronunciationItem]` โ Updated list of word-pronunciation pairs
+**branch_id:** `str` โ The branch ID.
@@ -9373,7 +8876,7 @@ client.waves.update_pronunciation_dict(
-client.waves.delete_pronunciation_dict(...) -> DeletePronunciationDictResponse
+client.atoms.agent_versioning_branches.get_draft(...) -> GetDraftAgentVersioningBranchesResponse
-
@@ -9385,7 +8888,7 @@ client.waves.update_pronunciation_dict(
-
-Delete an existing pronunciation dictionary for the authenticated user
+Return the currently-open draft on this branch, including per-edit history.
@@ -9408,8 +8911,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.delete_pronunciation_dict(
- id="64f1234567890abcdef12345",
+client.atoms.agent_versioning_branches.get_draft(
+ id="id",
+ branch_id="branchId",
)
```
@@ -9426,7 +8930,15 @@ client.waves.delete_pronunciation_dict(
-
-**id:** `str` โ ID of the pronunciation dictionary to delete
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
@@ -9446,7 +8958,7 @@ client.waves.delete_pronunciation_dict(
-client.waves.synthesize_lightning(...) -> typing.Iterator[bytes]
+client.atoms.agent_versioning_branches.update_draft(...) -> UpdateDraftAgentVersioningBranchesResponse
-
@@ -9458,7 +8970,7 @@ client.waves.delete_pronunciation_dict(
-
-Get speech for given text using the Waves API
+Upsert the open draft on this branch. If no draft is open, one is created automatically. The request body is a free-form agent config partial and must contain at least one recognized field (`prompt`, `voice`, `model`, `tools`, `post_call_analytics`, etc.); the server merges it into the existing draft and returns the resulting draft as a revision-shaped snapshot.
@@ -9481,9 +8993,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.synthesize_lightning(
- text="text",
- voice_id="voice_id",
+client.atoms.agent_versioning_branches.update_draft(
+ id="id",
+ branch_id="branchId",
)
```
@@ -9500,7 +9012,7 @@ client.waves.synthesize_lightning(
-
-**text:** `str` โ The text to convert to speech.
+**id:** `str` โ The agent ID.
@@ -9508,7 +9020,7 @@ client.waves.synthesize_lightning(
-
-**voice_id:** `str` โ The voice identifier to use for speech generation.
+**branch_id:** `str` โ The branch ID.
@@ -9516,7 +9028,7 @@ client.waves.synthesize_lightning(
-
-**sample_rate:** `typing.Optional[int]` โ The sample rate for the generated audio.
+**expected_revision:** `typing.Optional[int]` โ Optimistic-concurrency control. The `draftRevision` the client's edit was based on. When present, the server runs a field-level conflict check and rejects with `409 DraftConflictError` if the same field was changed by another edit since. Omit for last-write-wins semantics (which is also how a client force-overwrites after a `409`). Referencing a non-existent base revision returns `409 { errors: ["base_revision_unavailable"] }`.
@@ -9524,7 +9036,7 @@ client.waves.synthesize_lightning(
-
-**speed:** `typing.Optional[float]` โ The speed of the generated speech.
+**global_prompt:** `typing.Optional[str]` โ Top-level system prompt shown to the agent every turn.
@@ -9532,7 +9044,7 @@ client.waves.synthesize_lightning(
-
-**language:** `typing.Optional[LightningRequestLanguage]` โ Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
+**first_message:** `typing.Optional[str]` โ The agent's opening line at call start.
@@ -9540,7 +9052,7 @@ client.waves.synthesize_lightning(
-
-**output_format:** `typing.Optional[LightningRequestOutputFormat]` โ The format of the output audio.
+**slm_model:** `typing.Optional[UpdateBranchDraftRequestSlmModel]` โ LLM model powering the agent. See `CreateAgentRequest.slmModel` for org-level access notes.
@@ -9548,73 +9060,71 @@ client.waves.synthesize_lightning(
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+**background_sound:** `typing.Optional[UpdateBranchDraftRequestBackgroundSound]` โ Ambient background sound during calls.
-
-
+
+-
+**timezone:** `typing.Optional[str]` โ IANA timezone identifier used for date/time interpretation in prompts and tool calls.
+
-
-client.waves.synthesize_lightning_large(...) -> typing.Iterator[bytes]
-
-#### ๐ Description
+**global_knowledge_base_id:** `typing.Optional[str]` โ Knowledge base attached to the agent for retrieval-augmented responses.
+
+
+
-
+**mute_user_until_first_bot_response:** `typing.Optional[bool]`
+
+
+
+
-
-Get speech for given text using the Waves API
-
-
+**allow_interruptions:** `typing.Optional[bool]`
+
-#### ๐ Usage
-
-
+**wait_for_user_to_speak_first:** `typing.Optional[bool]`
+
+
+
+
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
-
-client.waves.synthesize_lightning_large(
- text="text",
- voice_id="voice_id",
-)
-
-```
-
-
+**interruption_backoff_timer:** `typing.Optional[float]`
+
-#### โ๏ธ Parameters
-
-
+**enable_style_guide:** `typing.Optional[bool]`
+
+
+
+
-
-**request:** `LightningLargeRequest`
+**synthesizer:** `typing.Optional[typing.Dict[str, typing.Any]]` โ TTS (voice) configuration. Same shape as `CreateAgentRequest.synthesizer`.
@@ -9622,73 +9132,103 @@ client.waves.synthesize_lightning_large(
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+**language:** `typing.Optional[typing.Dict[str, typing.Any]]` โ Language configuration. Same shape as `CreateAgentRequest.language`.
+
+
+-
+
+**default_variables:** `typing.Optional[typing.Dict[str, typing.Any]]` โ Default variables injected into prompts and tool calls.
+
+
+-
+**pre_call_api:** `typing.Optional[typing.Dict[str, typing.Any]]` โ Pre-call API webhook config. Same shape as `CreateAgentRequest.preCallAPI`.
+
-
-client.waves.synthesize_lightning_v2(...) -> typing.Iterator[bytes]
-
-#### ๐ Description
+**smart_turn_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
+
+
-
+**voice_detection_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
+
+
+
-
-Get speech for given text using the Waves API
+**voice_mail_detection_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
+
+
+-
+
+**denoising_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
-#### ๐ Usage
-
-
+**redaction_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
+
+
+
-
-```python
-from smallestai import SmallestAI
-from smallestai.environment import SmallestAIEnvironment
-
-client = SmallestAI(
- api_key="",
- environment=SmallestAIEnvironment.PRODUCTION,
-)
+**pronunciation_dicts:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
+
+
-client.waves.synthesize_lightning_v2(
- text="Hey i am your a text to speech model",
- voice_id="malcom",
-)
+
+-
-```
+**llm_idle_timeout_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
+
+
+-
+
+**session_timeout_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
-#### โ๏ธ Parameters
-
-
+**call_disposition_config:** `typing.Optional[typing.Dict[str, typing.Any]]`
+
+
+
+
-
-**request:** `Lightningv2Request`
+**speech_formatting:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -9708,7 +9248,7 @@ client.waves.synthesize_lightning_v2(
-client.waves.synthesize_sse_lightning_large(...) -> typing.Iterator[bytes]
+client.atoms.agent_versioning_branches.discard_draft(...) -> DiscardDraftAgentVersioningBranchesResponse
-
@@ -9720,21 +9260,7 @@ client.waves.synthesize_lightning_v2(
-
-The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
-
-## When to Use
-
-- **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
-- **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
-- **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
-- **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-
-## How It Works
-
-1. **Make a POST Request**: Send your text and voice settings to the API endpoint
-2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
-3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
-4. **End of Stream**: The API sends a completion event when all audio has been delivered
+Discard the open draft on this branch. Any unpublished edits are lost. The last committed revision remains the branch head.
@@ -9757,9 +9283,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.synthesize_sse_lightning_large(
- text="text",
- voice_id="voice_id",
+client.atoms.agent_versioning_branches.discard_draft(
+ id="id",
+ branch_id="branchId",
)
```
@@ -9776,7 +9302,15 @@ client.waves.synthesize_sse_lightning_large(
-
-**request:** `LightningLargeRequest`
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
@@ -9796,7 +9330,7 @@ client.waves.synthesize_sse_lightning_large(
-client.waves.synthesize_sse_lightning_v2(...) -> typing.Iterator[bytes]
+client.atoms.agent_versioning_branches.publish_draft(...) -> PublishDraftAgentVersioningBranchesResponse
-
@@ -9808,22 +9342,11 @@ client.waves.synthesize_sse_lightning_large(
-
-The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
-For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
-
-## When to Use
-
-- **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
-- **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
-- **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
-- **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
+Publish the open draft on this branch as a new revision.
-## How It Works
+The response is `200` with `state: "committed"` when the security scan finishes synchronously, and `202` with `state: "scanning"` when the scan is deferred. A `scanning` revision is visible in history but cannot be restored or made live until it becomes `committed`. If the scan fails, the revision is left in `scanning` state and this endpoint returns `409` on subsequent publishes until the scan is retried.
-1. **Make a POST Request**: Send your text and voice settings to the API endpoint
-2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
-3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
-4. **End of Stream**: The API sends a completion event when all audio has been delivered
+Publishing on the live branch pushes to production immediately.
@@ -9846,9 +9369,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.synthesize_sse_lightning_v2(
- text="text",
- voice_id="voice_id",
+client.atoms.agent_versioning_branches.publish_draft(
+ id="id",
+ branch_id="branchId",
)
```
@@ -9865,7 +9388,23 @@ client.waves.synthesize_sse_lightning_v2(
-
-**request:** `Lightningv2Request`
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
+
+
+
+
+
+-
+
+**label:** `typing.Optional[str]` โ Optional label saved on the committed revision.
@@ -9885,7 +9424,7 @@ client.waves.synthesize_sse_lightning_v2(
-client.waves.get_voices(...) -> GetVoicesWavesResponse
+client.atoms.agent_versioning_branches.cancel_publish(...) -> CancelPublishAgentVersioningBranchesResponse
-
@@ -9897,7 +9436,7 @@ client.waves.synthesize_sse_lightning_v2(
-
-List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
+Cancel a publish that is currently scanning. Idempotent. Returns `200` even if no scan is active.
@@ -9920,8 +9459,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.get_voices(
- model="lightning-v3.1",
+client.atoms.agent_versioning_branches.cancel_publish(
+ id="id",
+ branch_id="branchId",
)
```
@@ -9938,7 +9478,15 @@ client.waves.get_voices(
-
-**model:** `GetVoicesWavesRequestModel` โ The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
@@ -9958,7 +9506,7 @@ client.waves.get_voices(
-client.waves.add_voice(...) -> AddVoiceWavesResponse
+client.atoms.agent_versioning_branches.test_call(...) -> TestCallAgentVersioningBranchesResponse
-
@@ -9970,12 +9518,9 @@ client.waves.get_voices(
-
-**Deprecated** โ use `POST /waves/v1/voice-cloning` instead. The new
-endpoint defaults to `lightning-v3.1`, supports optional metadata,
-and returns pre-generated sample clips. This endpoint only clones
-onto `lightning-large` and the resulting voices do not work on
-`lightning-v3.1` (returns an empty WAV). Kept live for backward
-compatibility; new integrations should migrate.
+Start a test call using the branch's current config. Send `includeDraft: true` to test the open draft, or send `revisionId` to test a specific committed revision on the branch. Sending both is a validation error.
+
+The response always includes `conversationId` and `callId`. For `webcall` and `chat`, it also includes `token`, `roomName`, and `host`. Those fields are omitted for `telephony`.
@@ -9998,9 +9543,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.add_voice(
- file="example_file",
- display_name="displayName",
+client.atoms.agent_versioning_branches.test_call(
+ id="id",
+ branch_id="branchId",
)
```
@@ -10017,7 +9562,39 @@ client.waves.add_voice(
-
-**display_name:** `str` โ Display name for the voice clone.
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
+
+
+
+
+
+-
+
+**include_draft:** `typing.Optional[bool]` โ When `true`, the test call uses the branch's open draft. Mutually exclusive with `revisionId`.
+
+
+
+
+
+-
+
+**revision_id:** `typing.Optional[str]` โ Test against a specific committed revision on the branch. Mutually exclusive with `includeDraft: true`.
+
+
+
+
+
+-
+
+**mode:** `typing.Optional[TestCallV2RequestMode]`
@@ -10025,7 +9602,7 @@ client.waves.add_voice(
-
-**file:** `core.File` โ Audio file to create voice clone from.
+**to_phone:** `typing.Optional[str]` โ E.164-formatted number. Required when `mode` is `telephony`. Omit for `webcall` and `chat`.
@@ -10045,7 +9622,8 @@ client.waves.add_voice(
-client.waves.get_cloned_voices() -> GetClonedVoicesWavesResponse
+## Atoms AgentVersioningRevisions
+client.atoms.agent_versioning_revisions.list(...) -> ListAgentVersioningRevisionsResponse
-
@@ -10057,9 +9635,7 @@ client.waves.add_voice(
-
-**Deprecated** โ use `GET /waves/v1/voice-cloning` instead. The new
-list endpoint returns the same data plus a `modelIds` array per
-clone. Kept live for backward compatibility.
+Paginated list of committed and scanning revisions on this branch, newest first.
@@ -10082,7 +9658,10 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.get_cloned_voices()
+client.atoms.agent_versioning_revisions.list(
+ id="id",
+ branch_id="branchId",
+)
```
@@ -10098,6 +9677,38 @@ client.waves.get_cloned_voices()
-
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
+
+
+
+
+
+-
+
+**limit:** `typing.Optional[int]`
+
+
+
+
+
+-
+
+**skip:** `typing.Optional[int]`
+
+
+
+
+
+-
+
**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
@@ -10110,7 +9721,7 @@ client.waves.get_cloned_voices()
-client.waves.delete_voice(...) -> DeleteVoiceWavesResponse
+client.atoms.agent_versioning_revisions.get(...) -> GetAgentVersioningRevisionsResponse
-
@@ -10122,9 +9733,7 @@ client.waves.get_cloned_voices()
-
-Delete a voice clone by `voiceId`. Despite the `/lightning-large/`
-path, this endpoint deletes any voice clone on the organization,
-including clones created via `POST /waves/v1/voice-cloning`.
+Return a single revision plus its `resolvedConfig` (the fully-merged agent config at that revision).
@@ -10147,8 +9756,10 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.delete_voice(
- voice_id="voiceId",
+client.atoms.agent_versioning_revisions.get(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
)
```
@@ -10165,7 +9776,7 @@ client.waves.delete_voice(
-
-**voice_id:** `str` โ The unique identifier of the voice clone to delete.
+**id:** `str` โ The agent ID.
@@ -10173,122 +9784,3705 @@ client.waves.delete_voice(
-
-**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+**branch_id:** `str` โ The branch ID.
-
-
-
-
-
-
-
-client.waves.synthesize_tts(...) -> typing.Iterator[bytes]
-
-#### ๐ Description
+**revision_id:** `str` โ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+
+
-
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.agent_versioning_revisions.get_history(...) -> GetHistoryAgentVersioningRevisionsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Return the publish trail for a revision: who published it, and the ordered list of prior revisions on this branch with the sections that changed at each step.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.agent_versioning_revisions.get_history(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
+
+
+
+
+
+-
+
+**revision_id:** `str` โ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.agent_versioning_revisions.restore(...) -> RestoreAgentVersioningRevisionsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Republish an older revision as a new revision at the head of this branch. Restore does not overwrite history; the older revision keeps its ID, and a new revision is committed on top.
+
+The response mirrors `POST /agent/{id}/branches/{branchId}/draft/publish`: `200 committed` if the scan is synchronous, `202 scanning` if deferred. Only one publish or restore can be in flight per branch at a time.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.agent_versioning_revisions.restore(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**branch_id:** `str` โ The branch ID.
+
+
+
+
+
+-
+
+**revision_id:** `str` โ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.agent_versioning_revisions.diff(...) -> DiffAgentVersioningRevisionsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Compare any two references on this agent and return per-section diffs. Each side (`a` and `b`) is either a `revisionId` or the string `:draft` to reference the open draft on a branch. Sides may cross branches. Both references must resolve to configs on the same agent.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.agent_versioning_revisions.diff(
+ id="id",
+ a="a",
+ b="b",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ The agent ID.
+
+
+
+
+
+-
+
+**a:** `str` โ Left-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+
+
+
+
+-
+
+**b:** `str` โ Right-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Atoms PromptScoring
+client.atoms.prompt_scoring.score_a_prompt(...) -> PostPromptScoringScoreResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Scores an agent's prompt across 11 quality dimensions using Gemini-based analysis. Requires the prompt to have changed since the last scoring.
+
+**Input:** Provide exactly one of `versionId` (published agent version) or `draftId` (agent draft). Providing both or neither returns a 400.
+
+**Credit usage:** 1 credit is deducted per successful call.
+
+**Idempotency:** Re-submitting the same prompt without changes returns a 400 โ retrieve the cached score via the GET agent endpoint instead.
+
+**Supported agent types:** Only `single_prompt` agents are supported. Workflow-graph agents return a 400.
+
+**Scoring model:** Two sequential Gemini calls โ a Platform Analyst pass followed by a Rubric Judge pass.
+
+### Scored Dimensions
+
+| Tier | Dimension | Notes |
+|------|-----------|-------|
+| 1 | Role & Objective | |
+| 1 | Personality & Voice | |
+| 1 | Conversation Structure | |
+| 1 | Tool Integration | |
+| 1 | Constraints & Safety | |
+| 2 | Conversational Naturalness | |
+| 2 | Failure-Mode Coverage | |
+| 3 | Information Integrity | Gating โ if Weak/Missing, score capped at 70 |
+| 3 | Variable & Tool Hygiene | Gating โ if Weak/Missing, score capped at 50 |
+| 3 | Internal Consistency | |
+| 3 | Density | Computed from token analysis |
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+from smallestai.atoms.prompt_scoring import PostPromptScoringScoreRequestVersionId
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.prompt_scoring.score_a_prompt(
+ request=PostPromptScoringScoreRequestVersionId(
+ version_id="6a1589b75e048394eb37bc47",
+ ),
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**request:** `PostPromptScoringScoreRequest` โ Exactly one of `versionId` or `draftId` must be provided.
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Analytics
+client.atoms.analytics.get_call_counts_log(...) -> GetCallCountsLogResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Paginated listing of call records for the organization, with optional filtering by agent, campaign, call type, and date range.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_call_counts_log()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**page:** `typing.Optional[int]` โ Page number (default 1)
+
+
+
+
+
+-
+
+**limit:** `typing.Optional[int]` โ Records per page (default 10)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_call_counts_by_day(...) -> GetCallCountsByDayResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns call counts aggregated per calendar day, suitable for bar chart visualizations.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_call_counts_by_day()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_conversation_details(...) -> GetConversationDetailsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns the full transcript and event stream for a specific call, reconstructed from ClickHouse event data.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_conversation_details(
+ call_id="callId",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**call_id:** `str` โ Unique identifier for the call
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_usage_timeseries(...) -> GetUsageTimeseriesResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns daily credit usage over a date range.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_usage_timeseries()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_dashboard(...) -> GetDashboardResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Batched endpoint that fetches all dashboard panels in a single request by running six sub-queries in parallel. Equivalent to calling `summary`, `call-volume-timeseries`, `call-outcomes-timeseries`, `pickup-rate-by-number`, `hourly-performance`, and `duration-stats` individually. Each field may be absent if that sub-query failed; partial data is still returned.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_dashboard()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_analytics_summary(...) -> GetAnalyticsSummaryResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns high-level KPI metrics with current period value, previous period value, and percent change for trend comparison.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_analytics_summary()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_call_volume_timeseries(...) -> GetCallVolumeTimeseriesResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns daily call volume broken down by outcome (answered, no-answer, failed, cancelled) over the selected period.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_call_volume_timeseries()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_pickup_rate_by_number(...) -> GetPickupRateByNumberResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns pickup rate and call volume broken down per originating phone number.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_pickup_rate_by_number()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_phone_number_trends(...) -> GetPhoneNumberTrendsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns per-phone-number call volume and pickup rate as a daily timeseries, useful for spotting number-level degradation over time.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_phone_number_trends()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_hourly_performance(...) -> GetHourlyPerformanceResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns call volume and performance metrics broken down by hour of day (0โ23), aggregated across the selected date range.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_hourly_performance()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_call_outcomes_timeseries(...) -> GetCallOutcomesTimeseriesResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns a daily breakdown of call outcomes (answered, no-answer, failed, cancelled) over time, plus totals for the entire period.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_call_outcomes_timeseries()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_duration_stats(...) -> GetDurationStatsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns call duration statistics including average, median, p90, p95 percentiles, and the proportion of short vs. long calls.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_duration_stats()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_weekly_trends(...) -> GetWeeklyTrendsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns per-week call performance metrics including volume, pickup rate, and duration percentiles (p50, p90). Each week starts on Monday.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_weekly_trends()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_agent_performance(...) -> GetAgentPerformanceResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns per-agent call performance metrics. Supports sorting and limiting results.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_agent_performance()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**sort_by:** `typing.Optional[str]` โ Field to sort by (e.g. `totalCalls`, `pickupRate`, `avgDuration`)
+
+
+
+
+
+-
+
+**sort_order:** `typing.Optional[GetAgentPerformanceRequestSortOrder]` โ Sort direction
+
+
+
+
+
+-
+
+**limit:** `typing.Optional[int]` โ Maximum number of agents to return
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_analytics_concurrency(...) -> GetAnalyticsConcurrencyResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns minute-by-minute concurrent call counts for a specific day. Optionally broken down per agent.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+import datetime
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_analytics_concurrency(
+ date=datetime.date.fromisoformat("2023-01-15"),
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**date:** `datetime.date` โ Date to query (YYYY-MM-DD)
+
+
+
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Filter to a specific agent
+
+
+
+
+
+-
+
+**include_agents:** `typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents]` โ Pass `true` to include a per-agent breakdown in the response
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_call_start_distribution(...) -> GetCallStartDistributionResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns the distribution of call start times as minute-level buckets for a specific day, showing when calls were initiated throughout the day.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+import datetime
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_call_start_distribution(
+ date=datetime.date.fromisoformat("2023-01-15"),
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**date:** `datetime.date` โ Date to query (YYYY-MM-DD)
+
+
+
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Filter to a specific agent
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_daily_call_summary(...) -> GetDailyCallSummaryResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns an aggregate call summary for a specific day. Live in-progress and in-queue counts are merged from real-time data on top of the historical data.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+import datetime
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_daily_call_summary(
+ date=datetime.date.fromisoformat("2023-01-15"),
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**date:** `datetime.date` โ Date to query (YYYY-MM-DD)
+
+
+
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Filter to a specific agent
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.analytics.get_attempt_cohort(...) -> GetAttemptCohortResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns cohort analysis of call attempt numbers, showing how pickup rate changes across the 1st, 2nd, 3rd (etc.) attempts to reach the same number, including cumulative rates and marginal gain per additional attempt.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.analytics.get_attempt_cohort()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]` โ Comma-separated agent IDs to filter results
+
+
+
+
+
+-
+
+**campaign_id:** `typing.Optional[str]` โ Campaign ID to filter results
+
+
+
+
+
+-
+
+**call_type:** `typing.Optional[str]` โ Type of call to filter (e.g. `inbound`, `outbound`)
+
+
+
+
+
+-
+
+**date_from:** `typing.Optional[datetime.datetime]` โ Start of date range (ISO 8601)
+
+
+
+
+
+-
+
+**date_to:** `typing.Optional[datetime.datetime]` โ End of date range (ISO 8601)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Call Actions
+client.atoms.call_actions.list_call_actions(...) -> ListCallActionsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns a paginated list of call actions for the organization, filtered by agent. Optionally filter by category or provider.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.call_actions.list_call_actions(
+ agent_id="agentId",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `str` โ Filter by agent (ObjectId)
+
+
+
+
+
+-
+
+**page:** `typing.Optional[int]` โ Page number (default 1)
+
+
+
+
+
+-
+
+**limit:** `typing.Optional[int]` โ Records per page (default 10)
+
+
+
+
+
+-
+
+**category:** `typing.Optional[ListCallActionsRequestCategory]` โ Filter by category
+
+
+
+
+
+-
+
+**provider:** `typing.Optional[str]` โ Filter by provider name
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.call_actions.create_call_action(...) -> CreateCallActionResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Creates a new call action for an agent. Call actions define automated behaviors that fire at specific points in a call lifecycle.
+
+- **`trigger`** actions fire to initiate an outbound call and require `config.phoneNumberFieldName`.
+- **`post-call`** actions fire after a call ends (e.g. to update a CRM record).
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+from smallestai.atoms.call_actions import CreateCallActionRequestConfig
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.call_actions.create_call_action(
+ agent_id="agentId",
+ category="trigger",
+ provider="provider",
+ config=CreateCallActionRequestConfig(),
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `str` โ Agent this action belongs to (ObjectId)
+
+
+
+
+
+-
+
+**category:** `CreateCallActionRequestCategory` โ When the action fires
+
+
+
+
+
+-
+
+**provider:** `str` โ Integration provider (e.g. `hubspot`, `salesforce`)
+
+
+
+
+
+-
+
+**config:** `CreateCallActionRequestConfig`
+
+
+
+
+
+-
+
+**action_type:** `typing.Optional[CreateCallActionRequestActionType]` โ The operation to perform on the provider object
+
+
+
+
+
+-
+
+**object:** `typing.Optional[str]` โ Provider object type to act on (e.g. `contact`, `deal`)
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.call_actions.get_call_action(...) -> GetCallActionResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns a single call action by ID. Scoped to the authenticated organization.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.call_actions.get_call_action(
+ id="id",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ Call action ObjectId
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.call_actions.update_call_action(...) -> UpdateCallActionResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Updates an existing call action. All body fields are optional โ only provided fields are updated.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.call_actions.update_call_action(
+ id="id",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ Call action ObjectId
+
+
+
+
+
+-
+
+**category:** `typing.Optional[UpdateCallActionRequestCategory]` โ Change when the action fires
+
+
+
+
+
+-
+
+**provider:** `typing.Optional[str]` โ Change the integration provider
+
+
+
+
+
+-
+
+**action_type:** `typing.Optional[UpdateCallActionRequestActionType]` โ Change the operation type
+
+
+
+
+
+-
+
+**object:** `typing.Optional[str]` โ Change the provider object type
+
+
+
+
+
+-
+
+**config:** `typing.Optional[UpdateCallActionRequestConfig]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.call_actions.delete_call_action(...) -> DeleteCallActionResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Permanently deletes a call action. Scoped to the authenticated organization.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.call_actions.delete_call_action(
+ id="id",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ Call action ObjectId
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Integrations
+client.atoms.integrations.modify_web_engage_integration(...) -> ModifyWebEngageIntegrationResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Creates or updates the WebEngage integration for the organization. Replaces the existing integration configuration with the provided credential set(s).
+
+**Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+from smallestai.atoms import WebEngageIntegrationSet
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.integrations.modify_web_engage_integration(
+ integration_sets=[
+ WebEngageIntegrationSet(
+ license_code="licenseCode",
+ environment="environment",
+ api_key="apiKey",
+ )
+ ],
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**integration_sets:** `typing.List[WebEngageIntegrationSet]` โ One or more WebEngage credential sets
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.integrations.get_web_engage_details() -> GetWebEngageDetailsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns the current WebEngage integration configuration for the organization.
+
+**Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.integrations.get_web_engage_details()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Concurrency
+client.atoms.concurrency.get_concurrency() -> GetConcurrencyResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns the organization's overall concurrency limit, how much is reserved across all agents, the remaining unreserved pool, and the per-agent reservation breakdown per call channel.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.concurrency.get_concurrency()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.atoms.concurrency.update_concurrency_reservations(...) -> UpdateConcurrencyReservationsResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Updates concurrency reservations for one or more agents in a single request. Replaces the existing reservation values for each specified agent. **Admin role required.**
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+from smallestai.atoms.concurrency import UpdateConcurrencyReservationsRequestReservationsItem
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.concurrency.update_concurrency_reservations(
+ reservations=[
+ UpdateConcurrencyReservationsRequestReservationsItem(
+ agent_id="agentId",
+ webcall=1,
+ outbound=1,
+ inbound=1,
+ chat=1,
+ )
+ ],
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**reservations:** `typing.List[UpdateConcurrencyReservationsRequestReservationsItem]` โ Array of agent reservations to update
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Disposition Metric Templates
+client.atoms.disposition_metric_templates.list_disposition_metric_templates() -> ListDispositionMetricTemplatesResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Returns all available disposition metric templates. These reusable definitions are used to populate the post-call analytics metric picker when configuring an agent's `postCallAnalyticsConfig`. Only a user token is required โ no organization context needed.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.disposition_metric_templates.list_disposition_metric_templates()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Atoms Dnc
+client.atoms.dnc.list(...) -> ListDncResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Lists Do-Not-Call entries for the caller's organization with pagination, search,
+and sort. Optionally scope to a single agent via `agentId`.
+
+Each entry records a phone number that was flagged (via call outcome or manual
+upload) as not-to-be-called for either the org or a specific agent.
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.atoms.dnc.list()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**agent_id:** `typing.Optional[str]`
+
+Optional 24-character hex agent ID. When present, restricts results to entries
+for this agent. Returns 400/404 if the ID isn't valid or doesn't belong to
+the caller's org.
+
+
+
+
+
+-
+
+**search:** `typing.Optional[str]` โ Free-text search across phone numbers.
+
+
+
+
+
+-
+
+**sort_field:** `typing.Optional[ListDncRequestSortField]`
+
+
+
+
+
+-
+
+**sort_order:** `typing.Optional[ListDncRequestSortOrder]`
+
+
+
+
+
+-
+
+**page:** `typing.Optional[str]` โ Page number (string-encoded positive integer, โฅ 1).
+
+
+
+
+
+-
+
+**offset:** `typing.Optional[str]` โ Page size (string-encoded; server clamps to 1โ500).
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Waves
+client.waves.get_pronunciation_dicts() -> typing.List[PronunciationDict]
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Retrieve all pronunciation dictionaries for the authenticated user
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.get_pronunciation_dicts()
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.waves.create_pronunciation_dict(...) -> PronunciationDict
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Create a new pronunciation dictionary for the authenticated user
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+from smallestai.waves import PronunciationItem
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.create_pronunciation_dict(
+ items=[
+ PronunciationItem(
+ word="mysql",
+ pronunciation="my-sequel",
+ )
+ ],
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**items:** `typing.List[PronunciationItem]` โ List of word-pronunciation pairs to create
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.waves.update_pronunciation_dict(...) -> UpdatePronunciationDictResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Update an existing pronunciation dictionary for the authenticated user
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+from smallestai.waves import PronunciationItem
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.update_pronunciation_dict(
+ id="64f1234567890abcdef12345",
+ items=[
+ PronunciationItem(
+ word="mysql",
+ pronunciation="my-sequel",
+ )
+ ],
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ ID of the pronunciation dictionary to update
+
+
+
+
+
+-
+
+**items:** `typing.List[PronunciationItem]` โ Updated list of word-pronunciation pairs
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.waves.delete_pronunciation_dict(...) -> DeletePronunciationDictResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Delete an existing pronunciation dictionary for the authenticated user
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.delete_pronunciation_dict(
+ id="64f1234567890abcdef12345",
+)
+
+```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**id:** `str` โ ID of the pronunciation dictionary to delete
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.waves.synthesize_lightning(...)
-
-Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
+#### ๐ Description
-Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
+
+-
-**Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). On `lightning_v3.1` the full 12-language catalog applies (see voice catalog).
+
+-
-## When to use this
+Get speech for given text using the Waves API
+
+
+
+
-- **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
-- **Use `/waves/v1/tts/live`** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
-- **Use `/waves/v1/tts/live`** (WebSocket) when text arrives incrementally (LLM token streams, live captioning).
+#### ๐ Usage
-## Key features
+
+-
-- 44 kHz natural, expressive synthesis
-- Model selectable per request via `model` body parameter
-- Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
-- 12 documented languages on `lightning_v3.1`. On `lightning_v3.1_pro`: `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`.
-- Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
-- Sample rates: 8 kHz โ 44.1 kHz
-- Speed: 0.5ร โ 2ร
-- Per-call pronunciation dictionaries via `pronunciation_dicts`
+
+-
-## Examples
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.synthesize_lightning()
-**cURL โ Lightning v3.1 (default)**
-```bash
-curl -X POST "https://api.smallest.ai/waves/v1/tts" \
- -H "Authorization: Bearer $SMALLEST_API_KEY" \
- -H "Content-Type: application/json" \
- -H "Accept: audio/wav" \
- -d '{
- "text": "Hello from Waves TTS.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**output_format:** `typing.Optional[SynthesizeLightningWavesRequestOutputFormat]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.waves.synthesize_lightning_large(...)
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+Get speech for given text using the Waves API
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.synthesize_lightning_large()
-**cURL โ Lightning v3.1 Pro (omit `language` โ defaults to `en + hi`)**
-```bash
-curl -X POST "https://api.smallest.ai/waves/v1/tts" \
- -H "Authorization: Bearer $SMALLEST_API_KEY" \
- -H "Content-Type: application/json" \
- -H "Accept: audio/wav" \
- -d '{
- "text": "Hello from the Lightning v3.1 Pro pool.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**output_format:** `typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.waves.synthesize_sse_lightning_large(...)
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
+
+## When to Use
+
+- **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+- **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+- **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+- **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
+
+## How It Works
+
+1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+4. **End of Stream**: The API sends a completion event when all audio has been delivered
+
+
+
+
+
+#### ๐ Usage
+
+
+-
+
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.synthesize_sse_lightning_large()
-**cURL โ Lightning v3.1 Pro with explicit `language: en` (UK + American accented English)**
-```bash
-curl -X POST "https://api.smallest.ai/waves/v1/tts" \
- -H "Authorization: Bearer $SMALLEST_API_KEY" \
- -H "Content-Type: application/json" \
- -H "Accept: audio/wav" \
- -d '{
- "text": "Good morning, this is a Pro voice speaking.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "language": "en",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
```
+
+
+
+
+
+#### โ๏ธ Parameters
+
+
+-
+
+
+-
+
+**output_format:** `typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
-**cURL โ Lightning v3.1 Pro with explicit `language: hi` (Indian accented English + Hindi)**
-```bash
-curl -X POST "https://api.smallest.ai/waves/v1/tts" \
- -H "Authorization: Bearer $SMALLEST_API_KEY" \
- -H "Content-Type: application/json" \
- -H "Accept: audio/wav" \
- -d '{
- "text": "Namaste, this is an Indian-accented Pro voice.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "language": "hi",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
-```
+client.waves.synthesize_lightning_v2(...)
+
+-
-## Common gotchas
+#### ๐ Description
-- **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
-- **Pair voice IDs with the right model.** Voice catalogs differ between `lightning_v3.1` and `lightning_v3.1_pro`. The API does not reject mismatched pairings, but using a Pro-only `voice_id` with `model=lightning_v3.1` (or omitting `model`) can return wrong or hallucinated audio. Pair Pro voices with `model=lightning_v3.1_pro`; standard catalog voices with `model=lightning_v3.1` (the default).
-- **Cloned voices** (`voice_*` from `add_voice`) work with `lightning_v3.1` only; voice cloning is not available on `lightning_v3.1_pro`.
-- **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
+
+-
+
+
+-
+
+Get speech for given text using the Waves API
@@ -10311,10 +13505,7 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.synthesize_tts(
- text="Hello from Waves TTS.",
- voice_id="magnus",
-)
+client.waves.synthesize_lightning_v2()
```
@@ -10330,15 +13521,7 @@ client.waves.synthesize_tts(
-
-**accept:** `typing.Literal` โ Must be `audio/wav` to receive binary audio. Required for proper playback.
-
-
-
-
-
--
-
-**request:** `TtsRequest`
+**output_format:** `typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat]`
@@ -10358,7 +13541,7 @@ client.waves.synthesize_tts(
-client.waves.synthesize_sse_tts(...) -> typing.Iterator[bytes]
+client.waves.synthesize_sse_lightning_v2(...)
-
@@ -10370,47 +13553,93 @@ client.waves.synthesize_tts(
-
-Synthesize speech and stream the audio back over Server-Sent Events. Same body as `/waves/v1/tts` โ the only difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
+The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
+For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
-Pick the model with the `model` body parameter, same as the sync route.
+## When to Use
-
- **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/waves/api-reference/api-reference/text-to-speech/tts).
-
+- **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+- **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+- **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+- **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-## When to use this
+## How It Works
-- **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
-- **Use sync `/waves/v1/tts`** when total latency doesn't matter and you'd rather get one buffer.
-- **Use `/waves/v1/tts/live`** (WebSocket) when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
+1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+4. **End of Stream**: The API sends a completion event when all audio has been delivered
+
+
+
+
-## How it works
+#### ๐ Usage
-1. POST your text + voice settings โ same payload as `/waves/v1/tts`, plus optional `model`.
-2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\n` followed by `data: {"audio": ""}\n\n`.
-3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
-4. A final `data: {"done": true}\n\n` frame marks end of stream.
+
+-
-## Examples
+
+-
+
+```python
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+
+client = SmallestAI(
+ api_key="",
+ environment=SmallestAIEnvironment.PRODUCTION,
+)
+
+client.waves.synthesize_sse_lightning_v2()
-**cURL**
-```bash
-curl -N -X POST "https://api.smallest.ai/waves/v1/tts/live" \
- -H "Authorization: Bearer $SMALLEST_API_KEY" \
- -H "Content-Type: application/json" \
- -d '{
- "text": "Streaming this paragraph chunk by chunk so playback can start sooner.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "pcm"
- }'
```
+
+
+
+
-## Common gotchas
+#### โ๏ธ Parameters
-- **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
-- **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
-- **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
+
+-
+
+
+-
+
+**output_format:** `typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` โ Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.waves.get_voices(...) -> GetVoicesWavesResponse
+
+-
+
+#### ๐ Description
+
+
+-
+
+
+-
+
+List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
@@ -10433,9 +13662,8 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.synthesize_sse_tts(
- text="text",
- voice_id="voice_id",
+client.waves.get_voices(
+ model="lightning-v3.1",
)
```
@@ -10452,7 +13680,7 @@ client.waves.synthesize_sse_tts(
-
-**request:** `TtsRequest`
+**model:** `GetVoicesWavesRequestModel` โ The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
@@ -10472,7 +13700,7 @@ client.waves.synthesize_sse_tts(
-client.waves.synthesize_lightning_v31(...) -> typing.Iterator[bytes]
+client.waves.synthesize_tts(...) -> typing.Iterator[bytes]
-
@@ -10484,21 +13712,24 @@ client.waves.synthesize_sse_tts(
-
-**Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech) and select Lightning v3.1 via the `model` body field (default).
+Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
+
+Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
-Synthesize speech from text in a single request. The simplest way to get audio when you have the full text up front โ pass `text` + `voice_id`, get back binary audio.
+**Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). Pro supports 31 languages total (10 Indic, 8 Asian & Middle Eastern, 13 European including Dutch and Swedish). Pass the matching ISO 639-1 code (e.g. `ta`, `de`, `ja`) with a Pro voice from that language, or use `auto` to route across all supported languages with any English or Hindi voice. See the [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages) for the full list. On `lightning_v3.1` the model accepts 20 language codes (10 European + 10 Indic) plus `auto`; the trained voice catalog covers 12 of those directly.
## When to use this
- **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
-- **Use the SSE streaming endpoint** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
-- **Use the WebSocket endpoint** when text arrives incrementally (LLM token streams, live captioning).
+- **Use `/waves/v1/tts/live`** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
+- **Use `/waves/v1/tts/live`** (WebSocket) when text arrives incrementally (LLM token streams, live captioning).
## Key features
- 44 kHz natural, expressive synthesis
-- Cloned voice IDs (`voice_*`) work โ same param as catalog voices
-- 12 documented languages โ see the model card for the full list
+- Model selectable per request via `model` body parameter
+- Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
+- 20 accepted language codes on `lightning_v3.1` (12 with trained voices, 8 additional routed via English/Hindi voices). On `lightning_v3.1_pro`: 31 languages with dedicated voices (10 Indic, 8 Asian & Middle Eastern, 13 European); `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`. Both models accept `language: auto` for cross-language routing.
- Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- Sample rates: 8 kHz โ 44.1 kHz
- Speed: 0.5ร โ 2ร
@@ -10506,68 +13737,73 @@ Synthesize speech from text in a single request. The simplest way to get audio w
## Examples
-**cURL**
+**cURL โ Lightning v3.1 (default)**
```bash
-curl -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech" \
+curl -X POST "https://api.smallest.ai/waves/v1/tts" \
-H "Authorization: Bearer $SMALLEST_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: audio/wav" \
-d '{
- "text": "Hello from Lightning v3.1.",
+ "text": "Hello from Waves TTS.",
"voice_id": "magnus",
"sample_rate": 24000,
"output_format": "wav"
}' --output speech.wav
```
-**Python** (`pip install smallestai>=4.4.0`)
-```python
-from smallestai import SmallestAI
-
-client = SmallestAI(api_key="YOUR_API_KEY")
+**cURL โ Lightning v3.1 Pro (omit `language` โ defaults to `en + hi`)**
+```bash
+curl -X POST "https://api.smallest.ai/waves/v1/tts" \
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \
+ -H "Content-Type: application/json" \
+ -H "Accept: audio/wav" \
+ -d '{
+ "text": "Hello from the Lightning v3.1 Pro pool.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
+```
-with open("speech.wav", "wb") as f:
- for chunk in client.waves.synthesize_lightning_v3_1(
- text="Hello from Lightning v3.1.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="wav",
- # Optional: cloned voice support
- # voice_id="voice_FlPKRWI7DX",
- # Optional: pin pronunciations for specific words
- # pronunciation_dicts=[""],
- ):
- f.write(chunk)
+**cURL โ Lightning v3.1 Pro with explicit `language: en` (UK + American accented English)**
+```bash
+curl -X POST "https://api.smallest.ai/waves/v1/tts" \
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \
+ -H "Content-Type: application/json" \
+ -H "Accept: audio/wav" \
+ -d '{
+ "text": "Good morning, this is a Pro voice speaking.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "language": "en",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
```
-**JavaScript / TypeScript** (using `fetch`)
-```typescript
-const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- Accept: "audio/wav",
- },
- body: JSON.stringify({
- text: "Hello from Lightning v3.1.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "wav",
- }),
-});
-const audio = Buffer.from(await res.arrayBuffer());
-require("node:fs").writeFileSync("speech.wav", audio);
+**cURL โ Lightning v3.1 Pro with explicit `language: hi` (Indian accented English + Hindi)**
+```bash
+curl -X POST "https://api.smallest.ai/waves/v1/tts" \
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \
+ -H "Content-Type: application/json" \
+ -H "Accept: audio/wav" \
+ -d '{
+ "text": "Namaste, this is an Indian-accented Pro voice.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "language": "hi",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
```
## Common gotchas
- **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
-- **Cloned voices** (`voice_*` from `add_voice`) work on this endpoint and support `pronunciation_dicts`.
-- **`pronunciation_dicts` validates IDs at request time.** Passing an unknown ID returns `Invalid input data` โ create the dict first via the pronunciation-dicts endpoint and save the returned `id`.
-- **Pronunciation matching is case-sensitive.** Add both `Synopsis` and `synopsis` if your text uses both casings.
+- **Pair voice IDs with the right model.** Voice catalogs differ between `lightning_v3.1` and `lightning_v3.1_pro`. The API does not reject mismatched pairings, but using a Pro-only `voice_id` with `model=lightning_v3.1` (or omitting `model`) can return wrong or hallucinated audio. Pair Pro voices with `model=lightning_v3.1_pro`; standard catalog voices with `model=lightning_v3.1` (the default).
+- **Cloned voices** (`voice_*` from `add_voice`) work with `lightning_v3.1` only; voice cloning is not available on `lightning_v3.1_pro`.
- **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
-- **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` or `axios` as shown above.
@@ -10590,9 +13826,9 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.synthesize_lightning_v31(
- text="Hey i am your a text to speech model",
- voice_id="daniel",
+client.waves.synthesize_tts(
+ text="Hello from Waves TTS.",
+ voice_id="magnus",
)
```
@@ -10617,7 +13853,7 @@ client.waves.synthesize_lightning_v31(
-
-**request:** `LightningV31Request`
+**request:** `TtsRequest`
@@ -10637,7 +13873,7 @@ client.waves.synthesize_lightning_v31(
-client.waves.synthesize_sse_lightning_v31(...) -> typing.Iterator[bytes]
+client.waves.synthesize_sse_tts(...) -> typing.Iterator[bytes]
-
@@ -10649,19 +13885,23 @@ client.waves.synthesize_lightning_v31(
-
-**Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech-sse) and select Lightning v3.1 via the `model` body field (default).
+Synthesize speech and stream the audio back over Server-Sent Events. Same body as `/waves/v1/tts` โ the only difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
+
+Pick the model with the `model` body parameter, same as the sync route.
-Synthesize speech and stream the audio back over Server-Sent Events. The body and parameters are identical to the sync `/get_speech` endpoint โ the difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
+
+ **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/models/api-reference/text-to-speech/stream-speech-web-socket).
+
## When to use this
- **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
-- **Use sync `/get_speech`** when total latency doesn't matter and you'd rather get one buffer.
-- **Use the WebSocket endpoint** when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
+- **Use sync `/waves/v1/tts`** when total latency doesn't matter and you'd rather get one buffer.
+- **Use `/waves/v1/tts/live`** (WebSocket) when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
## How it works
-1. POST your text + voice settings โ same payload as `/get_speech`.
+1. POST your text + voice settings โ same payload as `/waves/v1/tts`, plus optional `model`.
2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\n` followed by `data: {"audio": ""}\n\n`.
3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
4. A final `data: {"done": true}\n\n` frame marks end of stream.
@@ -10670,7 +13910,7 @@ Synthesize speech and stream the audio back over Server-Sent Events. The body an
**cURL**
```bash
-curl -N -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/stream" \
+curl -N -X POST "https://api.smallest.ai/waves/v1/tts/live" \
-H "Authorization: Bearer $SMALLEST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
@@ -10681,74 +13921,11 @@ curl -N -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/stream" \
}'
```
-**Python** (`pip install smallestai>=4.4.0`)
-```python
-import base64
-from smallestai import SmallestAI
-
-client = SmallestAI(api_key="YOUR_API_KEY")
-
-with open("stream.pcm", "wb") as f:
- for chunk in client.waves.synthesize_sse_lightning_v3_1(
- text="Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="pcm",
- ):
- # Each chunk is `{"audio": ""}`.
- # Decode and pipe to your audio pipeline.
- if chunk.get("audio"):
- f.write(base64.b64decode(chunk["audio"]))
-```
-
-**JavaScript / TypeScript** (using `fetch` + a reader)
-```typescript
-const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/stream", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- text: "Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "pcm",
- }),
-});
-
-const reader = res.body!.getReader();
-const decoder = new TextDecoder();
-let buf = "";
-let finished = false;
-while (!finished) {
- const { value, done } = await reader.read();
- if (done) break;
- buf += decoder.decode(value);
- const events = buf.split("\n\n");
- buf = events.pop() ?? "";
- for (const ev of events) {
- // SSE frames are "event: audio\ndata: {json}" or just "data: {json}".
- // We only care about the data line โ pull it out and parse.
- const dataLine = ev.split("\n").find((l) => l.startsWith("data:"));
- if (!dataLine) continue;
- const payload = JSON.parse(dataLine.slice(5).trim());
- if (payload.done) { finished = true; break; }
- if (payload.audio) {
- const pcm = Buffer.from(payload.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- }
- }
-}
-```
-
## Common gotchas
- **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
- **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
- **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
-- **First-chunk latency** depends on model warm-up + network distance. Use `output_format=pcm` and a streaming-friendly client to minimize what you can control.
-- **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` as shown above.
@@ -10771,7 +13948,7 @@ client = SmallestAI(
environment=SmallestAIEnvironment.PRODUCTION,
)
-client.waves.synthesize_sse_lightning_v31(
+client.waves.synthesize_sse_tts(
text="text",
voice_id="voice_id",
)
@@ -10790,7 +13967,7 @@ client.waves.synthesize_sse_lightning_v31(
-
-**request:** `LightningV31Request`
+**request:** `TtsRequest`
@@ -10943,8 +14120,9 @@ Language of the audio file. Set explicitly to the known language for best accura
**Regional auto-detect aggregators** for unknown audio:
- `multi-eu` (default) โ auto-detects across all 21 European codes above plus `en`.
- `multi-asian` โ auto-detects across `zh`, `ko`, `ja`, `en`.
+- `multi-indic`: auto-detects across `en`, `hi`, `gu`, `mr`, `bn`, `or`. India region only.
-Omitting `language` routes to `multi-eu`. See the [Pulse model card](/waves/model-cards/speech-to-text/pulse) for the full table.
+Omitting `language` routes to `multi-eu`. See the [Pulse model card](/models/model-cards/speech-to-text/pulse) for the full table.
diff --git a/src/smallestai/.fern/metadata.json b/src/smallestai/.fern/metadata.json
deleted file mode 100644
index bcf2ed0f..00000000
--- a/src/smallestai/.fern/metadata.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "cliVersion": "5.44.7",
- "generatorName": "fernapi/fern-python-sdk",
- "generatorVersion": "5.12.12",
- "generatorConfig": {
- "package_name": "smallestai",
- "client": {
- "class_name": "SmallestAI",
- "filename": "client.py"
- },
- "pydantic_config": {
- "skip_validation": true
- },
- "exclude_types_from_init_exports": true,
- "should_generate_websocket_clients": true
- },
- "invokedBy": "manual"
-}
\ No newline at end of file
diff --git a/src/smallestai/__init__.py b/src/smallestai/__init__.py
index 2ee94423..b4388a2f 100644
--- a/src/smallestai/__init__.py
+++ b/src/smallestai/__init__.py
@@ -10,6 +10,7 @@
from ._default_clients import DefaultAioHttpClient, DefaultAsyncHttpxClient
from .atoms.client import AsyncAtomsClient, AtomsClient
from .client import AsyncSmallestAI, SmallestAI
+ from .environment import SmallestAIEnvironment
from .version import __version__
from .waves.client import AsyncWavesClient, WavesClient
_dynamic_imports: typing.Dict[str, str] = {
@@ -20,6 +21,7 @@
"DefaultAioHttpClient": "._default_clients",
"DefaultAsyncHttpxClient": "._default_clients",
"SmallestAI": ".client",
+ "SmallestAIEnvironment": ".environment",
"WavesClient": ".waves.client",
"__version__": ".version",
"atoms": ".atoms",
@@ -56,6 +58,7 @@ def __dir__():
"DefaultAioHttpClient",
"DefaultAsyncHttpxClient",
"SmallestAI",
+ "SmallestAIEnvironment",
"WavesClient",
"__version__",
"atoms",
diff --git a/src/smallestai/atoms/agent_templates/__init__.py b/src/smallestai/atoms/agent_templates/__init__.py
index 5cde0202..f258ccc9 100644
--- a/src/smallestai/atoms/agent_templates/__init__.py
+++ b/src/smallestai/atoms/agent_templates/__init__.py
@@ -2,3 +2,69 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ ListAgentTemplatesAgentTemplatesRequestRegion,
+ ListAgentTemplatesAgentTemplatesResponse,
+ ListAgentTemplatesAgentTemplatesResponseDataItem,
+ ListAgentTemplatesAgentTemplatesResponseDataItemCallType,
+ ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage,
+ ListAgentTemplatesAgentTemplatesResponseDataItemRegion,
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig,
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem,
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType,
+ ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType,
+ PostAgentFromTemplateResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ListAgentTemplatesAgentTemplatesRequestRegion": ".types",
+ "ListAgentTemplatesAgentTemplatesResponse": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItem": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemCallType": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemRegion": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType": ".types",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType": ".types",
+ "PostAgentFromTemplateResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ListAgentTemplatesAgentTemplatesRequestRegion",
+ "ListAgentTemplatesAgentTemplatesResponse",
+ "ListAgentTemplatesAgentTemplatesResponseDataItem",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemCallType",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemRegion",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType",
+ "PostAgentFromTemplateResponse",
+]
diff --git a/src/smallestai/atoms/agent_templates/types/__init__.py b/src/smallestai/atoms/agent_templates/types/__init__.py
index 5cde0202..5b1ee615 100644
--- a/src/smallestai/atoms/agent_templates/types/__init__.py
+++ b/src/smallestai/atoms/agent_templates/types/__init__.py
@@ -2,3 +2,83 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .list_agent_templates_agent_templates_request_region import ListAgentTemplatesAgentTemplatesRequestRegion
+ from .list_agent_templates_agent_templates_response import ListAgentTemplatesAgentTemplatesResponse
+ from .list_agent_templates_agent_templates_response_data_item import (
+ ListAgentTemplatesAgentTemplatesResponseDataItem,
+ )
+ from .list_agent_templates_agent_templates_response_data_item_call_type import (
+ ListAgentTemplatesAgentTemplatesResponseDataItemCallType,
+ )
+ from .list_agent_templates_agent_templates_response_data_item_default_language import (
+ ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage,
+ )
+ from .list_agent_templates_agent_templates_response_data_item_region import (
+ ListAgentTemplatesAgentTemplatesResponseDataItemRegion,
+ )
+ from .list_agent_templates_agent_templates_response_data_item_single_prompt_config import (
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig,
+ )
+ from .list_agent_templates_agent_templates_response_data_item_single_prompt_config_tools_item import (
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem,
+ )
+ from .list_agent_templates_agent_templates_response_data_item_single_prompt_config_tools_item_type import (
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType,
+ )
+ from .list_agent_templates_agent_templates_response_data_item_workflow_type import (
+ ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType,
+ )
+ from .post_agent_from_template_response import PostAgentFromTemplateResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "ListAgentTemplatesAgentTemplatesRequestRegion": ".list_agent_templates_agent_templates_request_region",
+ "ListAgentTemplatesAgentTemplatesResponse": ".list_agent_templates_agent_templates_response",
+ "ListAgentTemplatesAgentTemplatesResponseDataItem": ".list_agent_templates_agent_templates_response_data_item",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemCallType": ".list_agent_templates_agent_templates_response_data_item_call_type",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage": ".list_agent_templates_agent_templates_response_data_item_default_language",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemRegion": ".list_agent_templates_agent_templates_response_data_item_region",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig": ".list_agent_templates_agent_templates_response_data_item_single_prompt_config",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem": ".list_agent_templates_agent_templates_response_data_item_single_prompt_config_tools_item",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType": ".list_agent_templates_agent_templates_response_data_item_single_prompt_config_tools_item_type",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType": ".list_agent_templates_agent_templates_response_data_item_workflow_type",
+ "PostAgentFromTemplateResponse": ".post_agent_from_template_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ListAgentTemplatesAgentTemplatesRequestRegion",
+ "ListAgentTemplatesAgentTemplatesResponse",
+ "ListAgentTemplatesAgentTemplatesResponseDataItem",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemCallType",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemRegion",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType",
+ "PostAgentFromTemplateResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_branches/__init__.py b/src/smallestai/atoms/agent_versioning_branches/__init__.py
new file mode 100644
index 00000000..e6c7b257
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/__init__.py
@@ -0,0 +1,94 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ ArchiveAgentVersioningBranchesResponse,
+ ArchiveAgentVersioningBranchesResponseData,
+ CancelPublishAgentVersioningBranchesResponse,
+ CancelPublishAgentVersioningBranchesResponseData,
+ CreateBranchAgentVersioningBranchesResponse,
+ DiscardDraftAgentVersioningBranchesResponse,
+ DiscardDraftAgentVersioningBranchesResponseData,
+ GetAgentVersioningBranchesResponse,
+ GetDraftAgentVersioningBranchesResponse,
+ ListAgentVersioningBranchesResponse,
+ ListAgentVersioningBranchesResponseData,
+ MakeLiveAgentVersioningBranchesResponse,
+ PublishDraftAgentVersioningBranchesResponse,
+ RenameAgentVersioningBranchesResponse,
+ TestCallAgentVersioningBranchesResponse,
+ TestCallV2RequestMode,
+ UpdateBranchDraftRequestBackgroundSound,
+ UpdateBranchDraftRequestSlmModel,
+ UpdateDraftAgentVersioningBranchesResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ArchiveAgentVersioningBranchesResponse": ".types",
+ "ArchiveAgentVersioningBranchesResponseData": ".types",
+ "CancelPublishAgentVersioningBranchesResponse": ".types",
+ "CancelPublishAgentVersioningBranchesResponseData": ".types",
+ "CreateBranchAgentVersioningBranchesResponse": ".types",
+ "DiscardDraftAgentVersioningBranchesResponse": ".types",
+ "DiscardDraftAgentVersioningBranchesResponseData": ".types",
+ "GetAgentVersioningBranchesResponse": ".types",
+ "GetDraftAgentVersioningBranchesResponse": ".types",
+ "ListAgentVersioningBranchesResponse": ".types",
+ "ListAgentVersioningBranchesResponseData": ".types",
+ "MakeLiveAgentVersioningBranchesResponse": ".types",
+ "PublishDraftAgentVersioningBranchesResponse": ".types",
+ "RenameAgentVersioningBranchesResponse": ".types",
+ "TestCallAgentVersioningBranchesResponse": ".types",
+ "TestCallV2RequestMode": ".types",
+ "UpdateBranchDraftRequestBackgroundSound": ".types",
+ "UpdateBranchDraftRequestSlmModel": ".types",
+ "UpdateDraftAgentVersioningBranchesResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ArchiveAgentVersioningBranchesResponse",
+ "ArchiveAgentVersioningBranchesResponseData",
+ "CancelPublishAgentVersioningBranchesResponse",
+ "CancelPublishAgentVersioningBranchesResponseData",
+ "CreateBranchAgentVersioningBranchesResponse",
+ "DiscardDraftAgentVersioningBranchesResponse",
+ "DiscardDraftAgentVersioningBranchesResponseData",
+ "GetAgentVersioningBranchesResponse",
+ "GetDraftAgentVersioningBranchesResponse",
+ "ListAgentVersioningBranchesResponse",
+ "ListAgentVersioningBranchesResponseData",
+ "MakeLiveAgentVersioningBranchesResponse",
+ "PublishDraftAgentVersioningBranchesResponse",
+ "RenameAgentVersioningBranchesResponse",
+ "TestCallAgentVersioningBranchesResponse",
+ "TestCallV2RequestMode",
+ "UpdateBranchDraftRequestBackgroundSound",
+ "UpdateBranchDraftRequestSlmModel",
+ "UpdateDraftAgentVersioningBranchesResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_branches/client.py b/src/smallestai/atoms/agent_versioning_branches/client.py
new file mode 100644
index 00000000..700fa853
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/client.py
@@ -0,0 +1,1379 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from .raw_client import AsyncRawAgentVersioningBranchesClient, RawAgentVersioningBranchesClient
+from .types.archive_agent_versioning_branches_response import ArchiveAgentVersioningBranchesResponse
+from .types.cancel_publish_agent_versioning_branches_response import CancelPublishAgentVersioningBranchesResponse
+from .types.create_branch_agent_versioning_branches_response import CreateBranchAgentVersioningBranchesResponse
+from .types.discard_draft_agent_versioning_branches_response import DiscardDraftAgentVersioningBranchesResponse
+from .types.get_agent_versioning_branches_response import GetAgentVersioningBranchesResponse
+from .types.get_draft_agent_versioning_branches_response import GetDraftAgentVersioningBranchesResponse
+from .types.list_agent_versioning_branches_response import ListAgentVersioningBranchesResponse
+from .types.make_live_agent_versioning_branches_response import MakeLiveAgentVersioningBranchesResponse
+from .types.publish_draft_agent_versioning_branches_response import PublishDraftAgentVersioningBranchesResponse
+from .types.rename_agent_versioning_branches_response import RenameAgentVersioningBranchesResponse
+from .types.test_call_agent_versioning_branches_response import TestCallAgentVersioningBranchesResponse
+from .types.test_call_v2request_mode import TestCallV2RequestMode
+from .types.update_branch_draft_request_background_sound import UpdateBranchDraftRequestBackgroundSound
+from .types.update_branch_draft_request_slm_model import UpdateBranchDraftRequestSlmModel
+from .types.update_draft_agent_versioning_branches_response import UpdateDraftAgentVersioningBranchesResponse
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class AgentVersioningBranchesClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawAgentVersioningBranchesClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawAgentVersioningBranchesClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawAgentVersioningBranchesClient
+ """
+ return self._raw_client
+
+ def list(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ListAgentVersioningBranchesResponse:
+ """
+ List all non-archived branches for an agent, with per-branch draft and revision counts.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListAgentVersioningBranchesResponse
+ Successful response.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.list(
+ id="id",
+ )
+ """
+ _response = self._raw_client.list(id, request_options=request_options)
+ return _response.data
+
+ def create_branch(
+ self, id: str, *, source_branch_id: str, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> CreateBranchAgentVersioningBranchesResponse:
+ """
+ Fork a new branch from an existing branch. The source branch must have at least one committed revision. Branch names are unique per agent; the name `Main` is reserved for the default branch. Creating from a branch whose latest draft is still `scanning` returns `409 source_scanning`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ source_branch_id : str
+ Branch to fork from. Its head revision must exist.
+
+ name : str
+ New branch name. Unique per agent among active branches. `main` is reserved.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CreateBranchAgentVersioningBranchesResponse
+ Branch created.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.create_branch(
+ id="id",
+ source_branch_id="sourceBranchId",
+ name="name",
+ )
+ """
+ _response = self._raw_client.create_branch(
+ id, source_branch_id=source_branch_id, name=name, request_options=request_options
+ )
+ return _response.data
+
+ def get(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetAgentVersioningBranchesResponse:
+ """
+ Return a single branch summary, including draft state (`openDraftId`, `hasOpenDraft`) and head revision.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAgentVersioningBranchesResponse
+ Successful response.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.get(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.get(id, branch_id, request_options=request_options)
+ return _response.data
+
+ def rename(
+ self, id: str, branch_id: str, *, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> RenameAgentVersioningBranchesResponse:
+ """
+ Rename a non-default, non-archived branch. `Main` cannot be renamed; a name that is already in use on this agent returns `409 branch_name_exists`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ name : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ RenameAgentVersioningBranchesResponse
+ Renamed.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.rename(
+ id="id",
+ branch_id="branchId",
+ name="name",
+ )
+ """
+ _response = self._raw_client.rename(id, branch_id, name=name, request_options=request_options)
+ return _response.data
+
+ def archive(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ArchiveAgentVersioningBranchesResponse:
+ """
+ Archive a branch. `Main` cannot be archived. The live branch cannot be archived; make another branch live first. Archived branches are hidden from list views but their revisions remain queryable by ID.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ArchiveAgentVersioningBranchesResponse
+ Archived.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.archive(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.archive(id, branch_id, request_options=request_options)
+ return _response.data
+
+ def make_live(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> MakeLiveAgentVersioningBranchesResponse:
+ """
+ Make this branch the live branch. Its `headRevisionId` becomes the config that serves production traffic. The previously-live branch becomes not-live automatically. The branch must be non-archived, must have at least one `committed` (security-passed) revision, and its head revision must have passed the security scan.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ MakeLiveAgentVersioningBranchesResponse
+ Branch is now live.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.make_live(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.make_live(id, branch_id, request_options=request_options)
+ return _response.data
+
+ def get_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetDraftAgentVersioningBranchesResponse:
+ """
+ Return the currently-open draft on this branch, including per-edit history.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDraftAgentVersioningBranchesResponse
+ Successful response.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.get_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.get_draft(id, branch_id, request_options=request_options)
+ return _response.data
+
+ def update_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ expected_revision: typing.Optional[int] = OMIT,
+ global_prompt: typing.Optional[str] = OMIT,
+ first_message: typing.Optional[str] = OMIT,
+ slm_model: typing.Optional[UpdateBranchDraftRequestSlmModel] = OMIT,
+ background_sound: typing.Optional[UpdateBranchDraftRequestBackgroundSound] = OMIT,
+ timezone: typing.Optional[str] = OMIT,
+ global_knowledge_base_id: typing.Optional[str] = OMIT,
+ mute_user_until_first_bot_response: typing.Optional[bool] = OMIT,
+ allow_interruptions: typing.Optional[bool] = OMIT,
+ wait_for_user_to_speak_first: typing.Optional[bool] = OMIT,
+ interruption_backoff_timer: typing.Optional[float] = OMIT,
+ enable_style_guide: typing.Optional[bool] = OMIT,
+ synthesizer: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ language: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ default_variables: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pre_call_api: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ smart_turn_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_mail_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ denoising_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ redaction_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pronunciation_dicts: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ llm_idle_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ session_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ call_disposition_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ speech_formatting: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UpdateDraftAgentVersioningBranchesResponse:
+ """
+ Upsert the open draft on this branch. If no draft is open, one is created automatically. The request body is a free-form agent config partial and must contain at least one recognized field (`prompt`, `voice`, `model`, `tools`, `post_call_analytics`, etc.); the server merges it into the existing draft and returns the resulting draft as a revision-shaped snapshot.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ expected_revision : typing.Optional[int]
+ Optimistic-concurrency control. The `draftRevision` the client's edit was based on. When present, the server runs a field-level conflict check and rejects with `409 DraftConflictError` if the same field was changed by another edit since. Omit for last-write-wins semantics (which is also how a client force-overwrites after a `409`). Referencing a non-existent base revision returns `409 { errors: ["base_revision_unavailable"] }`.
+
+ global_prompt : typing.Optional[str]
+ Top-level system prompt shown to the agent every turn.
+
+ first_message : typing.Optional[str]
+ The agent's opening line at call start.
+
+ slm_model : typing.Optional[UpdateBranchDraftRequestSlmModel]
+ LLM model powering the agent. See `CreateAgentRequest.slmModel` for org-level access notes.
+
+ background_sound : typing.Optional[UpdateBranchDraftRequestBackgroundSound]
+ Ambient background sound during calls.
+
+ timezone : typing.Optional[str]
+ IANA timezone identifier used for date/time interpretation in prompts and tool calls.
+
+ global_knowledge_base_id : typing.Optional[str]
+ Knowledge base attached to the agent for retrieval-augmented responses.
+
+ mute_user_until_first_bot_response : typing.Optional[bool]
+
+ allow_interruptions : typing.Optional[bool]
+
+ wait_for_user_to_speak_first : typing.Optional[bool]
+
+ interruption_backoff_timer : typing.Optional[float]
+
+ enable_style_guide : typing.Optional[bool]
+
+ synthesizer : typing.Optional[typing.Dict[str, typing.Any]]
+ TTS (voice) configuration. Same shape as `CreateAgentRequest.synthesizer`.
+
+ language : typing.Optional[typing.Dict[str, typing.Any]]
+ Language configuration. Same shape as `CreateAgentRequest.language`.
+
+ default_variables : typing.Optional[typing.Dict[str, typing.Any]]
+ Default variables injected into prompts and tool calls.
+
+ pre_call_api : typing.Optional[typing.Dict[str, typing.Any]]
+ Pre-call API webhook config. Same shape as `CreateAgentRequest.preCallAPI`.
+
+ smart_turn_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_mail_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ denoising_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ redaction_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ pronunciation_dicts : typing.Optional[typing.Dict[str, typing.Any]]
+
+ llm_idle_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ session_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ call_disposition_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ speech_formatting : typing.Optional[typing.Dict[str, typing.Any]]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UpdateDraftAgentVersioningBranchesResponse
+ Draft updated.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.update_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.update_draft(
+ id,
+ branch_id,
+ expected_revision=expected_revision,
+ global_prompt=global_prompt,
+ first_message=first_message,
+ slm_model=slm_model,
+ background_sound=background_sound,
+ timezone=timezone,
+ global_knowledge_base_id=global_knowledge_base_id,
+ mute_user_until_first_bot_response=mute_user_until_first_bot_response,
+ allow_interruptions=allow_interruptions,
+ wait_for_user_to_speak_first=wait_for_user_to_speak_first,
+ interruption_backoff_timer=interruption_backoff_timer,
+ enable_style_guide=enable_style_guide,
+ synthesizer=synthesizer,
+ language=language,
+ default_variables=default_variables,
+ pre_call_api=pre_call_api,
+ smart_turn_config=smart_turn_config,
+ voice_detection_config=voice_detection_config,
+ voice_mail_detection_config=voice_mail_detection_config,
+ denoising_config=denoising_config,
+ redaction_config=redaction_config,
+ pronunciation_dicts=pronunciation_dicts,
+ llm_idle_timeout_config=llm_idle_timeout_config,
+ session_timeout_config=session_timeout_config,
+ call_disposition_config=call_disposition_config,
+ speech_formatting=speech_formatting,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def discard_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> DiscardDraftAgentVersioningBranchesResponse:
+ """
+ Discard the open draft on this branch. Any unpublished edits are lost. The last committed revision remains the branch head.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DiscardDraftAgentVersioningBranchesResponse
+ Discarded.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.discard_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.discard_draft(id, branch_id, request_options=request_options)
+ return _response.data
+
+ def publish_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ label: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> PublishDraftAgentVersioningBranchesResponse:
+ """
+ Publish the open draft on this branch as a new revision.
+
+ The response is `200` with `state: "committed"` when the security scan finishes synchronously, and `202` with `state: "scanning"` when the scan is deferred. A `scanning` revision is visible in history but cannot be restored or made live until it becomes `committed`. If the scan fails, the revision is left in `scanning` state and this endpoint returns `409` on subsequent publishes until the scan is retried.
+
+ Publishing on the live branch pushes to production immediately.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ label : typing.Optional[str]
+ Optional label saved on the committed revision.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ PublishDraftAgentVersioningBranchesResponse
+ Draft committed synchronously.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.publish_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.publish_draft(id, branch_id, label=label, request_options=request_options)
+ return _response.data
+
+ def cancel_publish(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> CancelPublishAgentVersioningBranchesResponse:
+ """
+ Cancel a publish that is currently scanning. Idempotent. Returns `200` even if no scan is active.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CancelPublishAgentVersioningBranchesResponse
+ Cancel processed.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.cancel_publish(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.cancel_publish(id, branch_id, request_options=request_options)
+ return _response.data
+
+ def test_call(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ include_draft: typing.Optional[bool] = OMIT,
+ revision_id: typing.Optional[str] = OMIT,
+ mode: typing.Optional[TestCallV2RequestMode] = OMIT,
+ to_phone: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> TestCallAgentVersioningBranchesResponse:
+ """
+ Start a test call using the branch's current config. Send `includeDraft: true` to test the open draft, or send `revisionId` to test a specific committed revision on the branch. Sending both is a validation error.
+
+ The response always includes `conversationId` and `callId`. For `webcall` and `chat`, it also includes `token`, `roomName`, and `host`. Those fields are omitted for `telephony`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ include_draft : typing.Optional[bool]
+ When `true`, the test call uses the branch's open draft. Mutually exclusive with `revisionId`.
+
+ revision_id : typing.Optional[str]
+ Test against a specific committed revision on the branch. Mutually exclusive with `includeDraft: true`.
+
+ mode : typing.Optional[TestCallV2RequestMode]
+
+ to_phone : typing.Optional[str]
+ E.164-formatted number. Required when `mode` is `telephony`. Omit for `webcall` and `chat`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ TestCallAgentVersioningBranchesResponse
+ Test call initiated.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_branches.test_call(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.test_call(
+ id,
+ branch_id,
+ include_draft=include_draft,
+ revision_id=revision_id,
+ mode=mode,
+ to_phone=to_phone,
+ request_options=request_options,
+ )
+ return _response.data
+
+
+class AsyncAgentVersioningBranchesClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawAgentVersioningBranchesClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawAgentVersioningBranchesClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawAgentVersioningBranchesClient
+ """
+ return self._raw_client
+
+ async def list(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ListAgentVersioningBranchesResponse:
+ """
+ List all non-archived branches for an agent, with per-branch draft and revision counts.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListAgentVersioningBranchesResponse
+ Successful response.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.list(
+ id="id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.list(id, request_options=request_options)
+ return _response.data
+
+ async def create_branch(
+ self, id: str, *, source_branch_id: str, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> CreateBranchAgentVersioningBranchesResponse:
+ """
+ Fork a new branch from an existing branch. The source branch must have at least one committed revision. Branch names are unique per agent; the name `Main` is reserved for the default branch. Creating from a branch whose latest draft is still `scanning` returns `409 source_scanning`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ source_branch_id : str
+ Branch to fork from. Its head revision must exist.
+
+ name : str
+ New branch name. Unique per agent among active branches. `main` is reserved.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CreateBranchAgentVersioningBranchesResponse
+ Branch created.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.create_branch(
+ id="id",
+ source_branch_id="sourceBranchId",
+ name="name",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.create_branch(
+ id, source_branch_id=source_branch_id, name=name, request_options=request_options
+ )
+ return _response.data
+
+ async def get(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetAgentVersioningBranchesResponse:
+ """
+ Return a single branch summary, including draft state (`openDraftId`, `hasOpenDraft`) and head revision.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAgentVersioningBranchesResponse
+ Successful response.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.get(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get(id, branch_id, request_options=request_options)
+ return _response.data
+
+ async def rename(
+ self, id: str, branch_id: str, *, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> RenameAgentVersioningBranchesResponse:
+ """
+ Rename a non-default, non-archived branch. `Main` cannot be renamed; a name that is already in use on this agent returns `409 branch_name_exists`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ name : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ RenameAgentVersioningBranchesResponse
+ Renamed.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.rename(
+ id="id",
+ branch_id="branchId",
+ name="name",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.rename(id, branch_id, name=name, request_options=request_options)
+ return _response.data
+
+ async def archive(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ArchiveAgentVersioningBranchesResponse:
+ """
+ Archive a branch. `Main` cannot be archived. The live branch cannot be archived; make another branch live first. Archived branches are hidden from list views but their revisions remain queryable by ID.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ArchiveAgentVersioningBranchesResponse
+ Archived.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.archive(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.archive(id, branch_id, request_options=request_options)
+ return _response.data
+
+ async def make_live(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> MakeLiveAgentVersioningBranchesResponse:
+ """
+ Make this branch the live branch. Its `headRevisionId` becomes the config that serves production traffic. The previously-live branch becomes not-live automatically. The branch must be non-archived, must have at least one `committed` (security-passed) revision, and its head revision must have passed the security scan.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ MakeLiveAgentVersioningBranchesResponse
+ Branch is now live.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.make_live(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.make_live(id, branch_id, request_options=request_options)
+ return _response.data
+
+ async def get_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetDraftAgentVersioningBranchesResponse:
+ """
+ Return the currently-open draft on this branch, including per-edit history.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDraftAgentVersioningBranchesResponse
+ Successful response.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.get_draft(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_draft(id, branch_id, request_options=request_options)
+ return _response.data
+
+ async def update_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ expected_revision: typing.Optional[int] = OMIT,
+ global_prompt: typing.Optional[str] = OMIT,
+ first_message: typing.Optional[str] = OMIT,
+ slm_model: typing.Optional[UpdateBranchDraftRequestSlmModel] = OMIT,
+ background_sound: typing.Optional[UpdateBranchDraftRequestBackgroundSound] = OMIT,
+ timezone: typing.Optional[str] = OMIT,
+ global_knowledge_base_id: typing.Optional[str] = OMIT,
+ mute_user_until_first_bot_response: typing.Optional[bool] = OMIT,
+ allow_interruptions: typing.Optional[bool] = OMIT,
+ wait_for_user_to_speak_first: typing.Optional[bool] = OMIT,
+ interruption_backoff_timer: typing.Optional[float] = OMIT,
+ enable_style_guide: typing.Optional[bool] = OMIT,
+ synthesizer: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ language: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ default_variables: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pre_call_api: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ smart_turn_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_mail_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ denoising_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ redaction_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pronunciation_dicts: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ llm_idle_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ session_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ call_disposition_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ speech_formatting: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UpdateDraftAgentVersioningBranchesResponse:
+ """
+ Upsert the open draft on this branch. If no draft is open, one is created automatically. The request body is a free-form agent config partial and must contain at least one recognized field (`prompt`, `voice`, `model`, `tools`, `post_call_analytics`, etc.); the server merges it into the existing draft and returns the resulting draft as a revision-shaped snapshot.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ expected_revision : typing.Optional[int]
+ Optimistic-concurrency control. The `draftRevision` the client's edit was based on. When present, the server runs a field-level conflict check and rejects with `409 DraftConflictError` if the same field was changed by another edit since. Omit for last-write-wins semantics (which is also how a client force-overwrites after a `409`). Referencing a non-existent base revision returns `409 { errors: ["base_revision_unavailable"] }`.
+
+ global_prompt : typing.Optional[str]
+ Top-level system prompt shown to the agent every turn.
+
+ first_message : typing.Optional[str]
+ The agent's opening line at call start.
+
+ slm_model : typing.Optional[UpdateBranchDraftRequestSlmModel]
+ LLM model powering the agent. See `CreateAgentRequest.slmModel` for org-level access notes.
+
+ background_sound : typing.Optional[UpdateBranchDraftRequestBackgroundSound]
+ Ambient background sound during calls.
+
+ timezone : typing.Optional[str]
+ IANA timezone identifier used for date/time interpretation in prompts and tool calls.
+
+ global_knowledge_base_id : typing.Optional[str]
+ Knowledge base attached to the agent for retrieval-augmented responses.
+
+ mute_user_until_first_bot_response : typing.Optional[bool]
+
+ allow_interruptions : typing.Optional[bool]
+
+ wait_for_user_to_speak_first : typing.Optional[bool]
+
+ interruption_backoff_timer : typing.Optional[float]
+
+ enable_style_guide : typing.Optional[bool]
+
+ synthesizer : typing.Optional[typing.Dict[str, typing.Any]]
+ TTS (voice) configuration. Same shape as `CreateAgentRequest.synthesizer`.
+
+ language : typing.Optional[typing.Dict[str, typing.Any]]
+ Language configuration. Same shape as `CreateAgentRequest.language`.
+
+ default_variables : typing.Optional[typing.Dict[str, typing.Any]]
+ Default variables injected into prompts and tool calls.
+
+ pre_call_api : typing.Optional[typing.Dict[str, typing.Any]]
+ Pre-call API webhook config. Same shape as `CreateAgentRequest.preCallAPI`.
+
+ smart_turn_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_mail_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ denoising_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ redaction_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ pronunciation_dicts : typing.Optional[typing.Dict[str, typing.Any]]
+
+ llm_idle_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ session_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ call_disposition_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ speech_formatting : typing.Optional[typing.Dict[str, typing.Any]]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UpdateDraftAgentVersioningBranchesResponse
+ Draft updated.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.update_draft(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.update_draft(
+ id,
+ branch_id,
+ expected_revision=expected_revision,
+ global_prompt=global_prompt,
+ first_message=first_message,
+ slm_model=slm_model,
+ background_sound=background_sound,
+ timezone=timezone,
+ global_knowledge_base_id=global_knowledge_base_id,
+ mute_user_until_first_bot_response=mute_user_until_first_bot_response,
+ allow_interruptions=allow_interruptions,
+ wait_for_user_to_speak_first=wait_for_user_to_speak_first,
+ interruption_backoff_timer=interruption_backoff_timer,
+ enable_style_guide=enable_style_guide,
+ synthesizer=synthesizer,
+ language=language,
+ default_variables=default_variables,
+ pre_call_api=pre_call_api,
+ smart_turn_config=smart_turn_config,
+ voice_detection_config=voice_detection_config,
+ voice_mail_detection_config=voice_mail_detection_config,
+ denoising_config=denoising_config,
+ redaction_config=redaction_config,
+ pronunciation_dicts=pronunciation_dicts,
+ llm_idle_timeout_config=llm_idle_timeout_config,
+ session_timeout_config=session_timeout_config,
+ call_disposition_config=call_disposition_config,
+ speech_formatting=speech_formatting,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def discard_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> DiscardDraftAgentVersioningBranchesResponse:
+ """
+ Discard the open draft on this branch. Any unpublished edits are lost. The last committed revision remains the branch head.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DiscardDraftAgentVersioningBranchesResponse
+ Discarded.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.discard_draft(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.discard_draft(id, branch_id, request_options=request_options)
+ return _response.data
+
+ async def publish_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ label: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> PublishDraftAgentVersioningBranchesResponse:
+ """
+ Publish the open draft on this branch as a new revision.
+
+ The response is `200` with `state: "committed"` when the security scan finishes synchronously, and `202` with `state: "scanning"` when the scan is deferred. A `scanning` revision is visible in history but cannot be restored or made live until it becomes `committed`. If the scan fails, the revision is left in `scanning` state and this endpoint returns `409` on subsequent publishes until the scan is retried.
+
+ Publishing on the live branch pushes to production immediately.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ label : typing.Optional[str]
+ Optional label saved on the committed revision.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ PublishDraftAgentVersioningBranchesResponse
+ Draft committed synchronously.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.publish_draft(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.publish_draft(id, branch_id, label=label, request_options=request_options)
+ return _response.data
+
+ async def cancel_publish(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> CancelPublishAgentVersioningBranchesResponse:
+ """
+ Cancel a publish that is currently scanning. Idempotent. Returns `200` even if no scan is active.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CancelPublishAgentVersioningBranchesResponse
+ Cancel processed.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.cancel_publish(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.cancel_publish(id, branch_id, request_options=request_options)
+ return _response.data
+
+ async def test_call(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ include_draft: typing.Optional[bool] = OMIT,
+ revision_id: typing.Optional[str] = OMIT,
+ mode: typing.Optional[TestCallV2RequestMode] = OMIT,
+ to_phone: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> TestCallAgentVersioningBranchesResponse:
+ """
+ Start a test call using the branch's current config. Send `includeDraft: true` to test the open draft, or send `revisionId` to test a specific committed revision on the branch. Sending both is a validation error.
+
+ The response always includes `conversationId` and `callId`. For `webcall` and `chat`, it also includes `token`, `roomName`, and `host`. Those fields are omitted for `telephony`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ include_draft : typing.Optional[bool]
+ When `true`, the test call uses the branch's open draft. Mutually exclusive with `revisionId`.
+
+ revision_id : typing.Optional[str]
+ Test against a specific committed revision on the branch. Mutually exclusive with `includeDraft: true`.
+
+ mode : typing.Optional[TestCallV2RequestMode]
+
+ to_phone : typing.Optional[str]
+ E.164-formatted number. Required when `mode` is `telephony`. Omit for `webcall` and `chat`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ TestCallAgentVersioningBranchesResponse
+ Test call initiated.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_branches.test_call(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.test_call(
+ id,
+ branch_id,
+ include_draft=include_draft,
+ revision_id=revision_id,
+ mode=mode,
+ to_phone=to_phone,
+ request_options=request_options,
+ )
+ return _response.data
diff --git a/src/smallestai/atoms/agent_versioning_branches/raw_client.py b/src/smallestai/atoms/agent_versioning_branches/raw_client.py
new file mode 100644
index 00000000..9e2f754d
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/raw_client.py
@@ -0,0 +1,3101 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.jsonable_encoder import encode_path_param
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.unchecked_base_model import construct_type
+from ..errors.bad_request_error import BadRequestError
+from ..errors.conflict_error import ConflictError
+from ..errors.forbidden_error import ForbiddenError
+from ..errors.internal_server_error import InternalServerError
+from ..errors.locked_error import LockedError
+from ..errors.not_found_error import NotFoundError
+from ..errors.unauthorized_error import UnauthorizedError
+from ..types.locked_error_response import LockedErrorResponse
+from .types.archive_agent_versioning_branches_response import ArchiveAgentVersioningBranchesResponse
+from .types.cancel_publish_agent_versioning_branches_response import CancelPublishAgentVersioningBranchesResponse
+from .types.create_branch_agent_versioning_branches_response import CreateBranchAgentVersioningBranchesResponse
+from .types.discard_draft_agent_versioning_branches_response import DiscardDraftAgentVersioningBranchesResponse
+from .types.get_agent_versioning_branches_response import GetAgentVersioningBranchesResponse
+from .types.get_draft_agent_versioning_branches_response import GetDraftAgentVersioningBranchesResponse
+from .types.list_agent_versioning_branches_response import ListAgentVersioningBranchesResponse
+from .types.make_live_agent_versioning_branches_response import MakeLiveAgentVersioningBranchesResponse
+from .types.publish_draft_agent_versioning_branches_response import PublishDraftAgentVersioningBranchesResponse
+from .types.rename_agent_versioning_branches_response import RenameAgentVersioningBranchesResponse
+from .types.test_call_agent_versioning_branches_response import TestCallAgentVersioningBranchesResponse
+from .types.test_call_v2request_mode import TestCallV2RequestMode
+from .types.update_branch_draft_request_background_sound import UpdateBranchDraftRequestBackgroundSound
+from .types.update_branch_draft_request_slm_model import UpdateBranchDraftRequestSlmModel
+from .types.update_draft_agent_versioning_branches_response import UpdateDraftAgentVersioningBranchesResponse
+from pydantic import ValidationError
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class RawAgentVersioningBranchesClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def list(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[ListAgentVersioningBranchesResponse]:
+ """
+ List all non-archived branches for an agent, with per-branch draft and revision counts.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[ListAgentVersioningBranchesResponse]
+ Successful response.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListAgentVersioningBranchesResponse,
+ construct_type(
+ type_=ListAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def create_branch(
+ self, id: str, *, source_branch_id: str, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[CreateBranchAgentVersioningBranchesResponse]:
+ """
+ Fork a new branch from an existing branch. The source branch must have at least one committed revision. Branch names are unique per agent; the name `Main` is reserved for the default branch. Creating from a branch whose latest draft is still `scanning` returns `409 source_scanning`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ source_branch_id : str
+ Branch to fork from. Its head revision must exist.
+
+ name : str
+ New branch name. Unique per agent among active branches. `main` is reserved.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[CreateBranchAgentVersioningBranchesResponse]
+ Branch created.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "sourceBranchId": source_branch_id,
+ "name": name,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ CreateBranchAgentVersioningBranchesResponse,
+ construct_type(
+ type_=CreateBranchAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetAgentVersioningBranchesResponse]:
+ """
+ Return a single branch summary, including draft state (`openDraftId`, `hasOpenDraft`) and head revision.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetAgentVersioningBranchesResponse]
+ Successful response.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAgentVersioningBranchesResponse,
+ construct_type(
+ type_=GetAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def rename(
+ self, id: str, branch_id: str, *, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[RenameAgentVersioningBranchesResponse]:
+ """
+ Rename a non-default, non-archived branch. `Main` cannot be renamed; a name that is already in use on this agent returns `409 branch_name_exists`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ name : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[RenameAgentVersioningBranchesResponse]
+ Renamed.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PATCH",
+ json={
+ "name": name,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ RenameAgentVersioningBranchesResponse,
+ construct_type(
+ type_=RenameAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def archive(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[ArchiveAgentVersioningBranchesResponse]:
+ """
+ Archive a branch. `Main` cannot be archived. The live branch cannot be archived; make another branch live first. Archived branches are hidden from list views but their revisions remain queryable by ID.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[ArchiveAgentVersioningBranchesResponse]
+ Archived.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/archive",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ArchiveAgentVersioningBranchesResponse,
+ construct_type(
+ type_=ArchiveAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def make_live(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[MakeLiveAgentVersioningBranchesResponse]:
+ """
+ Make this branch the live branch. Its `headRevisionId` becomes the config that serves production traffic. The previously-live branch becomes not-live automatically. The branch must be non-archived, must have at least one `committed` (security-passed) revision, and its head revision must have passed the security scan.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[MakeLiveAgentVersioningBranchesResponse]
+ Branch is now live.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/live",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ MakeLiveAgentVersioningBranchesResponse,
+ construct_type(
+ type_=MakeLiveAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetDraftAgentVersioningBranchesResponse]:
+ """
+ Return the currently-open draft on this branch, including per-edit history.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetDraftAgentVersioningBranchesResponse]
+ Successful response.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=GetDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def update_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ expected_revision: typing.Optional[int] = OMIT,
+ global_prompt: typing.Optional[str] = OMIT,
+ first_message: typing.Optional[str] = OMIT,
+ slm_model: typing.Optional[UpdateBranchDraftRequestSlmModel] = OMIT,
+ background_sound: typing.Optional[UpdateBranchDraftRequestBackgroundSound] = OMIT,
+ timezone: typing.Optional[str] = OMIT,
+ global_knowledge_base_id: typing.Optional[str] = OMIT,
+ mute_user_until_first_bot_response: typing.Optional[bool] = OMIT,
+ allow_interruptions: typing.Optional[bool] = OMIT,
+ wait_for_user_to_speak_first: typing.Optional[bool] = OMIT,
+ interruption_backoff_timer: typing.Optional[float] = OMIT,
+ enable_style_guide: typing.Optional[bool] = OMIT,
+ synthesizer: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ language: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ default_variables: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pre_call_api: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ smart_turn_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_mail_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ denoising_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ redaction_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pronunciation_dicts: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ llm_idle_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ session_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ call_disposition_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ speech_formatting: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[UpdateDraftAgentVersioningBranchesResponse]:
+ """
+ Upsert the open draft on this branch. If no draft is open, one is created automatically. The request body is a free-form agent config partial and must contain at least one recognized field (`prompt`, `voice`, `model`, `tools`, `post_call_analytics`, etc.); the server merges it into the existing draft and returns the resulting draft as a revision-shaped snapshot.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ expected_revision : typing.Optional[int]
+ Optimistic-concurrency control. The `draftRevision` the client's edit was based on. When present, the server runs a field-level conflict check and rejects with `409 DraftConflictError` if the same field was changed by another edit since. Omit for last-write-wins semantics (which is also how a client force-overwrites after a `409`). Referencing a non-existent base revision returns `409 { errors: ["base_revision_unavailable"] }`.
+
+ global_prompt : typing.Optional[str]
+ Top-level system prompt shown to the agent every turn.
+
+ first_message : typing.Optional[str]
+ The agent's opening line at call start.
+
+ slm_model : typing.Optional[UpdateBranchDraftRequestSlmModel]
+ LLM model powering the agent. See `CreateAgentRequest.slmModel` for org-level access notes.
+
+ background_sound : typing.Optional[UpdateBranchDraftRequestBackgroundSound]
+ Ambient background sound during calls.
+
+ timezone : typing.Optional[str]
+ IANA timezone identifier used for date/time interpretation in prompts and tool calls.
+
+ global_knowledge_base_id : typing.Optional[str]
+ Knowledge base attached to the agent for retrieval-augmented responses.
+
+ mute_user_until_first_bot_response : typing.Optional[bool]
+
+ allow_interruptions : typing.Optional[bool]
+
+ wait_for_user_to_speak_first : typing.Optional[bool]
+
+ interruption_backoff_timer : typing.Optional[float]
+
+ enable_style_guide : typing.Optional[bool]
+
+ synthesizer : typing.Optional[typing.Dict[str, typing.Any]]
+ TTS (voice) configuration. Same shape as `CreateAgentRequest.synthesizer`.
+
+ language : typing.Optional[typing.Dict[str, typing.Any]]
+ Language configuration. Same shape as `CreateAgentRequest.language`.
+
+ default_variables : typing.Optional[typing.Dict[str, typing.Any]]
+ Default variables injected into prompts and tool calls.
+
+ pre_call_api : typing.Optional[typing.Dict[str, typing.Any]]
+ Pre-call API webhook config. Same shape as `CreateAgentRequest.preCallAPI`.
+
+ smart_turn_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_mail_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ denoising_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ redaction_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ pronunciation_dicts : typing.Optional[typing.Dict[str, typing.Any]]
+
+ llm_idle_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ session_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ call_disposition_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ speech_formatting : typing.Optional[typing.Dict[str, typing.Any]]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[UpdateDraftAgentVersioningBranchesResponse]
+ Draft updated.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PUT",
+ json={
+ "expectedRevision": expected_revision,
+ "globalPrompt": global_prompt,
+ "firstMessage": first_message,
+ "slmModel": slm_model,
+ "backgroundSound": background_sound,
+ "timezone": timezone,
+ "globalKnowledgeBaseId": global_knowledge_base_id,
+ "muteUserUntilFirstBotResponse": mute_user_until_first_bot_response,
+ "allowInterruptions": allow_interruptions,
+ "waitForUserToSpeakFirst": wait_for_user_to_speak_first,
+ "interruptionBackoffTimer": interruption_backoff_timer,
+ "enableStyleGuide": enable_style_guide,
+ "synthesizer": synthesizer,
+ "language": language,
+ "defaultVariables": default_variables,
+ "preCallAPI": pre_call_api,
+ "smartTurnConfig": smart_turn_config,
+ "voiceDetectionConfig": voice_detection_config,
+ "voiceMailDetectionConfig": voice_mail_detection_config,
+ "denoisingConfig": denoising_config,
+ "redactionConfig": redaction_config,
+ "pronunciationDicts": pronunciation_dicts,
+ "llmIdleTimeoutConfig": llm_idle_timeout_config,
+ "sessionTimeoutConfig": session_timeout_config,
+ "callDispositionConfig": call_disposition_config,
+ "speechFormatting": speech_formatting,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ UpdateDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=UpdateDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def discard_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[DiscardDraftAgentVersioningBranchesResponse]:
+ """
+ Discard the open draft on this branch. Any unpublished edits are lost. The last committed revision remains the branch head.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[DiscardDraftAgentVersioningBranchesResponse]
+ Discarded.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="DELETE",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ DiscardDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=DiscardDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def publish_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ label: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[PublishDraftAgentVersioningBranchesResponse]:
+ """
+ Publish the open draft on this branch as a new revision.
+
+ The response is `200` with `state: "committed"` when the security scan finishes synchronously, and `202` with `state: "scanning"` when the scan is deferred. A `scanning` revision is visible in history but cannot be restored or made live until it becomes `committed`. If the scan fails, the revision is left in `scanning` state and this endpoint returns `409` on subsequent publishes until the scan is retried.
+
+ Publishing on the live branch pushes to production immediately.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ label : typing.Optional[str]
+ Optional label saved on the committed revision.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[PublishDraftAgentVersioningBranchesResponse]
+ Draft committed synchronously.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft/publish",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "label": label,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ PublishDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=PublishDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def cancel_publish(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[CancelPublishAgentVersioningBranchesResponse]:
+ """
+ Cancel a publish that is currently scanning. Idempotent. Returns `200` even if no scan is active.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[CancelPublishAgentVersioningBranchesResponse]
+ Cancel processed.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft/publish/cancel",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ CancelPublishAgentVersioningBranchesResponse,
+ construct_type(
+ type_=CancelPublishAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def test_call(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ include_draft: typing.Optional[bool] = OMIT,
+ revision_id: typing.Optional[str] = OMIT,
+ mode: typing.Optional[TestCallV2RequestMode] = OMIT,
+ to_phone: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[TestCallAgentVersioningBranchesResponse]:
+ """
+ Start a test call using the branch's current config. Send `includeDraft: true` to test the open draft, or send `revisionId` to test a specific committed revision on the branch. Sending both is a validation error.
+
+ The response always includes `conversationId` and `callId`. For `webcall` and `chat`, it also includes `token`, `roomName`, and `host`. Those fields are omitted for `telephony`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ include_draft : typing.Optional[bool]
+ When `true`, the test call uses the branch's open draft. Mutually exclusive with `revisionId`.
+
+ revision_id : typing.Optional[str]
+ Test against a specific committed revision on the branch. Mutually exclusive with `includeDraft: true`.
+
+ mode : typing.Optional[TestCallV2RequestMode]
+
+ to_phone : typing.Optional[str]
+ E.164-formatted number. Required when `mode` is `telephony`. Omit for `webcall` and `chat`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[TestCallAgentVersioningBranchesResponse]
+ Test call initiated.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/test-call",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "includeDraft": include_draft,
+ "revisionId": revision_id,
+ "mode": mode,
+ "toPhone": to_phone,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ TestCallAgentVersioningBranchesResponse,
+ construct_type(
+ type_=TestCallAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawAgentVersioningBranchesClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def list(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[ListAgentVersioningBranchesResponse]:
+ """
+ List all non-archived branches for an agent, with per-branch draft and revision counts.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ListAgentVersioningBranchesResponse]
+ Successful response.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListAgentVersioningBranchesResponse,
+ construct_type(
+ type_=ListAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def create_branch(
+ self, id: str, *, source_branch_id: str, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[CreateBranchAgentVersioningBranchesResponse]:
+ """
+ Fork a new branch from an existing branch. The source branch must have at least one committed revision. Branch names are unique per agent; the name `Main` is reserved for the default branch. Creating from a branch whose latest draft is still `scanning` returns `409 source_scanning`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ source_branch_id : str
+ Branch to fork from. Its head revision must exist.
+
+ name : str
+ New branch name. Unique per agent among active branches. `main` is reserved.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[CreateBranchAgentVersioningBranchesResponse]
+ Branch created.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "sourceBranchId": source_branch_id,
+ "name": name,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ CreateBranchAgentVersioningBranchesResponse,
+ construct_type(
+ type_=CreateBranchAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetAgentVersioningBranchesResponse]:
+ """
+ Return a single branch summary, including draft state (`openDraftId`, `hasOpenDraft`) and head revision.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetAgentVersioningBranchesResponse]
+ Successful response.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAgentVersioningBranchesResponse,
+ construct_type(
+ type_=GetAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def rename(
+ self, id: str, branch_id: str, *, name: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[RenameAgentVersioningBranchesResponse]:
+ """
+ Rename a non-default, non-archived branch. `Main` cannot be renamed; a name that is already in use on this agent returns `409 branch_name_exists`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ name : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[RenameAgentVersioningBranchesResponse]
+ Renamed.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PATCH",
+ json={
+ "name": name,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ RenameAgentVersioningBranchesResponse,
+ construct_type(
+ type_=RenameAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def archive(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[ArchiveAgentVersioningBranchesResponse]:
+ """
+ Archive a branch. `Main` cannot be archived. The live branch cannot be archived; make another branch live first. Archived branches are hidden from list views but their revisions remain queryable by ID.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ArchiveAgentVersioningBranchesResponse]
+ Archived.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/archive",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ArchiveAgentVersioningBranchesResponse,
+ construct_type(
+ type_=ArchiveAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def make_live(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[MakeLiveAgentVersioningBranchesResponse]:
+ """
+ Make this branch the live branch. Its `headRevisionId` becomes the config that serves production traffic. The previously-live branch becomes not-live automatically. The branch must be non-archived, must have at least one `committed` (security-passed) revision, and its head revision must have passed the security scan.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[MakeLiveAgentVersioningBranchesResponse]
+ Branch is now live.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/live",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ MakeLiveAgentVersioningBranchesResponse,
+ construct_type(
+ type_=MakeLiveAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetDraftAgentVersioningBranchesResponse]:
+ """
+ Return the currently-open draft on this branch, including per-edit history.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetDraftAgentVersioningBranchesResponse]
+ Successful response.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=GetDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def update_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ expected_revision: typing.Optional[int] = OMIT,
+ global_prompt: typing.Optional[str] = OMIT,
+ first_message: typing.Optional[str] = OMIT,
+ slm_model: typing.Optional[UpdateBranchDraftRequestSlmModel] = OMIT,
+ background_sound: typing.Optional[UpdateBranchDraftRequestBackgroundSound] = OMIT,
+ timezone: typing.Optional[str] = OMIT,
+ global_knowledge_base_id: typing.Optional[str] = OMIT,
+ mute_user_until_first_bot_response: typing.Optional[bool] = OMIT,
+ allow_interruptions: typing.Optional[bool] = OMIT,
+ wait_for_user_to_speak_first: typing.Optional[bool] = OMIT,
+ interruption_backoff_timer: typing.Optional[float] = OMIT,
+ enable_style_guide: typing.Optional[bool] = OMIT,
+ synthesizer: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ language: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ default_variables: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pre_call_api: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ smart_turn_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ voice_mail_detection_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ denoising_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ redaction_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ pronunciation_dicts: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ llm_idle_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ session_timeout_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ call_disposition_config: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ speech_formatting: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[UpdateDraftAgentVersioningBranchesResponse]:
+ """
+ Upsert the open draft on this branch. If no draft is open, one is created automatically. The request body is a free-form agent config partial and must contain at least one recognized field (`prompt`, `voice`, `model`, `tools`, `post_call_analytics`, etc.); the server merges it into the existing draft and returns the resulting draft as a revision-shaped snapshot.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ expected_revision : typing.Optional[int]
+ Optimistic-concurrency control. The `draftRevision` the client's edit was based on. When present, the server runs a field-level conflict check and rejects with `409 DraftConflictError` if the same field was changed by another edit since. Omit for last-write-wins semantics (which is also how a client force-overwrites after a `409`). Referencing a non-existent base revision returns `409 { errors: ["base_revision_unavailable"] }`.
+
+ global_prompt : typing.Optional[str]
+ Top-level system prompt shown to the agent every turn.
+
+ first_message : typing.Optional[str]
+ The agent's opening line at call start.
+
+ slm_model : typing.Optional[UpdateBranchDraftRequestSlmModel]
+ LLM model powering the agent. See `CreateAgentRequest.slmModel` for org-level access notes.
+
+ background_sound : typing.Optional[UpdateBranchDraftRequestBackgroundSound]
+ Ambient background sound during calls.
+
+ timezone : typing.Optional[str]
+ IANA timezone identifier used for date/time interpretation in prompts and tool calls.
+
+ global_knowledge_base_id : typing.Optional[str]
+ Knowledge base attached to the agent for retrieval-augmented responses.
+
+ mute_user_until_first_bot_response : typing.Optional[bool]
+
+ allow_interruptions : typing.Optional[bool]
+
+ wait_for_user_to_speak_first : typing.Optional[bool]
+
+ interruption_backoff_timer : typing.Optional[float]
+
+ enable_style_guide : typing.Optional[bool]
+
+ synthesizer : typing.Optional[typing.Dict[str, typing.Any]]
+ TTS (voice) configuration. Same shape as `CreateAgentRequest.synthesizer`.
+
+ language : typing.Optional[typing.Dict[str, typing.Any]]
+ Language configuration. Same shape as `CreateAgentRequest.language`.
+
+ default_variables : typing.Optional[typing.Dict[str, typing.Any]]
+ Default variables injected into prompts and tool calls.
+
+ pre_call_api : typing.Optional[typing.Dict[str, typing.Any]]
+ Pre-call API webhook config. Same shape as `CreateAgentRequest.preCallAPI`.
+
+ smart_turn_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ voice_mail_detection_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ denoising_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ redaction_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ pronunciation_dicts : typing.Optional[typing.Dict[str, typing.Any]]
+
+ llm_idle_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ session_timeout_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ call_disposition_config : typing.Optional[typing.Dict[str, typing.Any]]
+
+ speech_formatting : typing.Optional[typing.Dict[str, typing.Any]]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[UpdateDraftAgentVersioningBranchesResponse]
+ Draft updated.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PUT",
+ json={
+ "expectedRevision": expected_revision,
+ "globalPrompt": global_prompt,
+ "firstMessage": first_message,
+ "slmModel": slm_model,
+ "backgroundSound": background_sound,
+ "timezone": timezone,
+ "globalKnowledgeBaseId": global_knowledge_base_id,
+ "muteUserUntilFirstBotResponse": mute_user_until_first_bot_response,
+ "allowInterruptions": allow_interruptions,
+ "waitForUserToSpeakFirst": wait_for_user_to_speak_first,
+ "interruptionBackoffTimer": interruption_backoff_timer,
+ "enableStyleGuide": enable_style_guide,
+ "synthesizer": synthesizer,
+ "language": language,
+ "defaultVariables": default_variables,
+ "preCallAPI": pre_call_api,
+ "smartTurnConfig": smart_turn_config,
+ "voiceDetectionConfig": voice_detection_config,
+ "voiceMailDetectionConfig": voice_mail_detection_config,
+ "denoisingConfig": denoising_config,
+ "redactionConfig": redaction_config,
+ "pronunciationDicts": pronunciation_dicts,
+ "llmIdleTimeoutConfig": llm_idle_timeout_config,
+ "sessionTimeoutConfig": session_timeout_config,
+ "callDispositionConfig": call_disposition_config,
+ "speechFormatting": speech_formatting,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ UpdateDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=UpdateDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def discard_draft(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[DiscardDraftAgentVersioningBranchesResponse]:
+ """
+ Discard the open draft on this branch. Any unpublished edits are lost. The last committed revision remains the branch head.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[DiscardDraftAgentVersioningBranchesResponse]
+ Discarded.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="DELETE",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ DiscardDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=DiscardDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def publish_draft(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ label: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[PublishDraftAgentVersioningBranchesResponse]:
+ """
+ Publish the open draft on this branch as a new revision.
+
+ The response is `200` with `state: "committed"` when the security scan finishes synchronously, and `202` with `state: "scanning"` when the scan is deferred. A `scanning` revision is visible in history but cannot be restored or made live until it becomes `committed`. If the scan fails, the revision is left in `scanning` state and this endpoint returns `409` on subsequent publishes until the scan is retried.
+
+ Publishing on the live branch pushes to production immediately.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ label : typing.Optional[str]
+ Optional label saved on the committed revision.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[PublishDraftAgentVersioningBranchesResponse]
+ Draft committed synchronously.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft/publish",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "label": label,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ PublishDraftAgentVersioningBranchesResponse,
+ construct_type(
+ type_=PublishDraftAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def cancel_publish(
+ self, id: str, branch_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[CancelPublishAgentVersioningBranchesResponse]:
+ """
+ Cancel a publish that is currently scanning. Idempotent. Returns `200` even if no scan is active.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[CancelPublishAgentVersioningBranchesResponse]
+ Cancel processed.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/draft/publish/cancel",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ CancelPublishAgentVersioningBranchesResponse,
+ construct_type(
+ type_=CancelPublishAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def test_call(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ include_draft: typing.Optional[bool] = OMIT,
+ revision_id: typing.Optional[str] = OMIT,
+ mode: typing.Optional[TestCallV2RequestMode] = OMIT,
+ to_phone: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[TestCallAgentVersioningBranchesResponse]:
+ """
+ Start a test call using the branch's current config. Send `includeDraft: true` to test the open draft, or send `revisionId` to test a specific committed revision on the branch. Sending both is a validation error.
+
+ The response always includes `conversationId` and `callId`. For `webcall` and `chat`, it also includes `token`, `roomName`, and `host`. Those fields are omitted for `telephony`.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ include_draft : typing.Optional[bool]
+ When `true`, the test call uses the branch's open draft. Mutually exclusive with `revisionId`.
+
+ revision_id : typing.Optional[str]
+ Test against a specific committed revision on the branch. Mutually exclusive with `includeDraft: true`.
+
+ mode : typing.Optional[TestCallV2RequestMode]
+
+ to_phone : typing.Optional[str]
+ E.164-formatted number. Required when `mode` is `telephony`. Omit for `webcall` and `chat`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[TestCallAgentVersioningBranchesResponse]
+ Test call initiated.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/test-call",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "includeDraft": include_draft,
+ "revisionId": revision_id,
+ "mode": mode,
+ "toPhone": to_phone,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ TestCallAgentVersioningBranchesResponse,
+ construct_type(
+ type_=TestCallAgentVersioningBranchesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/__init__.py b/src/smallestai/atoms/agent_versioning_branches/types/__init__.py
new file mode 100644
index 00000000..0f904b35
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/__init__.py
@@ -0,0 +1,92 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .archive_agent_versioning_branches_response import ArchiveAgentVersioningBranchesResponse
+ from .archive_agent_versioning_branches_response_data import ArchiveAgentVersioningBranchesResponseData
+ from .cancel_publish_agent_versioning_branches_response import CancelPublishAgentVersioningBranchesResponse
+ from .cancel_publish_agent_versioning_branches_response_data import CancelPublishAgentVersioningBranchesResponseData
+ from .create_branch_agent_versioning_branches_response import CreateBranchAgentVersioningBranchesResponse
+ from .discard_draft_agent_versioning_branches_response import DiscardDraftAgentVersioningBranchesResponse
+ from .discard_draft_agent_versioning_branches_response_data import DiscardDraftAgentVersioningBranchesResponseData
+ from .get_agent_versioning_branches_response import GetAgentVersioningBranchesResponse
+ from .get_draft_agent_versioning_branches_response import GetDraftAgentVersioningBranchesResponse
+ from .list_agent_versioning_branches_response import ListAgentVersioningBranchesResponse
+ from .list_agent_versioning_branches_response_data import ListAgentVersioningBranchesResponseData
+ from .make_live_agent_versioning_branches_response import MakeLiveAgentVersioningBranchesResponse
+ from .publish_draft_agent_versioning_branches_response import PublishDraftAgentVersioningBranchesResponse
+ from .rename_agent_versioning_branches_response import RenameAgentVersioningBranchesResponse
+ from .test_call_agent_versioning_branches_response import TestCallAgentVersioningBranchesResponse
+ from .test_call_v2request_mode import TestCallV2RequestMode
+ from .update_branch_draft_request_background_sound import UpdateBranchDraftRequestBackgroundSound
+ from .update_branch_draft_request_slm_model import UpdateBranchDraftRequestSlmModel
+ from .update_draft_agent_versioning_branches_response import UpdateDraftAgentVersioningBranchesResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "ArchiveAgentVersioningBranchesResponse": ".archive_agent_versioning_branches_response",
+ "ArchiveAgentVersioningBranchesResponseData": ".archive_agent_versioning_branches_response_data",
+ "CancelPublishAgentVersioningBranchesResponse": ".cancel_publish_agent_versioning_branches_response",
+ "CancelPublishAgentVersioningBranchesResponseData": ".cancel_publish_agent_versioning_branches_response_data",
+ "CreateBranchAgentVersioningBranchesResponse": ".create_branch_agent_versioning_branches_response",
+ "DiscardDraftAgentVersioningBranchesResponse": ".discard_draft_agent_versioning_branches_response",
+ "DiscardDraftAgentVersioningBranchesResponseData": ".discard_draft_agent_versioning_branches_response_data",
+ "GetAgentVersioningBranchesResponse": ".get_agent_versioning_branches_response",
+ "GetDraftAgentVersioningBranchesResponse": ".get_draft_agent_versioning_branches_response",
+ "ListAgentVersioningBranchesResponse": ".list_agent_versioning_branches_response",
+ "ListAgentVersioningBranchesResponseData": ".list_agent_versioning_branches_response_data",
+ "MakeLiveAgentVersioningBranchesResponse": ".make_live_agent_versioning_branches_response",
+ "PublishDraftAgentVersioningBranchesResponse": ".publish_draft_agent_versioning_branches_response",
+ "RenameAgentVersioningBranchesResponse": ".rename_agent_versioning_branches_response",
+ "TestCallAgentVersioningBranchesResponse": ".test_call_agent_versioning_branches_response",
+ "TestCallV2RequestMode": ".test_call_v2request_mode",
+ "UpdateBranchDraftRequestBackgroundSound": ".update_branch_draft_request_background_sound",
+ "UpdateBranchDraftRequestSlmModel": ".update_branch_draft_request_slm_model",
+ "UpdateDraftAgentVersioningBranchesResponse": ".update_draft_agent_versioning_branches_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ArchiveAgentVersioningBranchesResponse",
+ "ArchiveAgentVersioningBranchesResponseData",
+ "CancelPublishAgentVersioningBranchesResponse",
+ "CancelPublishAgentVersioningBranchesResponseData",
+ "CreateBranchAgentVersioningBranchesResponse",
+ "DiscardDraftAgentVersioningBranchesResponse",
+ "DiscardDraftAgentVersioningBranchesResponseData",
+ "GetAgentVersioningBranchesResponse",
+ "GetDraftAgentVersioningBranchesResponse",
+ "ListAgentVersioningBranchesResponse",
+ "ListAgentVersioningBranchesResponseData",
+ "MakeLiveAgentVersioningBranchesResponse",
+ "PublishDraftAgentVersioningBranchesResponse",
+ "RenameAgentVersioningBranchesResponse",
+ "TestCallAgentVersioningBranchesResponse",
+ "TestCallV2RequestMode",
+ "UpdateBranchDraftRequestBackgroundSound",
+ "UpdateBranchDraftRequestSlmModel",
+ "UpdateDraftAgentVersioningBranchesResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/archive_agent_versioning_branches_response.py b/src/smallestai/atoms/agent_versioning_branches/types/archive_agent_versioning_branches_response.py
new file mode 100644
index 00000000..e7db20e3
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/archive_agent_versioning_branches_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .archive_agent_versioning_branches_response_data import ArchiveAgentVersioningBranchesResponseData
+
+
+class ArchiveAgentVersioningBranchesResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[ArchiveAgentVersioningBranchesResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/knowledge_base/types/delete_knowledgebase_id_response.py b/src/smallestai/atoms/agent_versioning_branches/types/archive_agent_versioning_branches_response_data.py
similarity index 81%
rename from src/smallestai/atoms/knowledge_base/types/delete_knowledgebase_id_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/archive_agent_versioning_branches_response_data.py
index 1063edc2..c583eb7c 100644
--- a/src/smallestai/atoms/knowledge_base/types/delete_knowledgebase_id_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/archive_agent_versioning_branches_response_data.py
@@ -7,8 +7,8 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class DeleteKnowledgebaseIdResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
+class ArchiveAgentVersioningBranchesResponseData(UncheckedBaseModel):
+ archived: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response.py b/src/smallestai/atoms/agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response.py
new file mode 100644
index 00000000..7d58c8b2
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .cancel_publish_agent_versioning_branches_response_data import CancelPublishAgentVersioningBranchesResponseData
+
+
+class CancelPublishAgentVersioningBranchesResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[CancelPublishAgentVersioningBranchesResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/delete_campaign_id_response.py b/src/smallestai/atoms/agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response_data.py
similarity index 80%
rename from src/smallestai/atoms/campaigns/types/delete_campaign_id_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response_data.py
index 9bcf5e6a..91ae64cb 100644
--- a/src/smallestai/atoms/campaigns/types/delete_campaign_id_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/cancel_publish_agent_versioning_branches_response_data.py
@@ -7,8 +7,8 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class DeleteCampaignIdResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
+class CancelPublishAgentVersioningBranchesResponseData(UncheckedBaseModel):
+ cancelled: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/create_branch_agent_versioning_branches_response.py b/src/smallestai/atoms/agent_versioning_branches/types/create_branch_agent_versioning_branches_response.py
new file mode 100644
index 00000000..4be572fd
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/create_branch_agent_versioning_branches_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.branch import Branch
+
+
+class CreateBranchAgentVersioningBranchesResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[Branch] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/discard_draft_agent_versioning_branches_response.py b/src/smallestai/atoms/agent_versioning_branches/types/discard_draft_agent_versioning_branches_response.py
new file mode 100644
index 00000000..5f2bd948
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/discard_draft_agent_versioning_branches_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .discard_draft_agent_versioning_branches_response_data import DiscardDraftAgentVersioningBranchesResponseData
+
+
+class DiscardDraftAgentVersioningBranchesResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[DiscardDraftAgentVersioningBranchesResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/create_with_ai_agents_request_questions_item.py b/src/smallestai/atoms/agent_versioning_branches/types/discard_draft_agent_versioning_branches_response_data.py
similarity index 81%
rename from src/smallestai/atoms/agents/types/create_with_ai_agents_request_questions_item.py
rename to src/smallestai/atoms/agent_versioning_branches/types/discard_draft_agent_versioning_branches_response_data.py
index fedf31af..632a2f74 100644
--- a/src/smallestai/atoms/agents/types/create_with_ai_agents_request_questions_item.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/discard_draft_agent_versioning_branches_response_data.py
@@ -7,9 +7,8 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class CreateWithAiAgentsRequestQuestionsItem(UncheckedBaseModel):
- question: str
- answer: str
+class DiscardDraftAgentVersioningBranchesResponseData(UncheckedBaseModel):
+ discarded: typing.Optional[bool] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_versions/types/patch_agent_id_versions_version_id_response.py b/src/smallestai/atoms/agent_versioning_branches/types/get_agent_versioning_branches_response.py
similarity index 77%
rename from src/smallestai/atoms/agent_versioning_versions/types/patch_agent_id_versions_version_id_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/get_agent_versioning_branches_response.py
index 416d54a3..2b44d4a0 100644
--- a/src/smallestai/atoms/agent_versioning_versions/types/patch_agent_id_versions_version_id_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/get_agent_versioning_branches_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.agent_version import AgentVersion
+from ...types.branch_summary import BranchSummary
-class PatchAgentIdVersionsVersionIdResponse(UncheckedBaseModel):
+class GetAgentVersioningBranchesResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[AgentVersion] = None
+ data: typing.Optional[BranchSummary] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/post_agent_id_drafts_draft_id_publish_response.py b/src/smallestai/atoms/agent_versioning_branches/types/get_draft_agent_versioning_branches_response.py
similarity index 77%
rename from src/smallestai/atoms/agent_versioning_drafts/types/post_agent_id_drafts_draft_id_publish_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/get_draft_agent_versioning_branches_response.py
index cdc04bb7..2e2d421c 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/post_agent_id_drafts_draft_id_publish_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/get_draft_agent_versioning_branches_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.agent_version import AgentVersion
+from ...types.draft_detail import DraftDetail
-class PostAgentIdDraftsDraftIdPublishResponse(UncheckedBaseModel):
+class GetDraftAgentVersioningBranchesResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[AgentVersion] = None
+ data: typing.Optional[DraftDetail] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response.py b/src/smallestai/atoms/agent_versioning_branches/types/list_agent_versioning_branches_response.py
similarity index 70%
rename from src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/list_agent_versioning_branches_response.py
index e51b00ef..d2c1d275 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/list_agent_versioning_branches_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .patch_agent_id_drafts_draft_id_response_data import PatchAgentIdDraftsDraftIdResponseData
+from .list_agent_versioning_branches_response_data import ListAgentVersioningBranchesResponseData
-class PatchAgentIdDraftsDraftIdResponse(UncheckedBaseModel):
+class ListAgentVersioningBranchesResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[PatchAgentIdDraftsDraftIdResponseData] = None
+ data: typing.Optional[ListAgentVersioningBranchesResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/list_agent_versioning_branches_response_data.py b/src/smallestai/atoms/agent_versioning_branches/types/list_agent_versioning_branches_response_data.py
new file mode 100644
index 00000000..c8b072a3
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/list_agent_versioning_branches_response_data.py
@@ -0,0 +1,21 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.branch_summary import BranchSummary
+
+
+class ListAgentVersioningBranchesResponseData(UncheckedBaseModel):
+ branches: typing.Optional[typing.List[BranchSummary]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_config_response.py b/src/smallestai/atoms/agent_versioning_branches/types/make_live_agent_versioning_branches_response.py
similarity index 77%
rename from src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_config_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/make_live_agent_versioning_branches_response.py
index 5e18e533..f326700b 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_config_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/make_live_agent_versioning_branches_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.agent_version import AgentVersion
+from ...types.branch_summary import BranchSummary
-class PatchAgentIdDraftsDraftIdConfigResponse(UncheckedBaseModel):
+class MakeLiveAgentVersioningBranchesResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[AgentVersion] = None
+ data: typing.Optional[BranchSummary] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/publish_draft_agent_versioning_branches_response.py b/src/smallestai/atoms/agent_versioning_branches/types/publish_draft_agent_versioning_branches_response.py
new file mode 100644
index 00000000..c37abadb
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/publish_draft_agent_versioning_branches_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.publish_result import PublishResult
+
+
+class PublishDraftAgentVersioningBranchesResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[PublishResult] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/delete_agent_id_drafts_draft_id_response.py b/src/smallestai/atoms/agent_versioning_branches/types/rename_agent_versioning_branches_response.py
similarity index 79%
rename from src/smallestai/atoms/agent_versioning_drafts/types/delete_agent_id_drafts_draft_id_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/rename_agent_versioning_branches_response.py
index 10e70032..65d60e23 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/delete_agent_id_drafts_draft_id_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/rename_agent_versioning_branches_response.py
@@ -5,11 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.branch import Branch
-class DeleteAgentIdDraftsDraftIdResponse(UncheckedBaseModel):
+class RenameAgentVersioningBranchesResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[typing.Any] = None
+ data: typing.Optional[Branch] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/test_call_agent_versioning_branches_response.py b/src/smallestai/atoms/agent_versioning_branches/types/test_call_agent_versioning_branches_response.py
new file mode 100644
index 00000000..c7fbdef7
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/test_call_agent_versioning_branches_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.test_call_result import TestCallResult
+
+
+class TestCallAgentVersioningBranchesResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[TestCallResult] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/test_call_v2request_mode.py b/src/smallestai/atoms/agent_versioning_branches/types/test_call_v2request_mode.py
new file mode 100644
index 00000000..b44c9994
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/test_call_v2request_mode.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+TestCallV2RequestMode = typing.Union[typing.Literal["webcall", "chat", "telephony"], typing.Any]
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_background_sound.py b/src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_background_sound.py
new file mode 100644
index 00000000..34104b8c
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_background_sound.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateBranchDraftRequestBackgroundSound = typing.Union[
+ typing.Literal["", "office", "cafe", "call_center", "static"], typing.Any
+]
diff --git a/src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_slm_model.py b/src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_slm_model.py
new file mode 100644
index 00000000..b262a066
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_branches/types/update_branch_draft_request_slm_model.py
@@ -0,0 +1,17 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateBranchDraftRequestSlmModel = typing.Union[
+ typing.Literal[
+ "electron",
+ "electron-kogta",
+ "electron-kogta-v2",
+ "gpt-4o",
+ "gpt-4.1",
+ "gpt-5.2",
+ "gpt-realtime",
+ "gpt-realtime-mini",
+ ],
+ typing.Any,
+]
diff --git a/src/smallestai/atoms/agents/types/get_widget_config_agents_response.py b/src/smallestai/atoms/agent_versioning_branches/types/update_draft_agent_versioning_branches_response.py
similarity index 78%
rename from src/smallestai/atoms/agents/types/get_widget_config_agents_response.py
rename to src/smallestai/atoms/agent_versioning_branches/types/update_draft_agent_versioning_branches_response.py
index 5072125f..784a6ba6 100644
--- a/src/smallestai/atoms/agents/types/get_widget_config_agents_response.py
+++ b/src/smallestai/atoms/agent_versioning_branches/types/update_draft_agent_versioning_branches_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.widget_config import WidgetConfig
+from ...types.revision import Revision
-class GetWidgetConfigAgentsResponse(UncheckedBaseModel):
+class UpdateDraftAgentVersioningBranchesResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[WidgetConfig] = None
+ data: typing.Optional[Revision] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_drafts/__init__.py b/src/smallestai/atoms/agent_versioning_drafts/__init__.py
index 5cde0202..9e2842e7 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/__init__.py
+++ b/src/smallestai/atoms/agent_versioning_drafts/__init__.py
@@ -2,3 +2,87 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ CreateDraftAgentVersioningDraftsResponse,
+ DiscardDraftAgentVersioningDraftsResponse,
+ DraftConfigRequestBackgroundSound,
+ DraftConfigRequestSlmModel,
+ GetAgentIdDraftsDraftIdDiffResponse,
+ GetAgentIdDraftsDraftIdDiffResponseData,
+ GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue,
+ GetAgentIdDraftsDraftIdResponse,
+ GetAgentIdDraftsDraftIdResponseData,
+ GetAgentIdDraftsResponse,
+ GetAgentIdDraftsResponseDataItem,
+ PostAgentIdDraftsDraftIdTestCallResponse,
+ PostAgentIdDraftsDraftIdTestCallResponseData,
+ PublishDraftAgentVersioningDraftsResponse,
+ RenameDraftAgentVersioningDraftsResponse,
+ RenameDraftAgentVersioningDraftsResponseData,
+ UpdateDraftConfigAgentVersioningDraftsResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateDraftAgentVersioningDraftsResponse": ".types",
+ "DiscardDraftAgentVersioningDraftsResponse": ".types",
+ "DraftConfigRequestBackgroundSound": ".types",
+ "DraftConfigRequestSlmModel": ".types",
+ "GetAgentIdDraftsDraftIdDiffResponse": ".types",
+ "GetAgentIdDraftsDraftIdDiffResponseData": ".types",
+ "GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue": ".types",
+ "GetAgentIdDraftsDraftIdResponse": ".types",
+ "GetAgentIdDraftsDraftIdResponseData": ".types",
+ "GetAgentIdDraftsResponse": ".types",
+ "GetAgentIdDraftsResponseDataItem": ".types",
+ "PostAgentIdDraftsDraftIdTestCallResponse": ".types",
+ "PostAgentIdDraftsDraftIdTestCallResponseData": ".types",
+ "PublishDraftAgentVersioningDraftsResponse": ".types",
+ "RenameDraftAgentVersioningDraftsResponse": ".types",
+ "RenameDraftAgentVersioningDraftsResponseData": ".types",
+ "UpdateDraftConfigAgentVersioningDraftsResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateDraftAgentVersioningDraftsResponse",
+ "DiscardDraftAgentVersioningDraftsResponse",
+ "DraftConfigRequestBackgroundSound",
+ "DraftConfigRequestSlmModel",
+ "GetAgentIdDraftsDraftIdDiffResponse",
+ "GetAgentIdDraftsDraftIdDiffResponseData",
+ "GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue",
+ "GetAgentIdDraftsDraftIdResponse",
+ "GetAgentIdDraftsDraftIdResponseData",
+ "GetAgentIdDraftsResponse",
+ "GetAgentIdDraftsResponseDataItem",
+ "PostAgentIdDraftsDraftIdTestCallResponse",
+ "PostAgentIdDraftsDraftIdTestCallResponseData",
+ "PublishDraftAgentVersioningDraftsResponse",
+ "RenameDraftAgentVersioningDraftsResponse",
+ "RenameDraftAgentVersioningDraftsResponseData",
+ "UpdateDraftConfigAgentVersioningDraftsResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_drafts/client.py b/src/smallestai/atoms/agent_versioning_drafts/client.py
index 99e0636c..ed1a1e76 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/client.py
+++ b/src/smallestai/atoms/agent_versioning_drafts/client.py
@@ -44,12 +44,14 @@ def list_active_drafts(
self, id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> GetAgentIdDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches (`openDraftId` / `hasOpenDraft`)`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
List all active (non-discarded) drafts for an agent.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -67,7 +69,7 @@ def list_active_drafts(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.list_active_drafts(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
"""
_response = self._raw_client.list_active_drafts(id, request_options=request_options)
@@ -83,12 +85,14 @@ def create_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> CreateDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Create a new draft from an existing published version or another draft. At least one of sourceVersionId or sourceDraftId is required (both may be sent simultaneously).
Parameters
----------
id : str
- The agent ID
+ The agent ID.
source_version_id : typing.Optional[str]
ID of a published version to branch from. Must be a valid MongoDB ObjectId (24-char hex).
@@ -116,7 +120,7 @@ def create_draft(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.create_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
"""
_response = self._raw_client.create_draft(
@@ -137,12 +141,14 @@ def get_draft_detail(
request_options: typing.Optional[RequestOptions] = None,
) -> GetAgentIdDraftsDraftIdResponse:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `draftId` remains resolvable. Migrate to `GET /agent/{id}/branches/{branchId}/draft`. Will be removed at the sunset date.
+
Returns the latest revision of a draft along with its edit history.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -166,7 +172,7 @@ def get_draft_detail(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.get_draft_detail(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
"""
@@ -177,12 +183,14 @@ def discard_draft(
self, id: str, draft_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> DiscardDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `DELETE /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Discard (soft-delete) a draft. Only the draft creator or an admin can discard.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -203,7 +211,7 @@ def discard_draft(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.discard_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
"""
@@ -214,12 +222,15 @@ def rename_draft(
self, id: str, draft_id: str, *, draft_name: str, request_options: typing.Optional[RequestOptions] = None
) -> RenameDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Rename a draft. For config changes, use PATCH /agent/{id}/drafts/{draftId}/config instead.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -243,7 +254,7 @@ def rename_draft(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.rename_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
draft_name="draftName",
)
@@ -260,12 +271,14 @@ def get_draft_diff(
request_options: typing.Optional[RequestOptions] = None,
) -> GetAgentIdDraftsDraftIdDiffResponse:
"""
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=:draft&b=`. Will be removed at the sunset date.
+
Compare a draft against its source version or another specified version.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -289,7 +302,7 @@ def get_draft_diff(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.get_draft_diff(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
"""
@@ -309,12 +322,14 @@ def publish_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> PublishDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/draft/publish`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Publish a draft as a new versioned release. Optionally activate it immediately.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -344,7 +359,7 @@ def publish_draft(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.publish_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
"""
@@ -363,12 +378,14 @@ def test_call_with_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> PostAgentIdDraftsDraftIdTestCallResponse:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `includeDraft: true`. Will be removed at the sunset date.
+
Initiate a test call using the draft's resolved configuration.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -395,7 +412,7 @@ def test_call_with_draft_config(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.test_call_with_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
"""
@@ -443,6 +460,9 @@ def update_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> UpdateDraftConfigAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update the configuration of a draft. This single endpoint is how every
agent-level config field is changed: prompt, tools, voice, language,
**post-call analytics (disposition metrics)**, and more. There is no
@@ -476,7 +496,7 @@ def update_draft_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -570,7 +590,7 @@ def update_draft_config(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_drafts.update_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
"""
@@ -632,12 +652,14 @@ async def list_active_drafts(
self, id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> GetAgentIdDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches (`openDraftId` / `hasOpenDraft`)`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
List all active (non-discarded) drafts for an agent.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -660,7 +682,7 @@ async def list_active_drafts(
async def main() -> None:
await client.atoms.agent_versioning_drafts.list_active_drafts(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
@@ -679,12 +701,14 @@ async def create_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> CreateDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Create a new draft from an existing published version or another draft. At least one of sourceVersionId or sourceDraftId is required (both may be sent simultaneously).
Parameters
----------
id : str
- The agent ID
+ The agent ID.
source_version_id : typing.Optional[str]
ID of a published version to branch from. Must be a valid MongoDB ObjectId (24-char hex).
@@ -717,7 +741,7 @@ async def create_draft(
async def main() -> None:
await client.atoms.agent_versioning_drafts.create_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
@@ -741,12 +765,14 @@ async def get_draft_detail(
request_options: typing.Optional[RequestOptions] = None,
) -> GetAgentIdDraftsDraftIdResponse:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `draftId` remains resolvable. Migrate to `GET /agent/{id}/branches/{branchId}/draft`. Will be removed at the sunset date.
+
Returns the latest revision of a draft along with its edit history.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -775,7 +801,7 @@ async def get_draft_detail(
async def main() -> None:
await client.atoms.agent_versioning_drafts.get_draft_detail(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -789,12 +815,14 @@ async def discard_draft(
self, id: str, draft_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> DiscardDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `DELETE /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Discard (soft-delete) a draft. Only the draft creator or an admin can discard.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -820,7 +848,7 @@ async def discard_draft(
async def main() -> None:
await client.atoms.agent_versioning_drafts.discard_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -834,12 +862,15 @@ async def rename_draft(
self, id: str, draft_id: str, *, draft_name: str, request_options: typing.Optional[RequestOptions] = None
) -> RenameDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Rename a draft. For config changes, use PATCH /agent/{id}/drafts/{draftId}/config instead.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -868,7 +899,7 @@ async def rename_draft(
async def main() -> None:
await client.atoms.agent_versioning_drafts.rename_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
draft_name="draftName",
)
@@ -890,12 +921,14 @@ async def get_draft_diff(
request_options: typing.Optional[RequestOptions] = None,
) -> GetAgentIdDraftsDraftIdDiffResponse:
"""
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=:draft&b=`. Will be removed at the sunset date.
+
Compare a draft against its source version or another specified version.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -924,7 +957,7 @@ async def get_draft_diff(
async def main() -> None:
await client.atoms.agent_versioning_drafts.get_draft_diff(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -947,12 +980,14 @@ async def publish_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> PublishDraftAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/draft/publish`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Publish a draft as a new versioned release. Optionally activate it immediately.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -987,7 +1022,7 @@ async def publish_draft(
async def main() -> None:
await client.atoms.agent_versioning_drafts.publish_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -1009,12 +1044,14 @@ async def test_call_with_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> PostAgentIdDraftsDraftIdTestCallResponse:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `includeDraft: true`. Will be removed at the sunset date.
+
Initiate a test call using the draft's resolved configuration.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1046,7 +1083,7 @@ async def test_call_with_draft_config(
async def main() -> None:
await client.atoms.agent_versioning_drafts.test_call_with_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
@@ -1097,6 +1134,9 @@ async def update_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> UpdateDraftConfigAgentVersioningDraftsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update the configuration of a draft. This single endpoint is how every
agent-level config field is changed: prompt, tools, voice, language,
**post-call analytics (disposition metrics)**, and more. There is no
@@ -1130,7 +1170,7 @@ async def update_draft_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1229,7 +1269,7 @@ async def update_draft_config(
async def main() -> None:
await client.atoms.agent_versioning_drafts.update_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
diff --git a/src/smallestai/atoms/agent_versioning_drafts/raw_client.py b/src/smallestai/atoms/agent_versioning_drafts/raw_client.py
index 6786c663..64db89ff 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/raw_client.py
+++ b/src/smallestai/atoms/agent_versioning_drafts/raw_client.py
@@ -12,6 +12,7 @@
from ...core.serialization import convert_and_respect_annotation_metadata
from ...core.unchecked_base_model import construct_type
from ..errors.bad_request_error import BadRequestError
+from ..errors.conflict_error import ConflictError
from ..errors.forbidden_error import ForbiddenError
from ..errors.internal_server_error import InternalServerError
from ..errors.not_found_error import NotFoundError
@@ -45,12 +46,14 @@ def list_active_drafts(
self, id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> HttpResponse[GetAgentIdDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches (`openDraftId` / `hasOpenDraft`)`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
List all active (non-discarded) drafts for an agent.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -109,6 +112,17 @@ def list_active_drafts(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -139,12 +153,14 @@ def create_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[CreateDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Create a new draft from an existing published version or another draft. At least one of sourceVersionId or sourceDraftId is required (both may be sent simultaneously).
Parameters
----------
id : str
- The agent ID
+ The agent ID.
source_version_id : typing.Optional[str]
ID of a published version to branch from. Must be a valid MongoDB ObjectId (24-char hex).
@@ -233,6 +249,17 @@ def create_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -262,12 +289,14 @@ def get_draft_detail(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[GetAgentIdDraftsDraftIdResponse]:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `draftId` remains resolvable. Migrate to `GET /agent/{id}/branches/{branchId}/draft`. Will be removed at the sunset date.
+
Returns the latest revision of a draft along with its edit history.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -370,12 +399,14 @@ def discard_draft(
self, id: str, draft_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> HttpResponse[DiscardDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `DELETE /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Discard (soft-delete) a draft. Only the draft creator or an admin can discard.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -437,6 +468,17 @@ def discard_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -461,12 +503,15 @@ def rename_draft(
self, id: str, draft_id: str, *, draft_name: str, request_options: typing.Optional[RequestOptions] = None
) -> HttpResponse[RenameDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Rename a draft. For config changes, use PATCH /agent/{id}/drafts/{draftId}/config instead.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -549,6 +594,17 @@ def rename_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -578,12 +634,14 @@ def get_draft_diff(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[GetAgentIdDraftsDraftIdDiffResponse]:
"""
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=:draft&b=`. Will be removed at the sunset date.
+
Compare a draft against its source version or another specified version.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -693,12 +751,14 @@ def publish_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[PublishDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/draft/publish`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Publish a draft as a new versioned release. Optionally activate it immediately.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -789,6 +849,17 @@ def publish_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -819,12 +890,14 @@ def test_call_with_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[PostAgentIdDraftsDraftIdTestCallResponse]:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `includeDraft: true`. Will be removed at the sunset date.
+
Initiate a test call using the draft's resolved configuration.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -970,6 +1043,9 @@ def update_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[UpdateDraftConfigAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update the configuration of a draft. This single endpoint is how every
agent-level config field is changed: prompt, tools, voice, language,
**post-call analytics (disposition metrics)**, and more. There is no
@@ -1003,7 +1079,7 @@ def update_draft_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1190,6 +1266,17 @@ def update_draft_config(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1219,12 +1306,14 @@ async def list_active_drafts(
self, id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> AsyncHttpResponse[GetAgentIdDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches (`openDraftId` / `hasOpenDraft`)`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
List all active (non-discarded) drafts for an agent.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -1283,6 +1372,17 @@ async def list_active_drafts(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1313,12 +1413,14 @@ async def create_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[CreateDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Create a new draft from an existing published version or another draft. At least one of sourceVersionId or sourceDraftId is required (both may be sent simultaneously).
Parameters
----------
id : str
- The agent ID
+ The agent ID.
source_version_id : typing.Optional[str]
ID of a published version to branch from. Must be a valid MongoDB ObjectId (24-char hex).
@@ -1407,6 +1509,17 @@ async def create_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1436,12 +1549,14 @@ async def get_draft_detail(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[GetAgentIdDraftsDraftIdResponse]:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `draftId` remains resolvable. Migrate to `GET /agent/{id}/branches/{branchId}/draft`. Will be removed at the sunset date.
+
Returns the latest revision of a draft along with its edit history.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1544,12 +1659,14 @@ async def discard_draft(
self, id: str, draft_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> AsyncHttpResponse[DiscardDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `DELETE /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Discard (soft-delete) a draft. Only the draft creator or an admin can discard.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1611,6 +1728,17 @@ async def discard_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1635,12 +1763,15 @@ async def rename_draft(
self, id: str, draft_id: str, *, draft_name: str, request_options: typing.Optional[RequestOptions] = None
) -> AsyncHttpResponse[RenameDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Rename a draft. For config changes, use PATCH /agent/{id}/drafts/{draftId}/config instead.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1723,6 +1854,17 @@ async def rename_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1752,12 +1894,14 @@ async def get_draft_diff(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[GetAgentIdDraftsDraftIdDiffResponse]:
"""
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=:draft&b=`. Will be removed at the sunset date.
+
Compare a draft against its source version or another specified version.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1867,12 +2011,14 @@ async def publish_draft(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[PublishDraftAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/draft/publish`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
Publish a draft as a new versioned release. Optionally activate it immediately.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -1963,6 +2109,17 @@ async def publish_draft(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1993,12 +2150,14 @@ async def test_call_with_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[PostAgentIdDraftsDraftIdTestCallResponse]:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `includeDraft: true`. Will be removed at the sunset date.
+
Initiate a test call using the draft's resolved configuration.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -2144,6 +2303,9 @@ async def update_draft_config(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[UpdateDraftConfigAgentVersioningDraftsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `PUT /agent/{id}/branches/{branchId}/draft`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update the configuration of a draft. This single endpoint is how every
agent-level config field is changed: prompt, tools, voice, language,
**post-call analytics (disposition metrics)**, and more. There is no
@@ -2177,7 +2339,7 @@ async def update_draft_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
draft_id : str
The draft ID
@@ -2364,6 +2526,17 @@ async def update_draft_config(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/__init__.py b/src/smallestai/atoms/agent_versioning_drafts/types/__init__.py
index 5cde0202..1860062a 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/__init__.py
+++ b/src/smallestai/atoms/agent_versioning_drafts/types/__init__.py
@@ -2,3 +2,87 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .create_draft_agent_versioning_drafts_response import CreateDraftAgentVersioningDraftsResponse
+ from .discard_draft_agent_versioning_drafts_response import DiscardDraftAgentVersioningDraftsResponse
+ from .draft_config_request_background_sound import DraftConfigRequestBackgroundSound
+ from .draft_config_request_slm_model import DraftConfigRequestSlmModel
+ from .get_agent_id_drafts_draft_id_diff_response import GetAgentIdDraftsDraftIdDiffResponse
+ from .get_agent_id_drafts_draft_id_diff_response_data import GetAgentIdDraftsDraftIdDiffResponseData
+ from .get_agent_id_drafts_draft_id_diff_response_data_sections_value import (
+ GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue,
+ )
+ from .get_agent_id_drafts_draft_id_response import GetAgentIdDraftsDraftIdResponse
+ from .get_agent_id_drafts_draft_id_response_data import GetAgentIdDraftsDraftIdResponseData
+ from .get_agent_id_drafts_response import GetAgentIdDraftsResponse
+ from .get_agent_id_drafts_response_data_item import GetAgentIdDraftsResponseDataItem
+ from .post_agent_id_drafts_draft_id_test_call_response import PostAgentIdDraftsDraftIdTestCallResponse
+ from .post_agent_id_drafts_draft_id_test_call_response_data import PostAgentIdDraftsDraftIdTestCallResponseData
+ from .publish_draft_agent_versioning_drafts_response import PublishDraftAgentVersioningDraftsResponse
+ from .rename_draft_agent_versioning_drafts_response import RenameDraftAgentVersioningDraftsResponse
+ from .rename_draft_agent_versioning_drafts_response_data import RenameDraftAgentVersioningDraftsResponseData
+ from .update_draft_config_agent_versioning_drafts_response import UpdateDraftConfigAgentVersioningDraftsResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateDraftAgentVersioningDraftsResponse": ".create_draft_agent_versioning_drafts_response",
+ "DiscardDraftAgentVersioningDraftsResponse": ".discard_draft_agent_versioning_drafts_response",
+ "DraftConfigRequestBackgroundSound": ".draft_config_request_background_sound",
+ "DraftConfigRequestSlmModel": ".draft_config_request_slm_model",
+ "GetAgentIdDraftsDraftIdDiffResponse": ".get_agent_id_drafts_draft_id_diff_response",
+ "GetAgentIdDraftsDraftIdDiffResponseData": ".get_agent_id_drafts_draft_id_diff_response_data",
+ "GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue": ".get_agent_id_drafts_draft_id_diff_response_data_sections_value",
+ "GetAgentIdDraftsDraftIdResponse": ".get_agent_id_drafts_draft_id_response",
+ "GetAgentIdDraftsDraftIdResponseData": ".get_agent_id_drafts_draft_id_response_data",
+ "GetAgentIdDraftsResponse": ".get_agent_id_drafts_response",
+ "GetAgentIdDraftsResponseDataItem": ".get_agent_id_drafts_response_data_item",
+ "PostAgentIdDraftsDraftIdTestCallResponse": ".post_agent_id_drafts_draft_id_test_call_response",
+ "PostAgentIdDraftsDraftIdTestCallResponseData": ".post_agent_id_drafts_draft_id_test_call_response_data",
+ "PublishDraftAgentVersioningDraftsResponse": ".publish_draft_agent_versioning_drafts_response",
+ "RenameDraftAgentVersioningDraftsResponse": ".rename_draft_agent_versioning_drafts_response",
+ "RenameDraftAgentVersioningDraftsResponseData": ".rename_draft_agent_versioning_drafts_response_data",
+ "UpdateDraftConfigAgentVersioningDraftsResponse": ".update_draft_config_agent_versioning_drafts_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateDraftAgentVersioningDraftsResponse",
+ "DiscardDraftAgentVersioningDraftsResponse",
+ "DraftConfigRequestBackgroundSound",
+ "DraftConfigRequestSlmModel",
+ "GetAgentIdDraftsDraftIdDiffResponse",
+ "GetAgentIdDraftsDraftIdDiffResponseData",
+ "GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue",
+ "GetAgentIdDraftsDraftIdResponse",
+ "GetAgentIdDraftsDraftIdResponseData",
+ "GetAgentIdDraftsResponse",
+ "GetAgentIdDraftsResponseDataItem",
+ "PostAgentIdDraftsDraftIdTestCallResponse",
+ "PostAgentIdDraftsDraftIdTestCallResponseData",
+ "PublishDraftAgentVersioningDraftsResponse",
+ "RenameDraftAgentVersioningDraftsResponse",
+ "RenameDraftAgentVersioningDraftsResponseData",
+ "UpdateDraftConfigAgentVersioningDraftsResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response.py b/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response.py
index f57d708e..e58f2f01 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response.py
+++ b/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response.py
@@ -5,14 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_agent_id_drafts_draft_id_diff_response_data import GetAgentIdDraftsDraftIdDiffResponseData
class GetAgentIdDraftsDraftIdDiffResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None)
- """
- Section-by-section diff between the draft and the comparison target
- """
+ data: typing.Optional[GetAgentIdDraftsDraftIdDiffResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data.py b/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data.py
new file mode 100644
index 00000000..d00f1864
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_agent_id_drafts_draft_id_diff_response_data_sections_value import (
+ GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue,
+)
+
+
+class GetAgentIdDraftsDraftIdDiffResponseData(UncheckedBaseModel):
+ sections: typing.Optional[typing.Dict[str, GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue]] = pydantic.Field(
+ default=None
+ )
+ """
+ Keyed by config section name. Each entry shows the before/after state of that section.
+ """
+
+ has_changes: typing_extensions.Annotated[
+ typing.Optional[bool],
+ FieldMetadata(alias="hasChanges"),
+ pydantic.Field(alias="hasChanges", description="True if at least one section differs"),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data_sections_value.py b/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data_sections_value.py
new file mode 100644
index 00000000..cca1ca68
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_drafts/types/get_agent_id_drafts_draft_id_diff_response_data_sections_value.py
@@ -0,0 +1,33 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue(UncheckedBaseModel):
+ before: typing.Optional[typing.Any] = pydantic.Field(default=None)
+ """
+ Section value in the comparison target (null if section did not exist)
+ """
+
+ after: typing.Optional[typing.Any] = pydantic.Field(default=None)
+ """
+ Section value in the draft (null if section was removed)
+ """
+
+ changed: typing.Optional[bool] = pydantic.Field(default=None)
+ """
+ Whether the section differs between the draft and the comparison target
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_revisions/__init__.py b/src/smallestai/atoms/agent_versioning_revisions/__init__.py
new file mode 100644
index 00000000..505dee21
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/__init__.py
@@ -0,0 +1,64 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ DiffAgentVersioningRevisionsResponse,
+ GetAgentVersioningRevisionsResponse,
+ GetAgentVersioningRevisionsResponseData,
+ GetHistoryAgentVersioningRevisionsResponse,
+ GetHistoryAgentVersioningRevisionsResponseData,
+ GetHistoryAgentVersioningRevisionsResponseDataTrailItem,
+ ListAgentVersioningRevisionsResponse,
+ ListAgentVersioningRevisionsResponseData,
+ RestoreAgentVersioningRevisionsResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "DiffAgentVersioningRevisionsResponse": ".types",
+ "GetAgentVersioningRevisionsResponse": ".types",
+ "GetAgentVersioningRevisionsResponseData": ".types",
+ "GetHistoryAgentVersioningRevisionsResponse": ".types",
+ "GetHistoryAgentVersioningRevisionsResponseData": ".types",
+ "GetHistoryAgentVersioningRevisionsResponseDataTrailItem": ".types",
+ "ListAgentVersioningRevisionsResponse": ".types",
+ "ListAgentVersioningRevisionsResponseData": ".types",
+ "RestoreAgentVersioningRevisionsResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "DiffAgentVersioningRevisionsResponse",
+ "GetAgentVersioningRevisionsResponse",
+ "GetAgentVersioningRevisionsResponseData",
+ "GetHistoryAgentVersioningRevisionsResponse",
+ "GetHistoryAgentVersioningRevisionsResponseData",
+ "GetHistoryAgentVersioningRevisionsResponseDataTrailItem",
+ "ListAgentVersioningRevisionsResponse",
+ "ListAgentVersioningRevisionsResponseData",
+ "RestoreAgentVersioningRevisionsResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_revisions/client.py b/src/smallestai/atoms/agent_versioning_revisions/client.py
new file mode 100644
index 00000000..ae7dbda1
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/client.py
@@ -0,0 +1,510 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from .raw_client import AsyncRawAgentVersioningRevisionsClient, RawAgentVersioningRevisionsClient
+from .types.diff_agent_versioning_revisions_response import DiffAgentVersioningRevisionsResponse
+from .types.get_agent_versioning_revisions_response import GetAgentVersioningRevisionsResponse
+from .types.get_history_agent_versioning_revisions_response import GetHistoryAgentVersioningRevisionsResponse
+from .types.list_agent_versioning_revisions_response import ListAgentVersioningRevisionsResponse
+from .types.restore_agent_versioning_revisions_response import RestoreAgentVersioningRevisionsResponse
+
+
+class AgentVersioningRevisionsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawAgentVersioningRevisionsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawAgentVersioningRevisionsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawAgentVersioningRevisionsClient
+ """
+ return self._raw_client
+
+ def list(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ limit: typing.Optional[int] = None,
+ skip: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ListAgentVersioningRevisionsResponse:
+ """
+ Paginated list of committed and scanning revisions on this branch, newest first.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ limit : typing.Optional[int]
+
+ skip : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_revisions.list(
+ id="id",
+ branch_id="branchId",
+ )
+ """
+ _response = self._raw_client.list(id, branch_id, limit=limit, skip=skip, request_options=request_options)
+ return _response.data
+
+ def get(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetAgentVersioningRevisionsResponse:
+ """
+ Return a single revision plus its `resolvedConfig` (the fully-merged agent config at that revision).
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_revisions.get(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+ """
+ _response = self._raw_client.get(id, branch_id, revision_id, request_options=request_options)
+ return _response.data
+
+ def get_history(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetHistoryAgentVersioningRevisionsResponse:
+ """
+ Return the publish trail for a revision: who published it, and the ordered list of prior revisions on this branch with the sections that changed at each step.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetHistoryAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_revisions.get_history(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+ """
+ _response = self._raw_client.get_history(id, branch_id, revision_id, request_options=request_options)
+ return _response.data
+
+ def restore(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> RestoreAgentVersioningRevisionsResponse:
+ """
+ Republish an older revision as a new revision at the head of this branch. Restore does not overwrite history; the older revision keeps its ID, and a new revision is committed on top.
+
+ The response mirrors `POST /agent/{id}/branches/{branchId}/draft/publish`: `200 committed` if the scan is synchronous, `202 scanning` if deferred. Only one publish or restore can be in flight per branch at a time.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ RestoreAgentVersioningRevisionsResponse
+ Restored synchronously.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_revisions.restore(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+ """
+ _response = self._raw_client.restore(id, branch_id, revision_id, request_options=request_options)
+ return _response.data
+
+ def diff(
+ self, id: str, *, a: str, b: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> DiffAgentVersioningRevisionsResponse:
+ """
+ Compare any two references on this agent and return per-section diffs. Each side (`a` and `b`) is either a `revisionId` or the string `:draft` to reference the open draft on a branch. Sides may cross branches. Both references must resolve to configs on the same agent.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ a : str
+ Left-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ b : str
+ Right-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DiffAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agent_versioning_revisions.diff(
+ id="id",
+ a="a",
+ b="b",
+ )
+ """
+ _response = self._raw_client.diff(id, a=a, b=b, request_options=request_options)
+ return _response.data
+
+
+class AsyncAgentVersioningRevisionsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawAgentVersioningRevisionsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawAgentVersioningRevisionsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawAgentVersioningRevisionsClient
+ """
+ return self._raw_client
+
+ async def list(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ limit: typing.Optional[int] = None,
+ skip: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ListAgentVersioningRevisionsResponse:
+ """
+ Paginated list of committed and scanning revisions on this branch, newest first.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ limit : typing.Optional[int]
+
+ skip : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_revisions.list(
+ id="id",
+ branch_id="branchId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.list(id, branch_id, limit=limit, skip=skip, request_options=request_options)
+ return _response.data
+
+ async def get(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetAgentVersioningRevisionsResponse:
+ """
+ Return a single revision plus its `resolvedConfig` (the fully-merged agent config at that revision).
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_revisions.get(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get(id, branch_id, revision_id, request_options=request_options)
+ return _response.data
+
+ async def get_history(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetHistoryAgentVersioningRevisionsResponse:
+ """
+ Return the publish trail for a revision: who published it, and the ordered list of prior revisions on this branch with the sections that changed at each step.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetHistoryAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_revisions.get_history(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_history(id, branch_id, revision_id, request_options=request_options)
+ return _response.data
+
+ async def restore(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> RestoreAgentVersioningRevisionsResponse:
+ """
+ Republish an older revision as a new revision at the head of this branch. Restore does not overwrite history; the older revision keeps its ID, and a new revision is committed on top.
+
+ The response mirrors `POST /agent/{id}/branches/{branchId}/draft/publish`: `200 committed` if the scan is synchronous, `202 scanning` if deferred. Only one publish or restore can be in flight per branch at a time.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ RestoreAgentVersioningRevisionsResponse
+ Restored synchronously.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_revisions.restore(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.restore(id, branch_id, revision_id, request_options=request_options)
+ return _response.data
+
+ async def diff(
+ self, id: str, *, a: str, b: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> DiffAgentVersioningRevisionsResponse:
+ """
+ Compare any two references on this agent and return per-section diffs. Each side (`a` and `b`) is either a `revisionId` or the string `:draft` to reference the open draft on a branch. Sides may cross branches. Both references must resolve to configs on the same agent.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ a : str
+ Left-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ b : str
+ Right-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DiffAgentVersioningRevisionsResponse
+ Successful response.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agent_versioning_revisions.diff(
+ id="id",
+ a="a",
+ b="b",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.diff(id, a=a, b=b, request_options=request_options)
+ return _response.data
diff --git a/src/smallestai/atoms/agent_versioning_revisions/raw_client.py b/src/smallestai/atoms/agent_versioning_revisions/raw_client.py
new file mode 100644
index 00000000..1b64028a
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/raw_client.py
@@ -0,0 +1,1164 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.jsonable_encoder import encode_path_param
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.unchecked_base_model import construct_type
+from ..errors.bad_request_error import BadRequestError
+from ..errors.conflict_error import ConflictError
+from ..errors.forbidden_error import ForbiddenError
+from ..errors.internal_server_error import InternalServerError
+from ..errors.locked_error import LockedError
+from ..errors.not_found_error import NotFoundError
+from ..errors.unauthorized_error import UnauthorizedError
+from ..types.locked_error_response import LockedErrorResponse
+from .types.diff_agent_versioning_revisions_response import DiffAgentVersioningRevisionsResponse
+from .types.get_agent_versioning_revisions_response import GetAgentVersioningRevisionsResponse
+from .types.get_history_agent_versioning_revisions_response import GetHistoryAgentVersioningRevisionsResponse
+from .types.list_agent_versioning_revisions_response import ListAgentVersioningRevisionsResponse
+from .types.restore_agent_versioning_revisions_response import RestoreAgentVersioningRevisionsResponse
+from pydantic import ValidationError
+
+
+class RawAgentVersioningRevisionsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def list(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ limit: typing.Optional[int] = None,
+ skip: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[ListAgentVersioningRevisionsResponse]:
+ """
+ Paginated list of committed and scanning revisions on this branch, newest first.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ limit : typing.Optional[int]
+
+ skip : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[ListAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "limit": limit,
+ "skip": skip,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=ListAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetAgentVersioningRevisionsResponse]:
+ """
+ Return a single revision plus its `resolvedConfig` (the fully-merged agent config at that revision).
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions/{encode_path_param(revision_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=GetAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_history(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetHistoryAgentVersioningRevisionsResponse]:
+ """
+ Return the publish trail for a revision: who published it, and the ordered list of prior revisions on this branch with the sections that changed at each step.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetHistoryAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions/{encode_path_param(revision_id)}/history",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetHistoryAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=GetHistoryAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def restore(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[RestoreAgentVersioningRevisionsResponse]:
+ """
+ Republish an older revision as a new revision at the head of this branch. Restore does not overwrite history; the older revision keeps its ID, and a new revision is committed on top.
+
+ The response mirrors `POST /agent/{id}/branches/{branchId}/draft/publish`: `200 committed` if the scan is synchronous, `202 scanning` if deferred. Only one publish or restore can be in flight per branch at a time.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[RestoreAgentVersioningRevisionsResponse]
+ Restored synchronously.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions/{encode_path_param(revision_id)}/restore",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ RestoreAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=RestoreAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def diff(
+ self, id: str, *, a: str, b: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[DiffAgentVersioningRevisionsResponse]:
+ """
+ Compare any two references on this agent and return per-section diffs. Each side (`a` and `b`) is either a `revisionId` or the string `:draft` to reference the open draft on a branch. Sides may cross branches. Both references must resolve to configs on the same agent.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ a : str
+ Left-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ b : str
+ Right-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[DiffAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/diff",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "a": a,
+ "b": b,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ DiffAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=DiffAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawAgentVersioningRevisionsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def list(
+ self,
+ id: str,
+ branch_id: str,
+ *,
+ limit: typing.Optional[int] = None,
+ skip: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ListAgentVersioningRevisionsResponse]:
+ """
+ Paginated list of committed and scanning revisions on this branch, newest first.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ limit : typing.Optional[int]
+
+ skip : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ListAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "limit": limit,
+ "skip": skip,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=ListAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetAgentVersioningRevisionsResponse]:
+ """
+ Return a single revision plus its `resolvedConfig` (the fully-merged agent config at that revision).
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions/{encode_path_param(revision_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=GetAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_history(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetHistoryAgentVersioningRevisionsResponse]:
+ """
+ Return the publish trail for a revision: who published it, and the ordered list of prior revisions on this branch with the sections that changed at each step.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetHistoryAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions/{encode_path_param(revision_id)}/history",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetHistoryAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=GetHistoryAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def restore(
+ self, id: str, branch_id: str, revision_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[RestoreAgentVersioningRevisionsResponse]:
+ """
+ Republish an older revision as a new revision at the head of this branch. Restore does not overwrite history; the older revision keeps its ID, and a new revision is committed on top.
+
+ The response mirrors `POST /agent/{id}/branches/{branchId}/draft/publish`: `200 committed` if the scan is synchronous, `202 scanning` if deferred. Only one publish or restore can be in flight per branch at a time.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ branch_id : str
+ The branch ID.
+
+ revision_id : str
+ The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[RestoreAgentVersioningRevisionsResponse]
+ Restored synchronously.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/branches/{encode_path_param(branch_id)}/revisions/{encode_path_param(revision_id)}/restore",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ RestoreAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=RestoreAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 423:
+ raise LockedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ LockedErrorResponse,
+ construct_type(
+ type_=LockedErrorResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def diff(
+ self, id: str, *, a: str, b: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[DiffAgentVersioningRevisionsResponse]:
+ """
+ Compare any two references on this agent and return per-section diffs. Each side (`a` and `b`) is either a `revisionId` or the string `:draft` to reference the open draft on a branch. Sides may cross branches. Both references must resolve to configs on the same agent.
+
+ Parameters
+ ----------
+ id : str
+ The agent ID.
+
+ a : str
+ Left-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ b : str
+ Right-hand side. Either a `revisionId` (24-hex ObjectId) or the token `:draft`.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[DiffAgentVersioningRevisionsResponse]
+ Successful response.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/diff",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "a": a,
+ "b": b,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ DiffAgentVersioningRevisionsResponse,
+ construct_type(
+ type_=DiffAgentVersioningRevisionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/__init__.py b/src/smallestai/atoms/agent_versioning_revisions/types/__init__.py
new file mode 100644
index 00000000..f57772c6
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/__init__.py
@@ -0,0 +1,64 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .diff_agent_versioning_revisions_response import DiffAgentVersioningRevisionsResponse
+ from .get_agent_versioning_revisions_response import GetAgentVersioningRevisionsResponse
+ from .get_agent_versioning_revisions_response_data import GetAgentVersioningRevisionsResponseData
+ from .get_history_agent_versioning_revisions_response import GetHistoryAgentVersioningRevisionsResponse
+ from .get_history_agent_versioning_revisions_response_data import GetHistoryAgentVersioningRevisionsResponseData
+ from .get_history_agent_versioning_revisions_response_data_trail_item import (
+ GetHistoryAgentVersioningRevisionsResponseDataTrailItem,
+ )
+ from .list_agent_versioning_revisions_response import ListAgentVersioningRevisionsResponse
+ from .list_agent_versioning_revisions_response_data import ListAgentVersioningRevisionsResponseData
+ from .restore_agent_versioning_revisions_response import RestoreAgentVersioningRevisionsResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "DiffAgentVersioningRevisionsResponse": ".diff_agent_versioning_revisions_response",
+ "GetAgentVersioningRevisionsResponse": ".get_agent_versioning_revisions_response",
+ "GetAgentVersioningRevisionsResponseData": ".get_agent_versioning_revisions_response_data",
+ "GetHistoryAgentVersioningRevisionsResponse": ".get_history_agent_versioning_revisions_response",
+ "GetHistoryAgentVersioningRevisionsResponseData": ".get_history_agent_versioning_revisions_response_data",
+ "GetHistoryAgentVersioningRevisionsResponseDataTrailItem": ".get_history_agent_versioning_revisions_response_data_trail_item",
+ "ListAgentVersioningRevisionsResponse": ".list_agent_versioning_revisions_response",
+ "ListAgentVersioningRevisionsResponseData": ".list_agent_versioning_revisions_response_data",
+ "RestoreAgentVersioningRevisionsResponse": ".restore_agent_versioning_revisions_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "DiffAgentVersioningRevisionsResponse",
+ "GetAgentVersioningRevisionsResponse",
+ "GetAgentVersioningRevisionsResponseData",
+ "GetHistoryAgentVersioningRevisionsResponse",
+ "GetHistoryAgentVersioningRevisionsResponseData",
+ "GetHistoryAgentVersioningRevisionsResponseDataTrailItem",
+ "ListAgentVersioningRevisionsResponse",
+ "ListAgentVersioningRevisionsResponseData",
+ "RestoreAgentVersioningRevisionsResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/post_agent_id_drafts_response.py b/src/smallestai/atoms/agent_versioning_revisions/types/diff_agent_versioning_revisions_response.py
similarity index 78%
rename from src/smallestai/atoms/agent_versioning_drafts/types/post_agent_id_drafts_response.py
rename to src/smallestai/atoms/agent_versioning_revisions/types/diff_agent_versioning_revisions_response.py
index 5079d301..6b4c1e3c 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/post_agent_id_drafts_response.py
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/diff_agent_versioning_revisions_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.agent_version import AgentVersion
+from ...types.diff_result import DiffResult
-class PostAgentIdDraftsResponse(UncheckedBaseModel):
+class DiffAgentVersioningRevisionsResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[AgentVersion] = None
+ data: typing.Optional[DiffResult] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response.py b/src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response.py
new file mode 100644
index 00000000..911c1789
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_agent_versioning_revisions_response_data import GetAgentVersioningRevisionsResponseData
+
+
+class GetAgentVersioningRevisionsResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[GetAgentVersioningRevisionsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response_data.py b/src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response_data.py
new file mode 100644
index 00000000..cae0de92
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/get_agent_versioning_revisions_response_data.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.revision import Revision
+
+
+class GetAgentVersioningRevisionsResponseData(UncheckedBaseModel):
+ revision: typing.Optional[Revision] = None
+ resolved_config: typing_extensions.Annotated[
+ typing.Optional[typing.Dict[str, typing.Any]],
+ FieldMetadata(alias="resolvedConfig"),
+ pydantic.Field(alias="resolvedConfig", description="Fully-merged agent config at this revision."),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response.py b/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response.py
new file mode 100644
index 00000000..e961adf6
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_history_agent_versioning_revisions_response_data import GetHistoryAgentVersioningRevisionsResponseData
+
+
+class GetHistoryAgentVersioningRevisionsResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[GetHistoryAgentVersioningRevisionsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data.py b/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data.py
new file mode 100644
index 00000000..fdc0be9d
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data.py
@@ -0,0 +1,30 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.revision import Revision
+from .get_history_agent_versioning_revisions_response_data_trail_item import (
+ GetHistoryAgentVersioningRevisionsResponseDataTrailItem,
+)
+
+
+class GetHistoryAgentVersioningRevisionsResponseData(UncheckedBaseModel):
+ revision: typing.Optional[Revision] = None
+ published_by: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="publishedBy"), pydantic.Field(alias="publishedBy")
+ ] = None
+ trail: typing.Optional[typing.List[GetHistoryAgentVersioningRevisionsResponseDataTrailItem]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data_trail_item.py b/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data_trail_item.py
new file mode 100644
index 00000000..bb958cad
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/get_history_agent_versioning_revisions_response_data_trail_item.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.revision import Revision
+
+
+class GetHistoryAgentVersioningRevisionsResponseDataTrailItem(UncheckedBaseModel):
+ revision: typing.Optional[Revision] = None
+ changed_sections: typing_extensions.Annotated[
+ typing.Optional[typing.List[str]],
+ FieldMetadata(alias="changedSections"),
+ pydantic.Field(alias="changedSections"),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response.py b/src/smallestai/atoms/agent_versioning_revisions/types/list_agent_versioning_revisions_response.py
similarity index 70%
rename from src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response.py
rename to src/smallestai/atoms/agent_versioning_revisions/types/list_agent_versioning_revisions_response.py
index d94958a8..1f350ea0 100644
--- a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response.py
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/list_agent_versioning_revisions_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_product_import_phone_number_response_data import PostProductImportPhoneNumberResponseData
+from .list_agent_versioning_revisions_response_data import ListAgentVersioningRevisionsResponseData
-class PostProductImportPhoneNumberResponse(UncheckedBaseModel):
+class ListAgentVersioningRevisionsResponse(UncheckedBaseModel):
status: typing.Optional[bool] = None
- data: typing.Optional[PostProductImportPhoneNumberResponseData] = None
+ data: typing.Optional[ListAgentVersioningRevisionsResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/list_agent_versioning_revisions_response_data.py b/src/smallestai/atoms/agent_versioning_revisions/types/list_agent_versioning_revisions_response_data.py
new file mode 100644
index 00000000..fc0f1b28
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/list_agent_versioning_revisions_response_data.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.revision import Revision
+
+
+class ListAgentVersioningRevisionsResponseData(UncheckedBaseModel):
+ revisions: typing.Optional[typing.List[Revision]] = None
+ total: typing.Optional[int] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_revisions/types/restore_agent_versioning_revisions_response.py b/src/smallestai/atoms/agent_versioning_revisions/types/restore_agent_versioning_revisions_response.py
new file mode 100644
index 00000000..ce7cb4ee
--- /dev/null
+++ b/src/smallestai/atoms/agent_versioning_revisions/types/restore_agent_versioning_revisions_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.publish_result import PublishResult
+
+
+class RestoreAgentVersioningRevisionsResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[PublishResult] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_versions/__init__.py b/src/smallestai/atoms/agent_versioning_versions/__init__.py
index 5cde0202..3319040f 100644
--- a/src/smallestai/atoms/agent_versioning_versions/__init__.py
+++ b/src/smallestai/atoms/agent_versioning_versions/__init__.py
@@ -2,3 +2,66 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ ActivateVersionAgentVersioningVersionsResponse,
+ GetAgentIdVersionsDiffResponse,
+ GetAgentIdVersionsResponse,
+ GetAgentIdVersionsResponseData,
+ GetAgentIdVersionsResponseDataVersionsItem,
+ GetAgentIdVersionsVersionIdResponse,
+ GetAgentIdVersionsVersionIdResponseData,
+ PostAgentIdVersionsVersionIdTestCallResponse,
+ PostAgentIdVersionsVersionIdTestCallResponseData,
+ UpdateVersionMetadataAgentVersioningVersionsResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ActivateVersionAgentVersioningVersionsResponse": ".types",
+ "GetAgentIdVersionsDiffResponse": ".types",
+ "GetAgentIdVersionsResponse": ".types",
+ "GetAgentIdVersionsResponseData": ".types",
+ "GetAgentIdVersionsResponseDataVersionsItem": ".types",
+ "GetAgentIdVersionsVersionIdResponse": ".types",
+ "GetAgentIdVersionsVersionIdResponseData": ".types",
+ "PostAgentIdVersionsVersionIdTestCallResponse": ".types",
+ "PostAgentIdVersionsVersionIdTestCallResponseData": ".types",
+ "UpdateVersionMetadataAgentVersioningVersionsResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ActivateVersionAgentVersioningVersionsResponse",
+ "GetAgentIdVersionsDiffResponse",
+ "GetAgentIdVersionsResponse",
+ "GetAgentIdVersionsResponseData",
+ "GetAgentIdVersionsResponseDataVersionsItem",
+ "GetAgentIdVersionsVersionIdResponse",
+ "GetAgentIdVersionsVersionIdResponseData",
+ "PostAgentIdVersionsVersionIdTestCallResponse",
+ "PostAgentIdVersionsVersionIdTestCallResponseData",
+ "UpdateVersionMetadataAgentVersioningVersionsResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_versions/client.py b/src/smallestai/atoms/agent_versioning_versions/client.py
index a386f9f9..e3508668 100644
--- a/src/smallestai/atoms/agent_versioning_versions/client.py
+++ b/src/smallestai/atoms/agent_versioning_versions/client.py
@@ -1,6 +1,5 @@
# This file was auto-generated by Fern from our API Definition.
-import datetime as dt
import typing
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
@@ -8,9 +7,6 @@
from ..types.test_call_request_mode import TestCallRequestMode
from .raw_client import AsyncRawAgentVersioningVersionsClient, RawAgentVersioningVersionsClient
from .types.activate_version_agent_versioning_versions_response import ActivateVersionAgentVersioningVersionsResponse
-from .types.compare_version_metrics_agent_versioning_versions_response import (
- CompareVersionMetricsAgentVersioningVersionsResponse,
-)
from .types.get_agent_id_versions_diff_response import GetAgentIdVersionsDiffResponse
from .types.get_agent_id_versions_response import GetAgentIdVersionsResponse
from .types.get_agent_id_versions_version_id_response import GetAgentIdVersionsVersionIdResponse
@@ -48,6 +44,9 @@ def list_published_versions(
request_options: typing.Optional[RequestOptions] = None,
) -> GetAgentIdVersionsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches/{branchId}/revisions`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
List published versions for an agent with pagination and optional pin filter.
The `total` value currently represents the total number of published versions
for the agent, not necessarily the filtered count when `isPinned` is used.
@@ -55,7 +54,7 @@ def list_published_versions(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
limit : typing.Optional[int]
Number of versions to return (1-100, default 20)
@@ -82,7 +81,7 @@ def list_published_versions(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_versions.list_published_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
"""
_response = self._raw_client.list_published_versions(
@@ -94,12 +93,14 @@ def diff_two_versions(
self, id: str, *, version_a: str, version_b: str, request_options: typing.Optional[RequestOptions] = None
) -> GetAgentIdVersionsDiffResponse:
"""
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=&b=`. Will be removed at the sunset date.
+
Compare two version or draft revision records side-by-side by their IDs. The implementation tries published versions first and can fall back to the latest draft revision.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_a : str
ID of the first version
@@ -123,7 +124,7 @@ def diff_two_versions(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_versions.diff_two_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_a="versionA",
version_b="versionB",
)
@@ -133,79 +134,13 @@ def diff_two_versions(
)
return _response.data
- def compare_version_metrics(
- self,
- id: str,
- *,
- version_a: str,
- version_b: str,
- date_from: typing.Optional[dt.date] = None,
- date_to: typing.Optional[dt.date] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> CompareVersionMetricsAgentVersioningVersionsResponse:
- """
- Compare analytics/call metrics between two published versions over an optional date range.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- version_a : str
- ID of the first version
-
- version_b : str
- ID of the second version
-
- date_from : typing.Optional[dt.date]
- Start date for the comparison range in YYYY-MM-DD format.
-
- date_to : typing.Optional[dt.date]
- End date for the comparison range in YYYY-MM-DD format.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- CompareVersionMetricsAgentVersioningVersionsResponse
- Metrics comparison returned
-
- Examples
- --------
- import datetime
-
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.agent_versioning_versions.compare_version_metrics(
- id="60d0fe4f5311236168a109ca",
- version_a="versionA",
- version_b="versionB",
- date_from=datetime.date.fromisoformat(
- "2026-05-01",
- ),
- date_to=datetime.date.fromisoformat(
- "2026-05-31",
- ),
- )
- """
- _response = self._raw_client.compare_version_metrics(
- id,
- version_a=version_a,
- version_b=version_b,
- date_from=date_from,
- date_to=date_to,
- request_options=request_options,
- )
- return _response.data
-
def get_version_detail(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> GetAgentIdVersionsVersionIdResponse:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `versionId` equals its migrated `revisionId`, so this continues to resolve across branches. Migrate to `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}`. Will be removed at the sunset date.
+
+ |
Returns the full detail of a specific published version (read-only).
Published versions are config-immutable โ to modify config, create a draft from
this version and publish it as a new version.
@@ -213,7 +148,7 @@ def get_version_detail(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -234,7 +169,7 @@ def get_version_detail(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_versions.get_version_detail(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
"""
@@ -252,6 +187,9 @@ def update_version_metadata(
request_options: typing.Optional[RequestOptions] = None,
) -> UpdateVersionMetadataAgentVersioningVersionsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `Metadata edits removed on v2. Set `label` at publish via `POST /agent/{id}/branches/{branchId}/draft/publish`.`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update a published version's label, description, or pinned status. At least one field is required.
Published versions (both active and inactive) are config-immutable โ their agent
configuration cannot be changed. To modify config, create a new draft from the version,
@@ -260,7 +198,7 @@ def update_version_metadata(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -290,7 +228,7 @@ def update_version_metadata(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_versions.update_version_metadata(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
"""
@@ -303,6 +241,9 @@ def activate_version(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> ActivateVersionAgentVersioningVersionsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/live or POST /agent/{id}/branches/{branchId}/revisions/{revisionId}/restore`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Set a published version as the active version for the agent. The previously
active version is deactivated. This does not modify the version's config โ it
only changes which version serves live traffic. Activation is idempotent: if
@@ -312,7 +253,7 @@ def activate_version(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -333,7 +274,7 @@ def activate_version(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_versions.activate_version(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
"""
@@ -350,6 +291,9 @@ def test_call_with_version_config(
request_options: typing.Optional[RequestOptions] = None,
) -> PostAgentIdVersionsVersionIdTestCallResponse:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `revisionId`. Will be removed at the sunset date.
+
+ |
Initiate a test call using a specific published version's configuration.
The response always includes `conversationId` and `callId`. For `webcall`
and `chat`, it also includes `token`, `roomName`, and `host`. Those fields
@@ -358,7 +302,7 @@ def test_call_with_version_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -385,7 +329,7 @@ def test_call_with_version_config(
api_key="YOUR_API_KEY",
)
client.atoms.agent_versioning_versions.test_call_with_version_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
"""
@@ -420,6 +364,9 @@ async def list_published_versions(
request_options: typing.Optional[RequestOptions] = None,
) -> GetAgentIdVersionsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches/{branchId}/revisions`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
List published versions for an agent with pagination and optional pin filter.
The `total` value currently represents the total number of published versions
for the agent, not necessarily the filtered count when `isPinned` is used.
@@ -427,7 +374,7 @@ async def list_published_versions(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
limit : typing.Optional[int]
Number of versions to return (1-100, default 20)
@@ -459,7 +406,7 @@ async def list_published_versions(
async def main() -> None:
await client.atoms.agent_versioning_versions.list_published_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
@@ -474,12 +421,14 @@ async def diff_two_versions(
self, id: str, *, version_a: str, version_b: str, request_options: typing.Optional[RequestOptions] = None
) -> GetAgentIdVersionsDiffResponse:
"""
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=&b=`. Will be removed at the sunset date.
+
Compare two version or draft revision records side-by-side by their IDs. The implementation tries published versions first and can fall back to the latest draft revision.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_a : str
ID of the first version
@@ -508,7 +457,7 @@ async def diff_two_versions(
async def main() -> None:
await client.atoms.agent_versioning_versions.diff_two_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_a="versionA",
version_b="versionB",
)
@@ -521,86 +470,13 @@ async def main() -> None:
)
return _response.data
- async def compare_version_metrics(
- self,
- id: str,
- *,
- version_a: str,
- version_b: str,
- date_from: typing.Optional[dt.date] = None,
- date_to: typing.Optional[dt.date] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> CompareVersionMetricsAgentVersioningVersionsResponse:
- """
- Compare analytics/call metrics between two published versions over an optional date range.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- version_a : str
- ID of the first version
-
- version_b : str
- ID of the second version
-
- date_from : typing.Optional[dt.date]
- Start date for the comparison range in YYYY-MM-DD format.
-
- date_to : typing.Optional[dt.date]
- End date for the comparison range in YYYY-MM-DD format.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- CompareVersionMetricsAgentVersioningVersionsResponse
- Metrics comparison returned
-
- Examples
- --------
- import asyncio
- import datetime
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.agent_versioning_versions.compare_version_metrics(
- id="60d0fe4f5311236168a109ca",
- version_a="versionA",
- version_b="versionB",
- date_from=datetime.date.fromisoformat(
- "2026-05-01",
- ),
- date_to=datetime.date.fromisoformat(
- "2026-05-31",
- ),
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.compare_version_metrics(
- id,
- version_a=version_a,
- version_b=version_b,
- date_from=date_from,
- date_to=date_to,
- request_options=request_options,
- )
- return _response.data
-
async def get_version_detail(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> GetAgentIdVersionsVersionIdResponse:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `versionId` equals its migrated `revisionId`, so this continues to resolve across branches. Migrate to `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}`. Will be removed at the sunset date.
+
+ |
Returns the full detail of a specific published version (read-only).
Published versions are config-immutable โ to modify config, create a draft from
this version and publish it as a new version.
@@ -608,7 +484,7 @@ async def get_version_detail(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -634,7 +510,7 @@ async def get_version_detail(
async def main() -> None:
await client.atoms.agent_versioning_versions.get_version_detail(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
@@ -655,6 +531,9 @@ async def update_version_metadata(
request_options: typing.Optional[RequestOptions] = None,
) -> UpdateVersionMetadataAgentVersioningVersionsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `Metadata edits removed on v2. Set `label` at publish via `POST /agent/{id}/branches/{branchId}/draft/publish`.`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update a published version's label, description, or pinned status. At least one field is required.
Published versions (both active and inactive) are config-immutable โ their agent
configuration cannot be changed. To modify config, create a new draft from the version,
@@ -663,7 +542,7 @@ async def update_version_metadata(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -698,7 +577,7 @@ async def update_version_metadata(
async def main() -> None:
await client.atoms.agent_versioning_versions.update_version_metadata(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
@@ -714,6 +593,9 @@ async def activate_version(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> ActivateVersionAgentVersioningVersionsResponse:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/live or POST /agent/{id}/branches/{branchId}/revisions/{revisionId}/restore`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Set a published version as the active version for the agent. The previously
active version is deactivated. This does not modify the version's config โ it
only changes which version serves live traffic. Activation is idempotent: if
@@ -723,7 +605,7 @@ async def activate_version(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -749,7 +631,7 @@ async def activate_version(
async def main() -> None:
await client.atoms.agent_versioning_versions.activate_version(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
@@ -769,6 +651,9 @@ async def test_call_with_version_config(
request_options: typing.Optional[RequestOptions] = None,
) -> PostAgentIdVersionsVersionIdTestCallResponse:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `revisionId`. Will be removed at the sunset date.
+
+ |
Initiate a test call using a specific published version's configuration.
The response always includes `conversationId` and `callId`. For `webcall`
and `chat`, it also includes `token`, `roomName`, and `host`. Those fields
@@ -777,7 +662,7 @@ async def test_call_with_version_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -809,7 +694,7 @@ async def test_call_with_version_config(
async def main() -> None:
await client.atoms.agent_versioning_versions.test_call_with_version_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
diff --git a/src/smallestai/atoms/agent_versioning_versions/raw_client.py b/src/smallestai/atoms/agent_versioning_versions/raw_client.py
index ce748a7f..0e914ea0 100644
--- a/src/smallestai/atoms/agent_versioning_versions/raw_client.py
+++ b/src/smallestai/atoms/agent_versioning_versions/raw_client.py
@@ -1,6 +1,5 @@
# This file was auto-generated by Fern from our API Definition.
-import datetime as dt
import typing
from json.decoder import JSONDecodeError
@@ -12,15 +11,13 @@
from ...core.request_options import RequestOptions
from ...core.unchecked_base_model import construct_type
from ..errors.bad_request_error import BadRequestError
+from ..errors.conflict_error import ConflictError
from ..errors.forbidden_error import ForbiddenError
from ..errors.internal_server_error import InternalServerError
from ..errors.not_found_error import NotFoundError
from ..errors.unauthorized_error import UnauthorizedError
from ..types.test_call_request_mode import TestCallRequestMode
from .types.activate_version_agent_versioning_versions_response import ActivateVersionAgentVersioningVersionsResponse
-from .types.compare_version_metrics_agent_versioning_versions_response import (
- CompareVersionMetricsAgentVersioningVersionsResponse,
-)
from .types.get_agent_id_versions_diff_response import GetAgentIdVersionsDiffResponse
from .types.get_agent_id_versions_response import GetAgentIdVersionsResponse
from .types.get_agent_id_versions_version_id_response import GetAgentIdVersionsVersionIdResponse
@@ -48,6 +45,9 @@ def list_published_versions(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[GetAgentIdVersionsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches/{branchId}/revisions`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
List published versions for an agent with pagination and optional pin filter.
The `total` value currently represents the total number of published versions
for the agent, not necessarily the filtered count when `isPinned` is used.
@@ -55,7 +55,7 @@ def list_published_versions(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
limit : typing.Optional[int]
Number of versions to return (1-100, default 20)
@@ -139,106 +139,8 @@ def list_published_versions(
),
),
)
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- def diff_two_versions(
- self, id: str, *, version_a: str, version_b: str, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetAgentIdVersionsDiffResponse]:
- """
- Compare two version or draft revision records side-by-side by their IDs. The implementation tries published versions first and can fall back to the latest draft revision.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- version_a : str
- ID of the first version
-
- version_b : str
- ID of the second version
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[GetAgentIdVersionsDiffResponse]
- Diff returned successfully
- """
- _response = self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/versions/diff",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- params={
- "versionA": version_a,
- "versionB": version_b,
- },
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetAgentIdVersionsDiffResponse,
- construct_type(
- type_=GetAgentIdVersionsDiffResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 403:
- raise ForbiddenError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 404:
- raise NotFoundError(
+ if _response.status_code == 409:
+ raise ConflictError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
@@ -268,23 +170,18 @@ def diff_two_versions(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def compare_version_metrics(
- self,
- id: str,
- *,
- version_a: str,
- version_b: str,
- date_from: typing.Optional[dt.date] = None,
- date_to: typing.Optional[dt.date] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> HttpResponse[CompareVersionMetricsAgentVersioningVersionsResponse]:
+ def diff_two_versions(
+ self, id: str, *, version_a: str, version_b: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetAgentIdVersionsDiffResponse]:
"""
- Compare analytics/call metrics between two published versions over an optional date range.
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=&b=`. Will be removed at the sunset date.
+
+ Compare two version or draft revision records side-by-side by their IDs. The implementation tries published versions first and can fall back to the latest draft revision.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_a : str
ID of the first version
@@ -292,38 +189,30 @@ def compare_version_metrics(
version_b : str
ID of the second version
- date_from : typing.Optional[dt.date]
- Start date for the comparison range in YYYY-MM-DD format.
-
- date_to : typing.Optional[dt.date]
- End date for the comparison range in YYYY-MM-DD format.
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[CompareVersionMetricsAgentVersioningVersionsResponse]
- Metrics comparison returned
+ HttpResponse[GetAgentIdVersionsDiffResponse]
+ Diff returned successfully
"""
_response = self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/versions/compare-metrics",
+ f"agent/{encode_path_param(id)}/versions/diff",
base_url=self._client_wrapper.get_environment().atoms,
method="GET",
params={
"versionA": version_a,
"versionB": version_b,
- "dateFrom": str(date_from) if date_from is not None else None,
- "dateTo": str(date_to) if date_to is not None else None,
},
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- CompareVersionMetricsAgentVersioningVersionsResponse,
+ GetAgentIdVersionsDiffResponse,
construct_type(
- type_=CompareVersionMetricsAgentVersioningVersionsResponse, # type: ignore
+ type_=GetAgentIdVersionsDiffResponse, # type: ignore
object_=_response.json(),
),
)
@@ -396,6 +285,9 @@ def get_version_detail(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> HttpResponse[GetAgentIdVersionsVersionIdResponse]:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `versionId` equals its migrated `revisionId`, so this continues to resolve across branches. Migrate to `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}`. Will be removed at the sunset date.
+
+ |
Returns the full detail of a specific published version (read-only).
Published versions are config-immutable โ to modify config, create a draft from
this version and publish it as a new version.
@@ -403,7 +295,7 @@ def get_version_detail(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -507,6 +399,9 @@ def update_version_metadata(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[UpdateVersionMetadataAgentVersioningVersionsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `Metadata edits removed on v2. Set `label` at publish via `POST /agent/{id}/branches/{branchId}/draft/publish`.`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update a published version's label, description, or pinned status. At least one field is required.
Published versions (both active and inactive) are config-immutable โ their agent
configuration cannot be changed. To modify config, create a new draft from the version,
@@ -515,7 +410,7 @@ def update_version_metadata(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -606,6 +501,17 @@ def update_version_metadata(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -630,6 +536,9 @@ def activate_version(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> HttpResponse[ActivateVersionAgentVersioningVersionsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/live or POST /agent/{id}/branches/{branchId}/revisions/{revisionId}/restore`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Set a published version as the active version for the agent. The previously
active version is deactivated. This does not modify the version's config โ it
only changes which version serves live traffic. Activation is idempotent: if
@@ -639,7 +548,7 @@ def activate_version(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -712,6 +621,17 @@ def activate_version(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -742,6 +662,9 @@ def test_call_with_version_config(
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[PostAgentIdVersionsVersionIdTestCallResponse]:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `revisionId`. Will be removed at the sunset date.
+
+ |
Initiate a test call using a specific published version's configuration.
The response always includes `conversationId` and `callId`. For `webcall`
and `chat`, it also includes `token`, `roomName`, and `host`. Those fields
@@ -750,7 +673,7 @@ def test_call_with_version_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -872,6 +795,9 @@ async def list_published_versions(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[GetAgentIdVersionsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `GET /agent/{id}/branches/{branchId}/revisions`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
List published versions for an agent with pagination and optional pin filter.
The `total` value currently represents the total number of published versions
for the agent, not necessarily the filtered count when `isPinned` is used.
@@ -879,7 +805,7 @@ async def list_published_versions(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
limit : typing.Optional[int]
Number of versions to return (1-100, default 20)
@@ -963,106 +889,8 @@ async def list_published_versions(
),
),
)
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def diff_two_versions(
- self, id: str, *, version_a: str, version_b: str, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetAgentIdVersionsDiffResponse]:
- """
- Compare two version or draft revision records side-by-side by their IDs. The implementation tries published versions first and can fall back to the latest draft revision.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- version_a : str
- ID of the first version
-
- version_b : str
- ID of the second version
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[GetAgentIdVersionsDiffResponse]
- Diff returned successfully
- """
- _response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/versions/diff",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- params={
- "versionA": version_a,
- "versionB": version_b,
- },
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetAgentIdVersionsDiffResponse,
- construct_type(
- type_=GetAgentIdVersionsDiffResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 403:
- raise ForbiddenError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 404:
- raise NotFoundError(
+ if _response.status_code == 409:
+ raise ConflictError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
@@ -1092,23 +920,18 @@ async def diff_two_versions(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def compare_version_metrics(
- self,
- id: str,
- *,
- version_a: str,
- version_b: str,
- date_from: typing.Optional[dt.date] = None,
- date_to: typing.Optional[dt.date] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[CompareVersionMetricsAgentVersioningVersionsResponse]:
+ async def diff_two_versions(
+ self, id: str, *, version_a: str, version_b: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetAgentIdVersionsDiffResponse]:
"""
- Compare analytics/call metrics between two published versions over an optional date range.
+ **Deprecated.** Kept working during the v1 coexistence window (~1 month). Migrate to `GET /agent/{id}/diff?a=&b=`. Will be removed at the sunset date.
+
+ Compare two version or draft revision records side-by-side by their IDs. The implementation tries published versions first and can fall back to the latest draft revision.
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_a : str
ID of the first version
@@ -1116,38 +939,30 @@ async def compare_version_metrics(
version_b : str
ID of the second version
- date_from : typing.Optional[dt.date]
- Start date for the comparison range in YYYY-MM-DD format.
-
- date_to : typing.Optional[dt.date]
- End date for the comparison range in YYYY-MM-DD format.
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[CompareVersionMetricsAgentVersioningVersionsResponse]
- Metrics comparison returned
+ AsyncHttpResponse[GetAgentIdVersionsDiffResponse]
+ Diff returned successfully
"""
_response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/versions/compare-metrics",
+ f"agent/{encode_path_param(id)}/versions/diff",
base_url=self._client_wrapper.get_environment().atoms,
method="GET",
params={
"versionA": version_a,
"versionB": version_b,
- "dateFrom": str(date_from) if date_from is not None else None,
- "dateTo": str(date_to) if date_to is not None else None,
},
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- CompareVersionMetricsAgentVersioningVersionsResponse,
+ GetAgentIdVersionsDiffResponse,
construct_type(
- type_=CompareVersionMetricsAgentVersioningVersionsResponse, # type: ignore
+ type_=GetAgentIdVersionsDiffResponse, # type: ignore
object_=_response.json(),
),
)
@@ -1220,6 +1035,9 @@ async def get_version_detail(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> AsyncHttpResponse[GetAgentIdVersionsVersionIdResponse]:
"""
+ **Deprecated.** By-id reads are kept working during the v1 coexistence window (~1 month). A v1 `versionId` equals its migrated `revisionId`, so this continues to resolve across branches. Migrate to `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}`. Will be removed at the sunset date.
+
+ |
Returns the full detail of a specific published version (read-only).
Published versions are config-immutable โ to modify config, create a draft from
this version and publish it as a new version.
@@ -1227,7 +1045,7 @@ async def get_version_detail(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -1331,6 +1149,9 @@ async def update_version_metadata(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[UpdateVersionMetadataAgentVersioningVersionsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `Metadata edits removed on v2. Set `label` at publish via `POST /agent/{id}/branches/{branchId}/draft/publish`.`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Update a published version's label, description, or pinned status. At least one field is required.
Published versions (both active and inactive) are config-immutable โ their agent
configuration cannot be changed. To modify config, create a new draft from the version,
@@ -1339,7 +1160,7 @@ async def update_version_metadata(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -1430,6 +1251,17 @@ async def update_version_metadata(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1454,6 +1286,9 @@ async def activate_version(
self, id: str, version_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> AsyncHttpResponse[ActivateVersionAgentVersioningVersionsResponse]:
"""
+ **Deprecated on the v2 branch model.** Migrate to `POST /agent/{id}/branches/{branchId}/live or POST /agent/{id}/branches/{branchId}/revisions/{revisionId}/restore`. When `ENABLE_BRANCH_MODEL` is on, this endpoint returns `409 versioning_v2_migration_required` with the `Deprecation: true` header. See the [migration guide](/voice-agents/deprecations/agent-versioning-migration).
+
+ |
Set a published version as the active version for the agent. The previously
active version is deactivated. This does not modify the version's config โ it
only changes which version serves live traffic. Activation is idempotent: if
@@ -1463,7 +1298,7 @@ async def activate_version(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
@@ -1536,6 +1371,17 @@ async def activate_version(
),
),
)
+ if _response.status_code == 409:
+ raise ConflictError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1566,6 +1412,9 @@ async def test_call_with_version_config(
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[PostAgentIdVersionsVersionIdTestCallResponse]:
"""
+ **Deprecated.** Test-calls are exempt from the v1 write-block and remain functional. Migrate to `POST /agent/{id}/branches/{branchId}/test-call` with `revisionId`. Will be removed at the sunset date.
+
+ |
Initiate a test call using a specific published version's configuration.
The response always includes `conversationId` and `callId`. For `webcall`
and `chat`, it also includes `token`, `roomName`, and `host`. Those fields
@@ -1574,7 +1423,7 @@ async def test_call_with_version_config(
Parameters
----------
id : str
- The agent ID
+ The agent ID.
version_id : str
The published version ID
diff --git a/src/smallestai/atoms/agent_versioning_versions/types/__init__.py b/src/smallestai/atoms/agent_versioning_versions/types/__init__.py
index 5cde0202..07833919 100644
--- a/src/smallestai/atoms/agent_versioning_versions/types/__init__.py
+++ b/src/smallestai/atoms/agent_versioning_versions/types/__init__.py
@@ -2,3 +2,68 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .activate_version_agent_versioning_versions_response import ActivateVersionAgentVersioningVersionsResponse
+ from .get_agent_id_versions_diff_response import GetAgentIdVersionsDiffResponse
+ from .get_agent_id_versions_response import GetAgentIdVersionsResponse
+ from .get_agent_id_versions_response_data import GetAgentIdVersionsResponseData
+ from .get_agent_id_versions_response_data_versions_item import GetAgentIdVersionsResponseDataVersionsItem
+ from .get_agent_id_versions_version_id_response import GetAgentIdVersionsVersionIdResponse
+ from .get_agent_id_versions_version_id_response_data import GetAgentIdVersionsVersionIdResponseData
+ from .post_agent_id_versions_version_id_test_call_response import PostAgentIdVersionsVersionIdTestCallResponse
+ from .post_agent_id_versions_version_id_test_call_response_data import (
+ PostAgentIdVersionsVersionIdTestCallResponseData,
+ )
+ from .update_version_metadata_agent_versioning_versions_response import (
+ UpdateVersionMetadataAgentVersioningVersionsResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ActivateVersionAgentVersioningVersionsResponse": ".activate_version_agent_versioning_versions_response",
+ "GetAgentIdVersionsDiffResponse": ".get_agent_id_versions_diff_response",
+ "GetAgentIdVersionsResponse": ".get_agent_id_versions_response",
+ "GetAgentIdVersionsResponseData": ".get_agent_id_versions_response_data",
+ "GetAgentIdVersionsResponseDataVersionsItem": ".get_agent_id_versions_response_data_versions_item",
+ "GetAgentIdVersionsVersionIdResponse": ".get_agent_id_versions_version_id_response",
+ "GetAgentIdVersionsVersionIdResponseData": ".get_agent_id_versions_version_id_response_data",
+ "PostAgentIdVersionsVersionIdTestCallResponse": ".post_agent_id_versions_version_id_test_call_response",
+ "PostAgentIdVersionsVersionIdTestCallResponseData": ".post_agent_id_versions_version_id_test_call_response_data",
+ "UpdateVersionMetadataAgentVersioningVersionsResponse": ".update_version_metadata_agent_versioning_versions_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ActivateVersionAgentVersioningVersionsResponse",
+ "GetAgentIdVersionsDiffResponse",
+ "GetAgentIdVersionsResponse",
+ "GetAgentIdVersionsResponseData",
+ "GetAgentIdVersionsResponseDataVersionsItem",
+ "GetAgentIdVersionsVersionIdResponse",
+ "GetAgentIdVersionsVersionIdResponseData",
+ "PostAgentIdVersionsVersionIdTestCallResponse",
+ "PostAgentIdVersionsVersionIdTestCallResponseData",
+ "UpdateVersionMetadataAgentVersioningVersionsResponse",
+]
diff --git a/src/smallestai/atoms/agent_versioning_versions/types/compare_version_metrics_agent_versioning_versions_response.py b/src/smallestai/atoms/agent_versioning_versions/types/compare_version_metrics_agent_versioning_versions_response.py
deleted file mode 100644
index 1d3dad19..00000000
--- a/src/smallestai/atoms/agent_versioning_versions/types/compare_version_metrics_agent_versioning_versions_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.agent_version_metrics_comparison import AgentVersionMetricsComparison
-
-
-class CompareVersionMetricsAgentVersioningVersionsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[AgentVersionMetricsComparison] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_versions/types/get_agent_id_versions_compare_metrics_response.py b/src/smallestai/atoms/agent_versioning_versions/types/get_agent_id_versions_compare_metrics_response.py
deleted file mode 100644
index b44dd89e..00000000
--- a/src/smallestai/atoms/agent_versioning_versions/types/get_agent_id_versions_compare_metrics_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.agent_version_metrics_comparison import AgentVersionMetricsComparison
-
-
-class GetAgentIdVersionsCompareMetricsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[AgentVersionMetricsComparison] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_versions/types/patch_agent_id_versions_version_id_activate_response.py b/src/smallestai/atoms/agent_versioning_versions/types/patch_agent_id_versions_version_id_activate_response.py
deleted file mode 100644
index 77a7f249..00000000
--- a/src/smallestai/atoms/agent_versioning_versions/types/patch_agent_id_versions_version_id_activate_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.agent_version import AgentVersion
-
-
-class PatchAgentIdVersionsVersionIdActivateResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[AgentVersion] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/__init__.py b/src/smallestai/atoms/agents/__init__.py
index 5cde0202..d685297d 100644
--- a/src/smallestai/atoms/agents/__init__.py
+++ b/src/smallestai/atoms/agents/__init__.py
@@ -2,3 +2,192 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ ArchiveAgentAgentsResponse,
+ ArchiveAgentAgentsResponseData,
+ ArchiveAgentAgentsResponseDataMessage,
+ CreateAgentAgentsResponse,
+ CreateAgentRequestBackgroundSound,
+ CreateAgentRequestDenoisingConfig,
+ CreateAgentRequestLanguage,
+ CreateAgentRequestLanguageDefault,
+ CreateAgentRequestLanguageSupportedItem,
+ CreateAgentRequestLanguageSwitching,
+ CreateAgentRequestLlmIdleTimeoutConfig,
+ CreateAgentRequestPreCallApi,
+ CreateAgentRequestPreCallApiMethod,
+ CreateAgentRequestPreCallApiResponseVariablesItem,
+ CreateAgentRequestPronunciationDictsItem,
+ CreateAgentRequestRedactionConfig,
+ CreateAgentRequestSessionTimeoutConfig,
+ CreateAgentRequestSlmModel,
+ CreateAgentRequestSmartTurnConfig,
+ CreateAgentRequestSynthesizer,
+ CreateAgentRequestSynthesizerVoiceConfig,
+ CreateAgentRequestSynthesizerVoiceConfigGender,
+ CreateAgentRequestSynthesizerVoiceConfigModel,
+ CreateAgentRequestTimezone,
+ CreateAgentRequestVoiceDetectionConfig,
+ CreateAgentRequestVoiceMailDetectionConfig,
+ DuplicateAgentAgentsResponse,
+ DuplicateAgentAgentsResponseData,
+ GetAgentAgentsResponse,
+ GetAgentAvatarPresignedUrlResponse,
+ GetAgentAvatarPresignedUrlResponseData,
+ GetAgentCallLogsResponse,
+ GetAgentCallLogsResponseData,
+ GetAgentCallLogsResponseDataCallLogsItem,
+ GetAgentWidgetConfigResponse,
+ GetAgentWidgetConfigResponseData,
+ GetAgentWidgetConfigResponseDataMode,
+ GetAgentWidgetConfigResponseDataPosition,
+ GetAgentWidgetConfigResponseDataSize,
+ GetAgentWidgetConfigResponseDataTheme,
+ ListAgentsAgentsRequestSortField,
+ ListAgentsAgentsRequestSortOrder,
+ ListAgentsAgentsRequestType,
+ ListAgentsAgentsResponse,
+ ListAgentsAgentsResponseData,
+ UpdateAgentAgentsResponse,
+ UpdateAgentWidgetConfigRequestWidgetConfig,
+ UpdateAgentWidgetConfigRequestWidgetConfigMode,
+ UpdateAgentWidgetConfigRequestWidgetConfigPosition,
+ UpdateAgentWidgetConfigRequestWidgetConfigSize,
+ UpdateAgentWidgetConfigRequestWidgetConfigTheme,
+ UpdateAgentWidgetConfigResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ArchiveAgentAgentsResponse": ".types",
+ "ArchiveAgentAgentsResponseData": ".types",
+ "ArchiveAgentAgentsResponseDataMessage": ".types",
+ "CreateAgentAgentsResponse": ".types",
+ "CreateAgentRequestBackgroundSound": ".types",
+ "CreateAgentRequestDenoisingConfig": ".types",
+ "CreateAgentRequestLanguage": ".types",
+ "CreateAgentRequestLanguageDefault": ".types",
+ "CreateAgentRequestLanguageSupportedItem": ".types",
+ "CreateAgentRequestLanguageSwitching": ".types",
+ "CreateAgentRequestLlmIdleTimeoutConfig": ".types",
+ "CreateAgentRequestPreCallApi": ".types",
+ "CreateAgentRequestPreCallApiMethod": ".types",
+ "CreateAgentRequestPreCallApiResponseVariablesItem": ".types",
+ "CreateAgentRequestPronunciationDictsItem": ".types",
+ "CreateAgentRequestRedactionConfig": ".types",
+ "CreateAgentRequestSessionTimeoutConfig": ".types",
+ "CreateAgentRequestSlmModel": ".types",
+ "CreateAgentRequestSmartTurnConfig": ".types",
+ "CreateAgentRequestSynthesizer": ".types",
+ "CreateAgentRequestSynthesizerVoiceConfig": ".types",
+ "CreateAgentRequestSynthesizerVoiceConfigGender": ".types",
+ "CreateAgentRequestSynthesizerVoiceConfigModel": ".types",
+ "CreateAgentRequestTimezone": ".types",
+ "CreateAgentRequestVoiceDetectionConfig": ".types",
+ "CreateAgentRequestVoiceMailDetectionConfig": ".types",
+ "DuplicateAgentAgentsResponse": ".types",
+ "DuplicateAgentAgentsResponseData": ".types",
+ "GetAgentAgentsResponse": ".types",
+ "GetAgentAvatarPresignedUrlResponse": ".types",
+ "GetAgentAvatarPresignedUrlResponseData": ".types",
+ "GetAgentCallLogsResponse": ".types",
+ "GetAgentCallLogsResponseData": ".types",
+ "GetAgentCallLogsResponseDataCallLogsItem": ".types",
+ "GetAgentWidgetConfigResponse": ".types",
+ "GetAgentWidgetConfigResponseData": ".types",
+ "GetAgentWidgetConfigResponseDataMode": ".types",
+ "GetAgentWidgetConfigResponseDataPosition": ".types",
+ "GetAgentWidgetConfigResponseDataSize": ".types",
+ "GetAgentWidgetConfigResponseDataTheme": ".types",
+ "ListAgentsAgentsRequestSortField": ".types",
+ "ListAgentsAgentsRequestSortOrder": ".types",
+ "ListAgentsAgentsRequestType": ".types",
+ "ListAgentsAgentsResponse": ".types",
+ "ListAgentsAgentsResponseData": ".types",
+ "UpdateAgentAgentsResponse": ".types",
+ "UpdateAgentWidgetConfigRequestWidgetConfig": ".types",
+ "UpdateAgentWidgetConfigRequestWidgetConfigMode": ".types",
+ "UpdateAgentWidgetConfigRequestWidgetConfigPosition": ".types",
+ "UpdateAgentWidgetConfigRequestWidgetConfigSize": ".types",
+ "UpdateAgentWidgetConfigRequestWidgetConfigTheme": ".types",
+ "UpdateAgentWidgetConfigResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ArchiveAgentAgentsResponse",
+ "ArchiveAgentAgentsResponseData",
+ "ArchiveAgentAgentsResponseDataMessage",
+ "CreateAgentAgentsResponse",
+ "CreateAgentRequestBackgroundSound",
+ "CreateAgentRequestDenoisingConfig",
+ "CreateAgentRequestLanguage",
+ "CreateAgentRequestLanguageDefault",
+ "CreateAgentRequestLanguageSupportedItem",
+ "CreateAgentRequestLanguageSwitching",
+ "CreateAgentRequestLlmIdleTimeoutConfig",
+ "CreateAgentRequestPreCallApi",
+ "CreateAgentRequestPreCallApiMethod",
+ "CreateAgentRequestPreCallApiResponseVariablesItem",
+ "CreateAgentRequestPronunciationDictsItem",
+ "CreateAgentRequestRedactionConfig",
+ "CreateAgentRequestSessionTimeoutConfig",
+ "CreateAgentRequestSlmModel",
+ "CreateAgentRequestSmartTurnConfig",
+ "CreateAgentRequestSynthesizer",
+ "CreateAgentRequestSynthesizerVoiceConfig",
+ "CreateAgentRequestSynthesizerVoiceConfigGender",
+ "CreateAgentRequestSynthesizerVoiceConfigModel",
+ "CreateAgentRequestTimezone",
+ "CreateAgentRequestVoiceDetectionConfig",
+ "CreateAgentRequestVoiceMailDetectionConfig",
+ "DuplicateAgentAgentsResponse",
+ "DuplicateAgentAgentsResponseData",
+ "GetAgentAgentsResponse",
+ "GetAgentAvatarPresignedUrlResponse",
+ "GetAgentAvatarPresignedUrlResponseData",
+ "GetAgentCallLogsResponse",
+ "GetAgentCallLogsResponseData",
+ "GetAgentCallLogsResponseDataCallLogsItem",
+ "GetAgentWidgetConfigResponse",
+ "GetAgentWidgetConfigResponseData",
+ "GetAgentWidgetConfigResponseDataMode",
+ "GetAgentWidgetConfigResponseDataPosition",
+ "GetAgentWidgetConfigResponseDataSize",
+ "GetAgentWidgetConfigResponseDataTheme",
+ "ListAgentsAgentsRequestSortField",
+ "ListAgentsAgentsRequestSortOrder",
+ "ListAgentsAgentsRequestType",
+ "ListAgentsAgentsResponse",
+ "ListAgentsAgentsResponseData",
+ "UpdateAgentAgentsResponse",
+ "UpdateAgentWidgetConfigRequestWidgetConfig",
+ "UpdateAgentWidgetConfigRequestWidgetConfigMode",
+ "UpdateAgentWidgetConfigRequestWidgetConfigPosition",
+ "UpdateAgentWidgetConfigRequestWidgetConfigSize",
+ "UpdateAgentWidgetConfigRequestWidgetConfigTheme",
+ "UpdateAgentWidgetConfigResponse",
+]
diff --git a/src/smallestai/atoms/agents/client.py b/src/smallestai/atoms/agents/client.py
index c1e0c177..c74da088 100644
--- a/src/smallestai/atoms/agents/client.py
+++ b/src/smallestai/atoms/agents/client.py
@@ -4,8 +4,6 @@
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ...core.request_options import RequestOptions
-from ..types.single_prompt_config import SinglePromptConfig
-from ..types.widget_config import WidgetConfig
from ..types.workflow_type import WorkflowType
from .raw_client import AsyncRawAgentsClient, RawAgentsClient
from .types.archive_agent_agents_response import ArchiveAgentAgentsResponse
@@ -24,25 +22,18 @@
from .types.create_agent_request_timezone import CreateAgentRequestTimezone
from .types.create_agent_request_voice_detection_config import CreateAgentRequestVoiceDetectionConfig
from .types.create_agent_request_voice_mail_detection_config import CreateAgentRequestVoiceMailDetectionConfig
-from .types.create_with_ai_agents_request_questions_item import CreateWithAiAgentsRequestQuestionsItem
-from .types.create_with_ai_agents_request_type import CreateWithAiAgentsRequestType
-from .types.create_with_ai_agents_request_voice_model import CreateWithAiAgentsRequestVoiceModel
-from .types.create_with_ai_agents_response import CreateWithAiAgentsResponse
from .types.duplicate_agent_agents_response import DuplicateAgentAgentsResponse
from .types.get_agent_agents_response import GetAgentAgentsResponse
-from .types.get_agent_id_workflow_response import GetAgentIdWorkflowResponse
-from .types.get_prompt_config_agents_response import GetPromptConfigAgentsResponse
-from .types.get_widget_config_agents_response import GetWidgetConfigAgentsResponse
+from .types.get_agent_avatar_presigned_url_response import GetAgentAvatarPresignedUrlResponse
+from .types.get_agent_call_logs_response import GetAgentCallLogsResponse
+from .types.get_agent_widget_config_response import GetAgentWidgetConfigResponse
from .types.list_agents_agents_request_sort_field import ListAgentsAgentsRequestSortField
from .types.list_agents_agents_request_sort_order import ListAgentsAgentsRequestSortOrder
from .types.list_agents_agents_request_type import ListAgentsAgentsRequestType
from .types.list_agents_agents_response import ListAgentsAgentsResponse
-from .types.list_call_logs_agents_response import ListCallLogsAgentsResponse
from .types.update_agent_agents_response import UpdateAgentAgentsResponse
-from .types.update_widget_config_agents_response import UpdateWidgetConfigAgentsResponse
-from .types.update_workflow_configuration_agents_request_workflow_graph import (
- UpdateWorkflowConfigurationAgentsRequestWorkflowGraph,
-)
+from .types.update_agent_widget_config_request_widget_config import UpdateAgentWidgetConfigRequestWidgetConfig
+from .types.update_agent_widget_config_response import UpdateAgentWidgetConfigResponse
# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)
@@ -191,10 +182,11 @@ def create_agent(
Tamil (`ta`) cannot be combined with other languages in `supported`.
synthesizer : typing.Optional[CreateAgentRequestSynthesizer]
- Synthesizer (TTS) configuration for the agent.
- Models `waves`, `waves_lightning_large`, `waves_lightning_v2`, and `waves_lightning_v3_1`
- validate `voiceId` against the Waves API. All other models accept any voiceId.
- Cloned voices are regular voiceIds โ use them with any compatible Waves model.
+ Synthesizer (TTS) configuration for the agent. Model
+ `waves_lightning_v3_1` validates `voiceId` against the Waves
+ API. `gpt-realtime` and `gpt-realtime-mini` accept any voiceId.
+ Cloned voices are regular voiceIds. Use them with a compatible
+ Waves model.
global_knowledge_base_id : typing.Optional[str]
The global knowledge base ID of the agent. You can create a global knowledge base by using the /knowledgebase endpoint and assign it to the agent. The agent will use this knowledge base for its responses.
@@ -328,6 +320,56 @@ def create_agent(
)
return _response.data
+ def duplicate_agent(
+ self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> DuplicateAgentAgentsResponse:
+ """
+ Duplicates a SINGLE_PROMPT agent's live active version into a target organization
+ (can also be the same organization). Copies all versioned configuration but strips
+ organization-specific resources: knowledge base tools are removed, default variable
+ values are blanked, and a new avatar is generated. The duplicate starts with a
+ published V1 as its active version.
+
+ **400 is returned when:**
+ - The source agent is archived (`"Cannot duplicate an archived agent"`)
+ - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
+ - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
+ - The agent is not `SINGLE_PROMPT` workflow type
+
+ Parameters
+ ----------
+ id : str
+ The ID of the source agent to duplicate
+
+ target_organization_id : str
+ MongoDB ObjectId of the target organization. Must be a 24-character hex string.
+ The authenticated user must be a member of this organization.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DuplicateAgentAgentsResponse
+ Agent duplicated successfully
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.agents.duplicate_agent(
+ id="id",
+ target_organization_id="60d0fe4f5311236168a109ca",
+ )
+ """
+ _response = self._raw_client.duplicate_agent(
+ id, target_organization_id=target_organization_id, request_options=request_options
+ )
+ return _response.data
+
def get_agent(
self,
id: str,
@@ -424,9 +466,7 @@ def update_agent(
**Non-versioned agents** (no active version): all configuration fields are accepted,
the same full set as `POST /agent`.
- **400 is also returned when:**
- - The agent is locked (`"Agent is locked, please unlock it to update"`)
- - Cross-field constraint violated (e.g. `north_indic` language requires `transcriberType: pulse`)
+ **400 is also returned when a cross-field constraint is violated** (for example, `north_indic` language requires `transcriberType: pulse`).
**403** is returned when selecting a gated model (`gpt-5.2`, `electron-kogta`, `electron-kogta-v2`)
without org-level access.
@@ -484,126 +524,24 @@ def update_agent(
)
return _response.data
- def archive_agent(
- self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
- ) -> ArchiveAgentAgentsResponse:
- """
- Soft-archives the agent โ it is excluded from listings and stops accepting calls,
- but all data is preserved and the operation is fully reversible.
-
- Pass `?on=false` to unarchive (restore) a previously archived agent.
-
- **409 is returned when:**
- - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
- - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
-
- Parameters
- ----------
- id : str
-
- on : typing.Optional[bool]
- `true` (default) โ archive the agent.
- `false` โ unarchive (restore) a previously archived agent.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- ArchiveAgentAgentsResponse
- Agent archived or unarchived successfully
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.agents.archive_agent(
- id="id",
- )
- """
- _response = self._raw_client.archive_agent(id, on=on, request_options=request_options)
- return _response.data
-
- def duplicate_agent(
- self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> DuplicateAgentAgentsResponse:
- """
- Duplicates a SINGLE_PROMPT agent's live active version into a target organization
- (can also be the same organization). Copies all versioned configuration but strips
- organization-specific resources: knowledge base tools are removed, default variable
- values are blanked, and a new avatar is generated. The duplicate starts with a
- published V1 as its active version.
-
- **400 is returned when:**
- - The source agent is archived (`"Cannot duplicate an archived agent"`)
- - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
- - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
- - The agent is not `SINGLE_PROMPT` workflow type
-
- Parameters
- ----------
- id : str
- The ID of the source agent to duplicate
-
- target_organization_id : str
- MongoDB ObjectId of the target organization. Must be a 24-character hex string.
- The authenticated user must be a member of this organization.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- DuplicateAgentAgentsResponse
- Agent duplicated successfully
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.agents.duplicate_agent(
- id="id",
- target_organization_id="60d0fe4f5311236168a109ca",
- )
- """
- _response = self._raw_client.duplicate_agent(
- id, target_organization_id=target_organization_id, request_options=request_options
- )
- return _response.data
-
- def get_agent_workflow(
+ def get_agent_widget_config(
self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetAgentIdWorkflowResponse:
+ ) -> GetAgentWidgetConfigResponse:
"""
- **Deprecated** โ prefer `GET /agent/{id}` (config is resolved into `_resolvedConfig`
- including prompt, tools, and post-call analytics).
-
- Returns the active version's prompt and tools for single-prompt agents, or the
- workflow graph data for workflow_graph agents. Customers still rely on this to
- fetch their current prompt + tools โ endpoint is kept live for now.
-
- **Caveat:** the `versionId` query param (if passed) is silently ignored.
- The response always reflects the currently-active version. To inspect a
- non-active version, use `GET /agent/{id}/versions/{versionId}`.
+ Returns the current web widget configuration for the agent. Also includes `assistantId` (same as the agent ID) as a convenience field for the widget embed code.
Parameters
----------
id : str
- The ID of the agent
+ Agent ObjectId
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- GetAgentIdWorkflowResponse
- Successful response
+ GetAgentWidgetConfigResponse
+ Widget configuration
Examples
--------
@@ -612,53 +550,38 @@ def get_agent_workflow(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.atoms.agents.get_agent_workflow(
+ client.atoms.agents.get_agent_widget_config(
id="id",
)
"""
- _response = self._raw_client.get_agent_workflow(id, request_options=request_options)
+ _response = self._raw_client.get_agent_widget_config(id, request_options=request_options)
return _response.data
- def update_workflow_configuration(
+ def update_agent_widget_config(
self,
id: str,
*,
- type: WorkflowType,
- workflow_graph: typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph] = OMIT,
- single_prompt_config: typing.Optional[SinglePromptConfig] = OMIT,
+ widget_config: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> None:
+ ) -> UpdateAgentWidgetConfigResponse:
"""
- **Deprecated** โ use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
-
- Directly mutates the legacy workflow document for an agent. This write path
- bypasses the versioning system entirely: the change is not captured as a
- new version, and future version activations may overwrite the legacy doc
- back to whatever the version snapshot contains.
-
- โ **Writing here on a versioned agent can silently wipe tools, prompt, or
- other fields that were missing from the PATCH payload.** Only use this if
- you know the agent is not using versioning, or if you are intentionally
- hot-patching the legacy doc.
+ Updates the web widget configuration for the agent. Only provided fields are updated (partial update). When `avatarUrl` is changed, the old CDN avatar is automatically deleted from S3. The `avatarUrl` must be a URL from the platform's CDN domain โ use `POST /agent/{id}/avatar/presigned-url` to upload first.
Parameters
----------
id : str
- The workflow ID (found at `agent.workflowId` on the agent document).
+ Agent ObjectId
- type : WorkflowType
-
- workflow_graph : typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph]
- Required when `type = workflow_graph`. Exactly one of `workflowGraph` or `singlePromptConfig` must be provided.
-
- single_prompt_config : typing.Optional[SinglePromptConfig]
+ widget_config : typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig]
+ All fields are optional โ only provided fields are updated
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- None
+ UpdateAgentWidgetConfigResponse
+ Updated widget configuration
Examples
--------
@@ -667,76 +590,48 @@ def update_workflow_configuration(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.atoms.agents.update_workflow_configuration(
- id="60d0fe4f5311236168a109ca",
- type="workflow_graph",
+ client.atoms.agents.update_agent_widget_config(
+ id="id",
)
"""
- _response = self._raw_client.update_workflow_configuration(
- id,
- type=type,
- workflow_graph=workflow_graph,
- single_prompt_config=single_prompt_config,
- request_options=request_options,
+ _response = self._raw_client.update_agent_widget_config(
+ id, widget_config=widget_config, request_options=request_options
)
return _response.data
- def create_with_ai(
+ def get_agent_avatar_presigned_url(
self,
+ id: str,
*,
- name: typing.Optional[str] = OMIT,
- description: typing.Optional[str] = OMIT,
- questions: typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]] = OMIT,
- type: typing.Optional[CreateWithAiAgentsRequestType] = OMIT,
- emotive_toggle: typing.Optional[bool] = OMIT,
- voice_id: typing.Optional[str] = OMIT,
- voice_model: typing.Optional[CreateWithAiAgentsRequestVoiceModel] = OMIT,
- knowledge_base_id: typing.Optional[str] = OMIT,
+ file_name: str,
+ content_type: str,
+ file_size: float,
request_options: typing.Optional[RequestOptions] = None,
- ) -> CreateWithAiAgentsResponse:
+ ) -> GetAgentAvatarPresignedUrlResponse:
"""
- Create a new single-prompt agent from a natural-language brief or structured
- question/answer pairs. Atoms generates the system prompt for you.
-
- Provide either `description` (free-form brief) or a non-empty `questions` array,
- but not both. The `emotiveToggle`, `voiceId`, and `voiceModel` fields must be
- supplied as a 3-tuple or omitted entirely.
+ Generates a pre-signed S3 upload URL for the agent's widget avatar image. Upload the image directly to S3 using the returned `presignedUrl`, then save `cdnUrl` as the agent's avatar via `PATCH /agent/{id}/widget-config`.
Parameters
----------
- name : typing.Optional[str]
- Agent name (trimmed). Auto-generated when omitted.
-
- description : typing.Optional[str]
- Free-form natural-language description of what the agent should do.
- Atoms turns this into the system prompt. Use this OR `questions`, not both.
-
- questions : typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]]
- Structured question/answer pairs. Atoms uses these to compose the
- system prompt. Use this OR `description`, not both.
-
- type : typing.Optional[CreateWithAiAgentsRequestType]
- Currently the only supported agent type.
-
- emotive_toggle : typing.Optional[bool]
- Enable emotive synthesis. Must be paired with `voiceId` + `voiceModel`.
+ id : str
+ Agent ObjectId
- voice_id : typing.Optional[str]
- Voice ID for synthesis. Must be paired with `emotiveToggle` + `voiceModel`.
+ file_name : str
+ Original file name (used to construct the S3 key)
- voice_model : typing.Optional[CreateWithAiAgentsRequestVoiceModel]
- Synthesizer to use. Must be paired with `emotiveToggle` + `voiceId`.
+ content_type : str
+ MIME type โ must start with `image/`
- knowledge_base_id : typing.Optional[str]
- Optional knowledge-base ID to attach to the new agent.
+ file_size : float
+ File size in bytes โ must be > 0 and โค 2 MB (2,097,152 bytes)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- CreateWithAiAgentsResponse
- Agent created. `data` is the new agent's `_id` (string).
+ GetAgentAvatarPresignedUrlResponse
+ Pre-signed upload URL and CDN URL
Examples
--------
@@ -745,52 +640,47 @@ def create_with_ai(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.atoms.agents.create_with_ai()
+ client.atoms.agents.get_agent_avatar_presigned_url(
+ id="id",
+ file_name="fileName",
+ content_type="contentType",
+ file_size=1.1,
+ )
"""
- _response = self._raw_client.create_with_ai(
- name=name,
- description=description,
- questions=questions,
- type=type,
- emotive_toggle=emotive_toggle,
- voice_id=voice_id,
- voice_model=voice_model,
- knowledge_base_id=knowledge_base_id,
- request_options=request_options,
+ _response = self._raw_client.get_agent_avatar_presigned_url(
+ id, file_name=file_name, content_type=content_type, file_size=file_size, request_options=request_options
)
return _response.data
- def list_call_logs(
+ def get_agent_call_logs(
self,
id: str,
*,
- page: typing.Optional[str] = None,
- offset: typing.Optional[str] = None,
+ page: typing.Optional[int] = None,
+ offset: typing.Optional[int] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> ListCallLogsAgentsResponse:
+ ) -> GetAgentCallLogsResponse:
"""
- Returns paginated conversation logs (calls) for a specific agent in the caller's
- organization. Use `GET /conversation` for cross-agent log listing; use this when
- you already have an agent ID.
+ Returns paginated call logs for a specific agent.
Parameters
----------
id : str
- The agent ID
+ Agent ObjectId
- page : typing.Optional[str]
- Page number (string-encoded positive integer).
+ page : typing.Optional[int]
+ Page number (default 1)
- offset : typing.Optional[str]
- Page size (string-encoded positive integer).
+ offset : typing.Optional[int]
+ Records per page (default 10)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- ListCallLogsAgentsResponse
- Paginated list of conversation logs for the agent.
+ GetAgentCallLogsResponse
+ Paginated call logs
Examples
--------
@@ -799,119 +689,54 @@ def list_call_logs(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.atoms.agents.list_call_logs(
- id="60d0fe4f5311236168a109ca",
+ client.atoms.agents.get_agent_call_logs(
+ id="id",
)
"""
- _response = self._raw_client.list_call_logs(id, page=page, offset=offset, request_options=request_options)
+ _response = self._raw_client.get_agent_call_logs(id, page=page, offset=offset, request_options=request_options)
return _response.data
- def get_widget_config(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetWidgetConfigAgentsResponse:
+ def archive_agent(
+ self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
+ ) -> ArchiveAgentAgentsResponse:
"""
- Returns the embeddable web widget configuration for the agent (theme, copy,
- consent prompts, branding, voice/chat mode, allowlist). The response merges the
- stored config with `assistantId: ` injected.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetWidgetConfigAgentsResponse
- Widget configuration for the agent.
-
- Examples
- --------
- from smallestai import SmallestAI
+ Soft-archives the agent โ it is excluded from listings and stops accepting calls,
+ but all data is preserved and the operation is fully reversible.
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.agents.get_widget_config(
- id="60d0fe4f5311236168a109ca",
- )
- """
- _response = self._raw_client.get_widget_config(id, request_options=request_options)
- return _response.data
+ Pass `?on=false` to unarchive (restore) a previously archived agent.
- def update_widget_config(
- self, id: str, *, widget_config: WidgetConfig, request_options: typing.Optional[RequestOptions] = None
- ) -> UpdateWidgetConfigAgentsResponse:
- """
- Merge updates into the agent's embeddable widget config. Only the fields in the
- request body are overwritten; everything else is preserved. Returns the full
- widget config after merge.
+ **409 is returned when:**
+ - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
+ - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
Parameters
----------
id : str
- The agent ID
- widget_config : WidgetConfig
+ on : typing.Optional[bool]
+ `true` (default) โ archive the agent.
+ `false` โ unarchive (restore) a previously archived agent.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- UpdateWidgetConfigAgentsResponse
- Merged widget configuration.
+ ArchiveAgentAgentsResponse
+ Agent archived or unarchived successfully
Examples
--------
from smallestai import SmallestAI
- from smallestai.atoms import WidgetConfig
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.atoms.agents.update_widget_config(
- id="60d0fe4f5311236168a109ca",
- widget_config=WidgetConfig(),
- )
- """
- _response = self._raw_client.update_widget_config(
- id, widget_config=widget_config, request_options=request_options
- )
- return _response.data
-
- def get_prompt_config(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetPromptConfigAgentsResponse:
- """
- Returns the canonical question definitions, option choices, and example labels
- used by the agent-builder UI when collecting input for `POST /agent/with-ai`.
-
- Use this to programmatically discover what questions to ask end-users when
- building agent-creation UIs.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetPromptConfigAgentsResponse
- Prompt-config catalogue.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
+ client.atoms.agents.archive_agent(
+ id="id",
)
- client.atoms.agents.get_prompt_config()
"""
- _response = self._raw_client.get_prompt_config(request_options=request_options)
+ _response = self._raw_client.archive_agent(id, on=on, request_options=request_options)
return _response.data
@@ -1066,10 +891,11 @@ async def create_agent(
Tamil (`ta`) cannot be combined with other languages in `supported`.
synthesizer : typing.Optional[CreateAgentRequestSynthesizer]
- Synthesizer (TTS) configuration for the agent.
- Models `waves`, `waves_lightning_large`, `waves_lightning_v2`, and `waves_lightning_v3_1`
- validate `voiceId` against the Waves API. All other models accept any voiceId.
- Cloned voices are regular voiceIds โ use them with any compatible Waves model.
+ Synthesizer (TTS) configuration for the agent. Model
+ `waves_lightning_v3_1` validates `voiceId` against the Waves
+ API. `gpt-realtime` and `gpt-realtime-mini` accept any voiceId.
+ Cloned voices are regular voiceIds. Use them with a compatible
+ Waves model.
global_knowledge_base_id : typing.Optional[str]
The global knowledge base ID of the agent. You can create a global knowledge base by using the /knowledgebase endpoint and assign it to the agent. The agent will use this knowledge base for its responses.
@@ -1211,21 +1037,79 @@ async def main() -> None:
)
return _response.data
- async def get_agent(
- self,
- id: str,
- *,
- draft_id: typing.Optional[str] = None,
- version_id: typing.Optional[str] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> GetAgentAgentsResponse:
+ async def duplicate_agent(
+ self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> DuplicateAgentAgentsResponse:
"""
- Returns the agent document merged with the resolved config of the active version
- under `_resolvedConfig`. Non-versioned fields (name, telephonyProductId, allowInboundCall,
- etc.) sit at the top level; versioned fields (prompt, tools, language, synthesizer,
- post-call analytics, โฆ) are resolved from the target version and exposed under `_resolvedConfig`.
-
- **Previewing a draft or specific version**
+ Duplicates a SINGLE_PROMPT agent's live active version into a target organization
+ (can also be the same organization). Copies all versioned configuration but strips
+ organization-specific resources: knowledge base tools are removed, default variable
+ values are blanked, and a new avatar is generated. The duplicate starts with a
+ published V1 as its active version.
+
+ **400 is returned when:**
+ - The source agent is archived (`"Cannot duplicate an archived agent"`)
+ - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
+ - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
+ - The agent is not `SINGLE_PROMPT` workflow type
+
+ Parameters
+ ----------
+ id : str
+ The ID of the source agent to duplicate
+
+ target_organization_id : str
+ MongoDB ObjectId of the target organization. Must be a 24-character hex string.
+ The authenticated user must be a member of this organization.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DuplicateAgentAgentsResponse
+ Agent duplicated successfully
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.agents.duplicate_agent(
+ id="id",
+ target_organization_id="60d0fe4f5311236168a109ca",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.duplicate_agent(
+ id, target_organization_id=target_organization_id, request_options=request_options
+ )
+ return _response.data
+
+ async def get_agent(
+ self,
+ id: str,
+ *,
+ draft_id: typing.Optional[str] = None,
+ version_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAgentAgentsResponse:
+ """
+ Returns the agent document merged with the resolved config of the active version
+ under `_resolvedConfig`. Non-versioned fields (name, telephonyProductId, allowInboundCall,
+ etc.) sit at the top level; versioned fields (prompt, tools, language, synthesizer,
+ post-call analytics, โฆ) are resolved from the target version and exposed under `_resolvedConfig`.
+
+ **Previewing a draft or specific version**
Pass `?draftId=` to resolve config from a specific draft instead of the active version.
Pass `?versionId=` to resolve config from a specific published version.
@@ -1315,9 +1199,7 @@ async def update_agent(
**Non-versioned agents** (no active version): all configuration fields are accepted,
the same full set as `POST /agent`.
- **400 is also returned when:**
- - The agent is locked (`"Agent is locked, please unlock it to update"`)
- - Cross-field constraint violated (e.g. `north_indic` language requires `transcriberType: pulse`)
+ **400 is also returned when a cross-field constraint is violated** (for example, `north_indic` language requires `transcriberType: pulse`).
**403** is returned when selecting a gated model (`gpt-5.2`, `electron-kogta`, `electron-kogta-v2`)
without org-level access.
@@ -1383,142 +1265,24 @@ async def main() -> None:
)
return _response.data
- async def archive_agent(
- self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
- ) -> ArchiveAgentAgentsResponse:
- """
- Soft-archives the agent โ it is excluded from listings and stops accepting calls,
- but all data is preserved and the operation is fully reversible.
-
- Pass `?on=false` to unarchive (restore) a previously archived agent.
-
- **409 is returned when:**
- - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
- - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
-
- Parameters
- ----------
- id : str
-
- on : typing.Optional[bool]
- `true` (default) โ archive the agent.
- `false` โ unarchive (restore) a previously archived agent.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- ArchiveAgentAgentsResponse
- Agent archived or unarchived successfully
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.agents.archive_agent(
- id="id",
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.archive_agent(id, on=on, request_options=request_options)
- return _response.data
-
- async def duplicate_agent(
- self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> DuplicateAgentAgentsResponse:
- """
- Duplicates a SINGLE_PROMPT agent's live active version into a target organization
- (can also be the same organization). Copies all versioned configuration but strips
- organization-specific resources: knowledge base tools are removed, default variable
- values are blanked, and a new avatar is generated. The duplicate starts with a
- published V1 as its active version.
-
- **400 is returned when:**
- - The source agent is archived (`"Cannot duplicate an archived agent"`)
- - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
- - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
- - The agent is not `SINGLE_PROMPT` workflow type
-
- Parameters
- ----------
- id : str
- The ID of the source agent to duplicate
-
- target_organization_id : str
- MongoDB ObjectId of the target organization. Must be a 24-character hex string.
- The authenticated user must be a member of this organization.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- DuplicateAgentAgentsResponse
- Agent duplicated successfully
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.agents.duplicate_agent(
- id="id",
- target_organization_id="60d0fe4f5311236168a109ca",
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.duplicate_agent(
- id, target_organization_id=target_organization_id, request_options=request_options
- )
- return _response.data
-
- async def get_agent_workflow(
+ async def get_agent_widget_config(
self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetAgentIdWorkflowResponse:
+ ) -> GetAgentWidgetConfigResponse:
"""
- **Deprecated** โ prefer `GET /agent/{id}` (config is resolved into `_resolvedConfig`
- including prompt, tools, and post-call analytics).
-
- Returns the active version's prompt and tools for single-prompt agents, or the
- workflow graph data for workflow_graph agents. Customers still rely on this to
- fetch their current prompt + tools โ endpoint is kept live for now.
-
- **Caveat:** the `versionId` query param (if passed) is silently ignored.
- The response always reflects the currently-active version. To inspect a
- non-active version, use `GET /agent/{id}/versions/{versionId}`.
+ Returns the current web widget configuration for the agent. Also includes `assistantId` (same as the agent ID) as a convenience field for the widget embed code.
Parameters
----------
id : str
- The ID of the agent
+ Agent ObjectId
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- GetAgentIdWorkflowResponse
- Successful response
+ GetAgentWidgetConfigResponse
+ Widget configuration
Examples
--------
@@ -1532,56 +1296,41 @@ async def get_agent_workflow(
async def main() -> None:
- await client.atoms.agents.get_agent_workflow(
+ await client.atoms.agents.get_agent_widget_config(
id="id",
)
asyncio.run(main())
"""
- _response = await self._raw_client.get_agent_workflow(id, request_options=request_options)
+ _response = await self._raw_client.get_agent_widget_config(id, request_options=request_options)
return _response.data
- async def update_workflow_configuration(
+ async def update_agent_widget_config(
self,
id: str,
*,
- type: WorkflowType,
- workflow_graph: typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph] = OMIT,
- single_prompt_config: typing.Optional[SinglePromptConfig] = OMIT,
+ widget_config: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> None:
+ ) -> UpdateAgentWidgetConfigResponse:
"""
- **Deprecated** โ use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
-
- Directly mutates the legacy workflow document for an agent. This write path
- bypasses the versioning system entirely: the change is not captured as a
- new version, and future version activations may overwrite the legacy doc
- back to whatever the version snapshot contains.
-
- โ **Writing here on a versioned agent can silently wipe tools, prompt, or
- other fields that were missing from the PATCH payload.** Only use this if
- you know the agent is not using versioning, or if you are intentionally
- hot-patching the legacy doc.
+ Updates the web widget configuration for the agent. Only provided fields are updated (partial update). When `avatarUrl` is changed, the old CDN avatar is automatically deleted from S3. The `avatarUrl` must be a URL from the platform's CDN domain โ use `POST /agent/{id}/avatar/presigned-url` to upload first.
Parameters
----------
id : str
- The workflow ID (found at `agent.workflowId` on the agent document).
+ Agent ObjectId
- type : WorkflowType
-
- workflow_graph : typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph]
- Required when `type = workflow_graph`. Exactly one of `workflowGraph` or `singlePromptConfig` must be provided.
-
- single_prompt_config : typing.Optional[SinglePromptConfig]
+ widget_config : typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig]
+ All fields are optional โ only provided fields are updated
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- None
+ UpdateAgentWidgetConfigResponse
+ Updated widget configuration
Examples
--------
@@ -1595,79 +1344,51 @@ async def update_workflow_configuration(
async def main() -> None:
- await client.atoms.agents.update_workflow_configuration(
- id="60d0fe4f5311236168a109ca",
- type="workflow_graph",
+ await client.atoms.agents.update_agent_widget_config(
+ id="id",
)
asyncio.run(main())
"""
- _response = await self._raw_client.update_workflow_configuration(
- id,
- type=type,
- workflow_graph=workflow_graph,
- single_prompt_config=single_prompt_config,
- request_options=request_options,
+ _response = await self._raw_client.update_agent_widget_config(
+ id, widget_config=widget_config, request_options=request_options
)
return _response.data
- async def create_with_ai(
+ async def get_agent_avatar_presigned_url(
self,
+ id: str,
*,
- name: typing.Optional[str] = OMIT,
- description: typing.Optional[str] = OMIT,
- questions: typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]] = OMIT,
- type: typing.Optional[CreateWithAiAgentsRequestType] = OMIT,
- emotive_toggle: typing.Optional[bool] = OMIT,
- voice_id: typing.Optional[str] = OMIT,
- voice_model: typing.Optional[CreateWithAiAgentsRequestVoiceModel] = OMIT,
- knowledge_base_id: typing.Optional[str] = OMIT,
+ file_name: str,
+ content_type: str,
+ file_size: float,
request_options: typing.Optional[RequestOptions] = None,
- ) -> CreateWithAiAgentsResponse:
+ ) -> GetAgentAvatarPresignedUrlResponse:
"""
- Create a new single-prompt agent from a natural-language brief or structured
- question/answer pairs. Atoms generates the system prompt for you.
-
- Provide either `description` (free-form brief) or a non-empty `questions` array,
- but not both. The `emotiveToggle`, `voiceId`, and `voiceModel` fields must be
- supplied as a 3-tuple or omitted entirely.
+ Generates a pre-signed S3 upload URL for the agent's widget avatar image. Upload the image directly to S3 using the returned `presignedUrl`, then save `cdnUrl` as the agent's avatar via `PATCH /agent/{id}/widget-config`.
Parameters
----------
- name : typing.Optional[str]
- Agent name (trimmed). Auto-generated when omitted.
-
- description : typing.Optional[str]
- Free-form natural-language description of what the agent should do.
- Atoms turns this into the system prompt. Use this OR `questions`, not both.
-
- questions : typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]]
- Structured question/answer pairs. Atoms uses these to compose the
- system prompt. Use this OR `description`, not both.
-
- type : typing.Optional[CreateWithAiAgentsRequestType]
- Currently the only supported agent type.
-
- emotive_toggle : typing.Optional[bool]
- Enable emotive synthesis. Must be paired with `voiceId` + `voiceModel`.
+ id : str
+ Agent ObjectId
- voice_id : typing.Optional[str]
- Voice ID for synthesis. Must be paired with `emotiveToggle` + `voiceModel`.
+ file_name : str
+ Original file name (used to construct the S3 key)
- voice_model : typing.Optional[CreateWithAiAgentsRequestVoiceModel]
- Synthesizer to use. Must be paired with `emotiveToggle` + `voiceId`.
+ content_type : str
+ MIME type โ must start with `image/`
- knowledge_base_id : typing.Optional[str]
- Optional knowledge-base ID to attach to the new agent.
+ file_size : float
+ File size in bytes โ must be > 0 and โค 2 MB (2,097,152 bytes)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- CreateWithAiAgentsResponse
- Agent created. `data` is the new agent's `_id` (string).
+ GetAgentAvatarPresignedUrlResponse
+ Pre-signed upload URL and CDN URL
Examples
--------
@@ -1681,55 +1402,50 @@ async def create_with_ai(
async def main() -> None:
- await client.atoms.agents.create_with_ai()
+ await client.atoms.agents.get_agent_avatar_presigned_url(
+ id="id",
+ file_name="fileName",
+ content_type="contentType",
+ file_size=1.1,
+ )
asyncio.run(main())
"""
- _response = await self._raw_client.create_with_ai(
- name=name,
- description=description,
- questions=questions,
- type=type,
- emotive_toggle=emotive_toggle,
- voice_id=voice_id,
- voice_model=voice_model,
- knowledge_base_id=knowledge_base_id,
- request_options=request_options,
+ _response = await self._raw_client.get_agent_avatar_presigned_url(
+ id, file_name=file_name, content_type=content_type, file_size=file_size, request_options=request_options
)
return _response.data
- async def list_call_logs(
+ async def get_agent_call_logs(
self,
id: str,
*,
- page: typing.Optional[str] = None,
- offset: typing.Optional[str] = None,
+ page: typing.Optional[int] = None,
+ offset: typing.Optional[int] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> ListCallLogsAgentsResponse:
+ ) -> GetAgentCallLogsResponse:
"""
- Returns paginated conversation logs (calls) for a specific agent in the caller's
- organization. Use `GET /conversation` for cross-agent log listing; use this when
- you already have an agent ID.
+ Returns paginated call logs for a specific agent.
Parameters
----------
id : str
- The agent ID
+ Agent ObjectId
- page : typing.Optional[str]
- Page number (string-encoded positive integer).
+ page : typing.Optional[int]
+ Page number (default 1)
- offset : typing.Optional[str]
- Page size (string-encoded positive integer).
+ offset : typing.Optional[int]
+ Records per page (default 10)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- ListCallLogsAgentsResponse
- Paginated list of conversation logs for the agent.
+ GetAgentCallLogsResponse
+ Paginated call logs
Examples
--------
@@ -1743,88 +1459,52 @@ async def list_call_logs(
async def main() -> None:
- await client.atoms.agents.list_call_logs(
- id="60d0fe4f5311236168a109ca",
+ await client.atoms.agents.get_agent_call_logs(
+ id="id",
)
asyncio.run(main())
"""
- _response = await self._raw_client.list_call_logs(id, page=page, offset=offset, request_options=request_options)
+ _response = await self._raw_client.get_agent_call_logs(
+ id, page=page, offset=offset, request_options=request_options
+ )
return _response.data
- async def get_widget_config(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetWidgetConfigAgentsResponse:
+ async def archive_agent(
+ self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
+ ) -> ArchiveAgentAgentsResponse:
"""
- Returns the embeddable web widget configuration for the agent (theme, copy,
- consent prompts, branding, voice/chat mode, allowlist). The response merges the
- stored config with `assistantId: ` injected.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetWidgetConfigAgentsResponse
- Widget configuration for the agent.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.agents.get_widget_config(
- id="60d0fe4f5311236168a109ca",
- )
-
+ Soft-archives the agent โ it is excluded from listings and stops accepting calls,
+ but all data is preserved and the operation is fully reversible.
- asyncio.run(main())
- """
- _response = await self._raw_client.get_widget_config(id, request_options=request_options)
- return _response.data
+ Pass `?on=false` to unarchive (restore) a previously archived agent.
- async def update_widget_config(
- self, id: str, *, widget_config: WidgetConfig, request_options: typing.Optional[RequestOptions] = None
- ) -> UpdateWidgetConfigAgentsResponse:
- """
- Merge updates into the agent's embeddable widget config. Only the fields in the
- request body are overwritten; everything else is preserved. Returns the full
- widget config after merge.
+ **409 is returned when:**
+ - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
+ - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
Parameters
----------
id : str
- The agent ID
- widget_config : WidgetConfig
+ on : typing.Optional[bool]
+ `true` (default) โ archive the agent.
+ `false` โ unarchive (restore) a previously archived agent.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- UpdateWidgetConfigAgentsResponse
- Merged widget configuration.
+ ArchiveAgentAgentsResponse
+ Agent archived or unarchived successfully
Examples
--------
import asyncio
from smallestai import AsyncSmallestAI
- from smallestai.atoms import WidgetConfig
client = AsyncSmallestAI(
api_key="YOUR_API_KEY",
@@ -1832,55 +1512,12 @@ async def update_widget_config(
async def main() -> None:
- await client.atoms.agents.update_widget_config(
- id="60d0fe4f5311236168a109ca",
- widget_config=WidgetConfig(),
+ await client.atoms.agents.archive_agent(
+ id="id",
)
asyncio.run(main())
"""
- _response = await self._raw_client.update_widget_config(
- id, widget_config=widget_config, request_options=request_options
- )
- return _response.data
-
- async def get_prompt_config(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetPromptConfigAgentsResponse:
- """
- Returns the canonical question definitions, option choices, and example labels
- used by the agent-builder UI when collecting input for `POST /agent/with-ai`.
-
- Use this to programmatically discover what questions to ask end-users when
- building agent-creation UIs.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetPromptConfigAgentsResponse
- Prompt-config catalogue.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.agents.get_prompt_config()
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.get_prompt_config(request_options=request_options)
+ _response = await self._raw_client.archive_agent(id, on=on, request_options=request_options)
return _response.data
diff --git a/src/smallestai/atoms/agents/raw_client.py b/src/smallestai/atoms/agents/raw_client.py
index 0b6645fd..305a5bf2 100644
--- a/src/smallestai/atoms/agents/raw_client.py
+++ b/src/smallestai/atoms/agents/raw_client.py
@@ -17,8 +17,6 @@
from ..errors.internal_server_error import InternalServerError
from ..errors.not_found_error import NotFoundError
from ..errors.unauthorized_error import UnauthorizedError
-from ..types.single_prompt_config import SinglePromptConfig
-from ..types.widget_config import WidgetConfig
from ..types.workflow_type import WorkflowType
from .types.archive_agent_agents_response import ArchiveAgentAgentsResponse
from .types.create_agent_agents_response import CreateAgentAgentsResponse
@@ -36,25 +34,18 @@
from .types.create_agent_request_timezone import CreateAgentRequestTimezone
from .types.create_agent_request_voice_detection_config import CreateAgentRequestVoiceDetectionConfig
from .types.create_agent_request_voice_mail_detection_config import CreateAgentRequestVoiceMailDetectionConfig
-from .types.create_with_ai_agents_request_questions_item import CreateWithAiAgentsRequestQuestionsItem
-from .types.create_with_ai_agents_request_type import CreateWithAiAgentsRequestType
-from .types.create_with_ai_agents_request_voice_model import CreateWithAiAgentsRequestVoiceModel
-from .types.create_with_ai_agents_response import CreateWithAiAgentsResponse
from .types.duplicate_agent_agents_response import DuplicateAgentAgentsResponse
from .types.get_agent_agents_response import GetAgentAgentsResponse
-from .types.get_agent_id_workflow_response import GetAgentIdWorkflowResponse
-from .types.get_prompt_config_agents_response import GetPromptConfigAgentsResponse
-from .types.get_widget_config_agents_response import GetWidgetConfigAgentsResponse
+from .types.get_agent_avatar_presigned_url_response import GetAgentAvatarPresignedUrlResponse
+from .types.get_agent_call_logs_response import GetAgentCallLogsResponse
+from .types.get_agent_widget_config_response import GetAgentWidgetConfigResponse
from .types.list_agents_agents_request_sort_field import ListAgentsAgentsRequestSortField
from .types.list_agents_agents_request_sort_order import ListAgentsAgentsRequestSortOrder
from .types.list_agents_agents_request_type import ListAgentsAgentsRequestType
from .types.list_agents_agents_response import ListAgentsAgentsResponse
-from .types.list_call_logs_agents_response import ListCallLogsAgentsResponse
from .types.update_agent_agents_response import UpdateAgentAgentsResponse
-from .types.update_widget_config_agents_response import UpdateWidgetConfigAgentsResponse
-from .types.update_workflow_configuration_agents_request_workflow_graph import (
- UpdateWorkflowConfigurationAgentsRequestWorkflowGraph,
-)
+from .types.update_agent_widget_config_request_widget_config import UpdateAgentWidgetConfigRequestWidgetConfig
+from .types.update_agent_widget_config_response import UpdateAgentWidgetConfigResponse
from pydantic import ValidationError
# this is used as the default value for optional parameters
@@ -250,10 +241,11 @@ def create_agent(
Tamil (`ta`) cannot be combined with other languages in `supported`.
synthesizer : typing.Optional[CreateAgentRequestSynthesizer]
- Synthesizer (TTS) configuration for the agent.
- Models `waves`, `waves_lightning_large`, `waves_lightning_v2`, and `waves_lightning_v3_1`
- validate `voiceId` against the Waves API. All other models accept any voiceId.
- Cloned voices are regular voiceIds โ use them with any compatible Waves model.
+ Synthesizer (TTS) configuration for the agent. Model
+ `waves_lightning_v3_1` validates `voiceId` against the Waves
+ API. `gpt-realtime` and `gpt-realtime-mini` accept any voiceId.
+ Cloned voices are regular voiceIds. Use them with a compatible
+ Waves model.
global_knowledge_base_id : typing.Optional[str]
The global knowledge base ID of the agent. You can create a global knowledge base by using the /knowledgebase endpoint and assign it to the agent. The agent will use this knowledge base for its responses.
@@ -476,6 +468,126 @@ def create_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ def duplicate_agent(
+ self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[DuplicateAgentAgentsResponse]:
+ """
+ Duplicates a SINGLE_PROMPT agent's live active version into a target organization
+ (can also be the same organization). Copies all versioned configuration but strips
+ organization-specific resources: knowledge base tools are removed, default variable
+ values are blanked, and a new avatar is generated. The duplicate starts with a
+ published V1 as its active version.
+
+ **400 is returned when:**
+ - The source agent is archived (`"Cannot duplicate an archived agent"`)
+ - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
+ - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
+ - The agent is not `SINGLE_PROMPT` workflow type
+
+ Parameters
+ ----------
+ id : str
+ The ID of the source agent to duplicate
+
+ target_organization_id : str
+ MongoDB ObjectId of the target organization. Must be a 24-character hex string.
+ The authenticated user must be a member of this organization.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[DuplicateAgentAgentsResponse]
+ Agent duplicated successfully
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"agent/{encode_path_param(id)}/duplicate",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "targetOrganizationId": target_organization_id,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ DuplicateAgentAgentsResponse,
+ construct_type(
+ type_=DuplicateAgentAgentsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
def get_agent(
self,
id: str,
@@ -629,9 +741,7 @@ def update_agent(
**Non-versioned agents** (no active version): all configuration fields are accepted,
the same full set as `POST /agent`.
- **400 is also returned when:**
- - The agent is locked (`"Agent is locked, please unlock it to update"`)
- - Cross-field constraint violated (e.g. `north_indic` language requires `transcriberType: pulse`)
+ **400 is also returned when a cross-field constraint is violated** (for example, `north_indic` language requires `transcriberType: pulse`).
**403** is returned when selecting a gated model (`gpt-5.2`, `electron-kogta`, `electron-kogta-v2`)
without org-level access.
@@ -758,65 +868,41 @@ def update_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def archive_agent(
- self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[ArchiveAgentAgentsResponse]:
+ def get_agent_widget_config(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetAgentWidgetConfigResponse]:
"""
- Soft-archives the agent โ it is excluded from listings and stops accepting calls,
- but all data is preserved and the operation is fully reversible.
-
- Pass `?on=false` to unarchive (restore) a previously archived agent.
-
- **409 is returned when:**
- - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
- - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
+ Returns the current web widget configuration for the agent. Also includes `assistantId` (same as the agent ID) as a convenience field for the widget embed code.
Parameters
----------
id : str
-
- on : typing.Optional[bool]
- `true` (default) โ archive the agent.
- `false` โ unarchive (restore) a previously archived agent.
+ Agent ObjectId
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[ArchiveAgentAgentsResponse]
- Agent archived or unarchived successfully
+ HttpResponse[GetAgentWidgetConfigResponse]
+ Widget configuration
"""
_response = self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/archive",
+ f"agent/{encode_path_param(id)}/widget-config",
base_url=self._client_wrapper.get_environment().atoms,
- method="DELETE",
- params={
- "on": on,
- },
+ method="GET",
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ArchiveAgentAgentsResponse,
+ GetAgentWidgetConfigResponse,
construct_type(
- type_=ArchiveAgentAgentsResponse, # type: ignore
+ type_=GetAgentWidgetConfigResponse, # type: ignore
object_=_response.json(),
),
)
return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 401:
raise UnauthorizedError(
headers=dict(_response.headers),
@@ -828,17 +914,6 @@ def archive_agent(
),
),
)
- if _response.status_code == 403:
- raise ForbiddenError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 404:
raise NotFoundError(
headers=dict(_response.headers),
@@ -850,17 +925,6 @@ def archive_agent(
),
),
)
- if _response.status_code == 409:
- raise ConflictError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -881,45 +945,40 @@ def archive_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def duplicate_agent(
- self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[DuplicateAgentAgentsResponse]:
+ def update_agent_widget_config(
+ self,
+ id: str,
+ *,
+ widget_config: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[UpdateAgentWidgetConfigResponse]:
"""
- Duplicates a SINGLE_PROMPT agent's live active version into a target organization
- (can also be the same organization). Copies all versioned configuration but strips
- organization-specific resources: knowledge base tools are removed, default variable
- values are blanked, and a new avatar is generated. The duplicate starts with a
- published V1 as its active version.
-
- **400 is returned when:**
- - The source agent is archived (`"Cannot duplicate an archived agent"`)
- - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
- - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
- - The agent is not `SINGLE_PROMPT` workflow type
+ Updates the web widget configuration for the agent. Only provided fields are updated (partial update). When `avatarUrl` is changed, the old CDN avatar is automatically deleted from S3. The `avatarUrl` must be a URL from the platform's CDN domain โ use `POST /agent/{id}/avatar/presigned-url` to upload first.
Parameters
----------
id : str
- The ID of the source agent to duplicate
+ Agent ObjectId
- target_organization_id : str
- MongoDB ObjectId of the target organization. Must be a 24-character hex string.
- The authenticated user must be a member of this organization.
+ widget_config : typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig]
+ All fields are optional โ only provided fields are updated
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[DuplicateAgentAgentsResponse]
- Agent duplicated successfully
+ HttpResponse[UpdateAgentWidgetConfigResponse]
+ Updated widget configuration
"""
_response = self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/duplicate",
+ f"agent/{encode_path_param(id)}/widget-config",
base_url=self._client_wrapper.get_environment().atoms,
- method="POST",
+ method="PATCH",
json={
- "targetOrganizationId": target_organization_id,
+ "widgetConfig": convert_and_respect_annotation_metadata(
+ object_=widget_config, annotation=UpdateAgentWidgetConfigRequestWidgetConfig, direction="write"
+ ),
},
headers={
"content-type": "application/json",
@@ -930,9 +989,9 @@ def duplicate_agent(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- DuplicateAgentAgentsResponse,
+ UpdateAgentWidgetConfigResponse,
construct_type(
- type_=DuplicateAgentAgentsResponse, # type: ignore
+ type_=UpdateAgentWidgetConfigResponse, # type: ignore
object_=_response.json(),
),
)
@@ -959,17 +1018,6 @@ def duplicate_agent(
),
),
)
- if _response.status_code == 403:
- raise ForbiddenError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 404:
raise NotFoundError(
headers=dict(_response.headers),
@@ -1001,50 +1049,76 @@ def duplicate_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def get_agent_workflow(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetAgentIdWorkflowResponse]:
+ def get_agent_avatar_presigned_url(
+ self,
+ id: str,
+ *,
+ file_name: str,
+ content_type: str,
+ file_size: float,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetAgentAvatarPresignedUrlResponse]:
"""
- **Deprecated** โ prefer `GET /agent/{id}` (config is resolved into `_resolvedConfig`
- including prompt, tools, and post-call analytics).
-
- Returns the active version's prompt and tools for single-prompt agents, or the
- workflow graph data for workflow_graph agents. Customers still rely on this to
- fetch their current prompt + tools โ endpoint is kept live for now.
-
- **Caveat:** the `versionId` query param (if passed) is silently ignored.
- The response always reflects the currently-active version. To inspect a
- non-active version, use `GET /agent/{id}/versions/{versionId}`.
+ Generates a pre-signed S3 upload URL for the agent's widget avatar image. Upload the image directly to S3 using the returned `presignedUrl`, then save `cdnUrl` as the agent's avatar via `PATCH /agent/{id}/widget-config`.
Parameters
----------
id : str
- The ID of the agent
+ Agent ObjectId
+
+ file_name : str
+ Original file name (used to construct the S3 key)
+
+ content_type : str
+ MIME type โ must start with `image/`
+
+ file_size : float
+ File size in bytes โ must be > 0 and โค 2 MB (2,097,152 bytes)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[GetAgentIdWorkflowResponse]
- Successful response
+ HttpResponse[GetAgentAvatarPresignedUrlResponse]
+ Pre-signed upload URL and CDN URL
"""
_response = self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/workflow",
+ f"agent/{encode_path_param(id)}/avatar/presigned-url",
base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
+ method="POST",
+ json={
+ "fileName": file_name,
+ "contentType": content_type,
+ "fileSize": file_size,
+ },
+ headers={
+ "content-type": "application/json",
+ },
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetAgentIdWorkflowResponse,
+ GetAgentAvatarPresignedUrlResponse,
construct_type(
- type_=GetAgentIdWorkflowResponse, # type: ignore
+ type_=GetAgentAvatarPresignedUrlResponse, # type: ignore
object_=_response.json(),
),
)
return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 401:
raise UnauthorizedError(
headers=dict(_response.headers),
@@ -1087,288 +1161,35 @@ def get_agent_workflow(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def update_workflow_configuration(
+ def get_agent_call_logs(
self,
id: str,
*,
- type: WorkflowType,
- workflow_graph: typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph] = OMIT,
- single_prompt_config: typing.Optional[SinglePromptConfig] = OMIT,
+ page: typing.Optional[int] = None,
+ offset: typing.Optional[int] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> HttpResponse[None]:
+ ) -> HttpResponse[GetAgentCallLogsResponse]:
"""
- **Deprecated** โ use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
-
- Directly mutates the legacy workflow document for an agent. This write path
- bypasses the versioning system entirely: the change is not captured as a
- new version, and future version activations may overwrite the legacy doc
- back to whatever the version snapshot contains.
-
- โ **Writing here on a versioned agent can silently wipe tools, prompt, or
- other fields that were missing from the PATCH payload.** Only use this if
- you know the agent is not using versioning, or if you are intentionally
- hot-patching the legacy doc.
-
- Parameters
- ----------
- id : str
- The workflow ID (found at `agent.workflowId` on the agent document).
-
- type : WorkflowType
-
- workflow_graph : typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph]
- Required when `type = workflow_graph`. Exactly one of `workflowGraph` or `singlePromptConfig` must be provided.
-
- single_prompt_config : typing.Optional[SinglePromptConfig]
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[None]
- """
- _response = self._client_wrapper.httpx_client.request(
- f"workflow/{encode_path_param(id)}",
- base_url=self._client_wrapper.get_environment().atoms,
- method="PATCH",
- json={
- "type": type,
- "workflowGraph": convert_and_respect_annotation_metadata(
- object_=workflow_graph,
- annotation=UpdateWorkflowConfigurationAgentsRequestWorkflowGraph,
- direction="write",
- ),
- "singlePromptConfig": convert_and_respect_annotation_metadata(
- object_=single_prompt_config, annotation=SinglePromptConfig, direction="write"
- ),
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- return HttpResponse(response=_response, data=None)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- def create_with_ai(
- self,
- *,
- name: typing.Optional[str] = OMIT,
- description: typing.Optional[str] = OMIT,
- questions: typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]] = OMIT,
- type: typing.Optional[CreateWithAiAgentsRequestType] = OMIT,
- emotive_toggle: typing.Optional[bool] = OMIT,
- voice_id: typing.Optional[str] = OMIT,
- voice_model: typing.Optional[CreateWithAiAgentsRequestVoiceModel] = OMIT,
- knowledge_base_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> HttpResponse[CreateWithAiAgentsResponse]:
- """
- Create a new single-prompt agent from a natural-language brief or structured
- question/answer pairs. Atoms generates the system prompt for you.
-
- Provide either `description` (free-form brief) or a non-empty `questions` array,
- but not both. The `emotiveToggle`, `voiceId`, and `voiceModel` fields must be
- supplied as a 3-tuple or omitted entirely.
-
- Parameters
- ----------
- name : typing.Optional[str]
- Agent name (trimmed). Auto-generated when omitted.
-
- description : typing.Optional[str]
- Free-form natural-language description of what the agent should do.
- Atoms turns this into the system prompt. Use this OR `questions`, not both.
-
- questions : typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]]
- Structured question/answer pairs. Atoms uses these to compose the
- system prompt. Use this OR `description`, not both.
-
- type : typing.Optional[CreateWithAiAgentsRequestType]
- Currently the only supported agent type.
-
- emotive_toggle : typing.Optional[bool]
- Enable emotive synthesis. Must be paired with `voiceId` + `voiceModel`.
-
- voice_id : typing.Optional[str]
- Voice ID for synthesis. Must be paired with `emotiveToggle` + `voiceModel`.
-
- voice_model : typing.Optional[CreateWithAiAgentsRequestVoiceModel]
- Synthesizer to use. Must be paired with `emotiveToggle` + `voiceId`.
-
- knowledge_base_id : typing.Optional[str]
- Optional knowledge-base ID to attach to the new agent.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[CreateWithAiAgentsResponse]
- Agent created. `data` is the new agent's `_id` (string).
- """
- _response = self._client_wrapper.httpx_client.request(
- "agent/with-ai",
- base_url=self._client_wrapper.get_environment().atoms,
- method="POST",
- json={
- "name": name,
- "description": description,
- "questions": convert_and_respect_annotation_metadata(
- object_=questions,
- annotation=typing.Sequence[CreateWithAiAgentsRequestQuestionsItem],
- direction="write",
- ),
- "type": type,
- "emotiveToggle": emotive_toggle,
- "voiceId": voice_id,
- "voiceModel": voice_model,
- "knowledgeBaseId": knowledge_base_id,
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- CreateWithAiAgentsResponse,
- construct_type(
- type_=CreateWithAiAgentsResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- def list_call_logs(
- self,
- id: str,
- *,
- page: typing.Optional[str] = None,
- offset: typing.Optional[str] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> HttpResponse[ListCallLogsAgentsResponse]:
- """
- Returns paginated conversation logs (calls) for a specific agent in the caller's
- organization. Use `GET /conversation` for cross-agent log listing; use this when
- you already have an agent ID.
+ Returns paginated call logs for a specific agent.
Parameters
----------
id : str
- The agent ID
+ Agent ObjectId
- page : typing.Optional[str]
- Page number (string-encoded positive integer).
+ page : typing.Optional[int]
+ Page number (default 1)
- offset : typing.Optional[str]
- Page size (string-encoded positive integer).
+ offset : typing.Optional[int]
+ Records per page (default 10)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[ListCallLogsAgentsResponse]
- Paginated list of conversation logs for the agent.
+ HttpResponse[GetAgentCallLogsResponse]
+ Paginated call logs
"""
_response = self._client_wrapper.httpx_client.request(
f"agent/{encode_path_param(id)}/call-logs",
@@ -1383,9 +1204,9 @@ def list_call_logs(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ListCallLogsAgentsResponse,
+ GetAgentCallLogsResponse,
construct_type(
- type_=ListCallLogsAgentsResponse, # type: ignore
+ type_=GetAgentCallLogsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -1401,17 +1222,6 @@ def list_call_logs(
),
),
)
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -1432,129 +1242,50 @@ def list_call_logs(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def get_widget_config(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetWidgetConfigAgentsResponse]:
+ def archive_agent(
+ self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[ArchiveAgentAgentsResponse]:
"""
- Returns the embeddable web widget configuration for the agent (theme, copy,
- consent prompts, branding, voice/chat mode, allowlist). The response merges the
- stored config with `assistantId: ` injected.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
+ Soft-archives the agent โ it is excluded from listings and stops accepting calls,
+ but all data is preserved and the operation is fully reversible.
- Returns
- -------
- HttpResponse[GetWidgetConfigAgentsResponse]
- Widget configuration for the agent.
- """
- _response = self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/widget-config",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetWidgetConfigAgentsResponse,
- construct_type(
- type_=GetWidgetConfigAgentsResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ Pass `?on=false` to unarchive (restore) a previously archived agent.
- def update_widget_config(
- self, id: str, *, widget_config: WidgetConfig, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[UpdateWidgetConfigAgentsResponse]:
- """
- Merge updates into the agent's embeddable widget config. Only the fields in the
- request body are overwritten; everything else is preserved. Returns the full
- widget config after merge.
+ **409 is returned when:**
+ - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
+ - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
Parameters
----------
id : str
- The agent ID
- widget_config : WidgetConfig
+ on : typing.Optional[bool]
+ `true` (default) โ archive the agent.
+ `false` โ unarchive (restore) a previously archived agent.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[UpdateWidgetConfigAgentsResponse]
- Merged widget configuration.
+ HttpResponse[ArchiveAgentAgentsResponse]
+ Agent archived or unarchived successfully
"""
_response = self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/widget-config",
+ f"agent/{encode_path_param(id)}/archive",
base_url=self._client_wrapper.get_environment().atoms,
- method="PATCH",
- json={
- "widgetConfig": convert_and_respect_annotation_metadata(
- object_=widget_config, annotation=WidgetConfig, direction="write"
- ),
- },
- headers={
- "content-type": "application/json",
+ method="DELETE",
+ params={
+ "on": on,
},
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- UpdateWidgetConfigAgentsResponse,
+ ArchiveAgentAgentsResponse,
construct_type(
- type_=UpdateWidgetConfigAgentsResponse, # type: ignore
+ type_=ArchiveAgentAgentsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -1581,8 +1312,8 @@ def update_widget_config(
),
),
)
- if _response.status_code == 404:
- raise NotFoundError(
+ if _response.status_code == 403:
+ raise ForbiddenError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
@@ -1592,8 +1323,8 @@ def update_widget_config(
),
),
)
- if _response.status_code == 500:
- raise InternalServerError(
+ if _response.status_code == 404:
+ raise NotFoundError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
@@ -1603,53 +1334,8 @@ def update_widget_config(
),
),
)
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- def get_prompt_config(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetPromptConfigAgentsResponse]:
- """
- Returns the canonical question definitions, option choices, and example labels
- used by the agent-builder UI when collecting input for `POST /agent/with-ai`.
-
- Use this to programmatically discover what questions to ask end-users when
- building agent-creation UIs.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[GetPromptConfigAgentsResponse]
- Prompt-config catalogue.
- """
- _response = self._client_wrapper.httpx_client.request(
- "agent/prompt-config",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetPromptConfigAgentsResponse,
- construct_type(
- type_=GetPromptConfigAgentsResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 401:
- raise UnauthorizedError(
+ if _response.status_code == 409:
+ raise ConflictError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
@@ -1869,10 +1555,11 @@ async def create_agent(
Tamil (`ta`) cannot be combined with other languages in `supported`.
synthesizer : typing.Optional[CreateAgentRequestSynthesizer]
- Synthesizer (TTS) configuration for the agent.
- Models `waves`, `waves_lightning_large`, `waves_lightning_v2`, and `waves_lightning_v3_1`
- validate `voiceId` against the Waves API. All other models accept any voiceId.
- Cloned voices are regular voiceIds โ use them with any compatible Waves model.
+ Synthesizer (TTS) configuration for the agent. Model
+ `waves_lightning_v3_1` validates `voiceId` against the Waves
+ API. `gpt-realtime` and `gpt-realtime-mini` accept any voiceId.
+ Cloned voices are regular voiceIds. Use them with a compatible
+ Waves model.
global_knowledge_base_id : typing.Optional[str]
The global knowledge base ID of the agent. You can create a global knowledge base by using the /knowledgebase endpoint and assign it to the agent. The agent will use this knowledge base for its responses.
@@ -1967,206 +1654,77 @@ async def create_agent(
json={
"name": name,
"description": description,
- "backgroundSound": background_sound,
- "language": convert_and_respect_annotation_metadata(
- object_=language, annotation=CreateAgentRequestLanguage, direction="write"
- ),
- "synthesizer": convert_and_respect_annotation_metadata(
- object_=synthesizer, annotation=CreateAgentRequestSynthesizer, direction="write"
- ),
- "globalKnowledgeBaseId": global_knowledge_base_id,
- "slmModel": slm_model,
- "defaultVariables": default_variables,
- "preCallAPI": convert_and_respect_annotation_metadata(
- object_=pre_call_api, annotation=CreateAgentRequestPreCallApi, direction="write"
- ),
- "globalPrompt": global_prompt,
- "telephonyProductId": telephony_product_id,
- "workflowType": workflow_type,
- "firstMessage": first_message,
- "muteUserUntilFirstBotResponse": mute_user_until_first_bot_response,
- "allowInterruptions": allow_interruptions,
- "waitForUserToSpeakFirst": wait_for_user_to_speak_first,
- "interruptionBackoffTimer": interruption_backoff_timer,
- "smartTurnConfig": convert_and_respect_annotation_metadata(
- object_=smart_turn_config, annotation=CreateAgentRequestSmartTurnConfig, direction="write"
- ),
- "voiceDetectionConfig": convert_and_respect_annotation_metadata(
- object_=voice_detection_config, annotation=CreateAgentRequestVoiceDetectionConfig, direction="write"
- ),
- "voiceMailDetectionConfig": convert_and_respect_annotation_metadata(
- object_=voice_mail_detection_config,
- annotation=CreateAgentRequestVoiceMailDetectionConfig,
- direction="write",
- ),
- "denoisingConfig": convert_and_respect_annotation_metadata(
- object_=denoising_config, annotation=CreateAgentRequestDenoisingConfig, direction="write"
- ),
- "redactionConfig": convert_and_respect_annotation_metadata(
- object_=redaction_config, annotation=CreateAgentRequestRedactionConfig, direction="write"
- ),
- "pronunciationDicts": convert_and_respect_annotation_metadata(
- object_=pronunciation_dicts,
- annotation=typing.Sequence[CreateAgentRequestPronunciationDictsItem],
- direction="write",
- ),
- "llmIdleTimeoutConfig": convert_and_respect_annotation_metadata(
- object_=llm_idle_timeout_config,
- annotation=CreateAgentRequestLlmIdleTimeoutConfig,
- direction="write",
- ),
- "sessionTimeoutConfig": convert_and_respect_annotation_metadata(
- object_=session_timeout_config, annotation=CreateAgentRequestSessionTimeoutConfig, direction="write"
- ),
- "timezone": convert_and_respect_annotation_metadata(
- object_=timezone, annotation=CreateAgentRequestTimezone, direction="write"
- ),
- "callDispositionConfig": call_disposition_config,
- "allowInboundCall": allow_inbound_call,
- "enableStyleGuide": enable_style_guide,
- "speechFormatting": speech_formatting,
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- CreateAgentAgentsResponse,
- construct_type(
- type_=CreateAgentAgentsResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 403:
- raise ForbiddenError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def get_agent(
- self,
- id: str,
- *,
- draft_id: typing.Optional[str] = None,
- version_id: typing.Optional[str] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[GetAgentAgentsResponse]:
- """
- Returns the agent document merged with the resolved config of the active version
- under `_resolvedConfig`. Non-versioned fields (name, telephonyProductId, allowInboundCall,
- etc.) sit at the top level; versioned fields (prompt, tools, language, synthesizer,
- post-call analytics, โฆ) are resolved from the target version and exposed under `_resolvedConfig`.
-
- **Previewing a draft or specific version**
-
- Pass `?draftId=` to resolve config from a specific draft instead of the active version.
- Pass `?versionId=` to resolve config from a specific published version.
- When either param is used, the response includes `_configSource: "draft" | "version" | "active"`
- indicating which config was resolved.
-
- Notable resolved fields in `_resolvedConfig`:
-
- - `prompt` โ active version's single-prompt text
- - `tools` โ configured tools on the resolved version
- - `postCallAnalyticsConfig` โ disposition metrics + analytics model flags
- - `modelName` โ LLM model name on the resolved version
- - `defaultLanguage`, `supportedLanguages` โ active language config
- - `firstMessage`, `globalPrompt` โ active messaging config
- - `workflowGraph` โ full node graph for `workflow_graph` agents
-
- To read prompt + tools alone, use `GET /agent/{id}/workflow` (deprecated for
- new integrations but still live). To inspect a specific non-active version,
- use `GET /agent/{id}/versions/{versionId}`.
-
- **400 โ also used for "not found":** if the agent ID does not exist in the
- organization, the API returns 400 with `errors: ["No agent found"]` rather than 404.
-
- Parameters
- ----------
- id : str
-
- draft_id : typing.Optional[str]
- Resolve `_resolvedConfig` from this draft instead of the active version. Sets `_configSource` to `"draft"` in the response.
-
- version_id : typing.Optional[str]
- Resolve `_resolvedConfig` from this published version instead of the active version. Sets `_configSource` to `"version"` in the response.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[GetAgentAgentsResponse]
- Successful response
- """
- _response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- params={
- "draftId": draft_id,
- "versionId": version_id,
+ "backgroundSound": background_sound,
+ "language": convert_and_respect_annotation_metadata(
+ object_=language, annotation=CreateAgentRequestLanguage, direction="write"
+ ),
+ "synthesizer": convert_and_respect_annotation_metadata(
+ object_=synthesizer, annotation=CreateAgentRequestSynthesizer, direction="write"
+ ),
+ "globalKnowledgeBaseId": global_knowledge_base_id,
+ "slmModel": slm_model,
+ "defaultVariables": default_variables,
+ "preCallAPI": convert_and_respect_annotation_metadata(
+ object_=pre_call_api, annotation=CreateAgentRequestPreCallApi, direction="write"
+ ),
+ "globalPrompt": global_prompt,
+ "telephonyProductId": telephony_product_id,
+ "workflowType": workflow_type,
+ "firstMessage": first_message,
+ "muteUserUntilFirstBotResponse": mute_user_until_first_bot_response,
+ "allowInterruptions": allow_interruptions,
+ "waitForUserToSpeakFirst": wait_for_user_to_speak_first,
+ "interruptionBackoffTimer": interruption_backoff_timer,
+ "smartTurnConfig": convert_and_respect_annotation_metadata(
+ object_=smart_turn_config, annotation=CreateAgentRequestSmartTurnConfig, direction="write"
+ ),
+ "voiceDetectionConfig": convert_and_respect_annotation_metadata(
+ object_=voice_detection_config, annotation=CreateAgentRequestVoiceDetectionConfig, direction="write"
+ ),
+ "voiceMailDetectionConfig": convert_and_respect_annotation_metadata(
+ object_=voice_mail_detection_config,
+ annotation=CreateAgentRequestVoiceMailDetectionConfig,
+ direction="write",
+ ),
+ "denoisingConfig": convert_and_respect_annotation_metadata(
+ object_=denoising_config, annotation=CreateAgentRequestDenoisingConfig, direction="write"
+ ),
+ "redactionConfig": convert_and_respect_annotation_metadata(
+ object_=redaction_config, annotation=CreateAgentRequestRedactionConfig, direction="write"
+ ),
+ "pronunciationDicts": convert_and_respect_annotation_metadata(
+ object_=pronunciation_dicts,
+ annotation=typing.Sequence[CreateAgentRequestPronunciationDictsItem],
+ direction="write",
+ ),
+ "llmIdleTimeoutConfig": convert_and_respect_annotation_metadata(
+ object_=llm_idle_timeout_config,
+ annotation=CreateAgentRequestLlmIdleTimeoutConfig,
+ direction="write",
+ ),
+ "sessionTimeoutConfig": convert_and_respect_annotation_metadata(
+ object_=session_timeout_config, annotation=CreateAgentRequestSessionTimeoutConfig, direction="write"
+ ),
+ "timezone": convert_and_respect_annotation_metadata(
+ object_=timezone, annotation=CreateAgentRequestTimezone, direction="write"
+ ),
+ "callDispositionConfig": call_disposition_config,
+ "allowInboundCall": allow_inbound_call,
+ "enableStyleGuide": enable_style_guide,
+ "speechFormatting": speech_formatting,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetAgentAgentsResponse,
+ CreateAgentAgentsResponse,
construct_type(
- type_=GetAgentAgentsResponse, # type: ignore
+ type_=CreateAgentAgentsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -2224,78 +1782,45 @@ async def get_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def update_agent(
- self,
- id: str,
- *,
- name: typing.Optional[str] = OMIT,
- description: typing.Optional[str] = OMIT,
- avatar_url: typing.Optional[str] = OMIT,
- telephony_product_id: typing.Optional[typing.Sequence[str]] = OMIT,
- allow_inbound_call: typing.Optional[bool] = OMIT,
- visible_to_everyone: typing.Optional[bool] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[UpdateAgentAgentsResponse]:
+ async def duplicate_agent(
+ self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[DuplicateAgentAgentsResponse]:
"""
- Update agent fields. Behavior depends on whether the agent has versioning enabled:
-
- **Versioned agents** (have an active published version): only metadata fields are accepted โ
- `name`, `description`, `avatarUrl`, `telephonyProductId`, `allowInboundCall`, `visibleToEveryone`.
- Submitting any config-level field returns 400 with
- `"Agent has versioning enabled. Config changes must be made through drafts."`.
- Use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
-
- **Non-versioned agents** (no active version): all configuration fields are accepted,
- the same full set as `POST /agent`.
-
- **400 is also returned when:**
- - The agent is locked (`"Agent is locked, please unlock it to update"`)
- - Cross-field constraint violated (e.g. `north_indic` language requires `transcriberType: pulse`)
+ Duplicates a SINGLE_PROMPT agent's live active version into a target organization
+ (can also be the same organization). Copies all versioned configuration but strips
+ organization-specific resources: knowledge base tools are removed, default variable
+ values are blanked, and a new avatar is generated. The duplicate starts with a
+ published V1 as its active version.
- **403** is returned when selecting a gated model (`gpt-5.2`, `electron-kogta`, `electron-kogta-v2`)
- without org-level access.
+ **400 is returned when:**
+ - The source agent is archived (`"Cannot duplicate an archived agent"`)
+ - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
+ - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
+ - The agent is not `SINGLE_PROMPT` workflow type
Parameters
----------
id : str
+ The ID of the source agent to duplicate
- name : typing.Optional[str]
- Name of the agent.
-
- description : typing.Optional[str]
- Description of the agent.
-
- avatar_url : typing.Optional[str]
- URL of the agent's avatar image.
-
- telephony_product_id : typing.Optional[typing.Sequence[str]]
- IDs of telephony products (phone numbers) to associate with the agent.
-
- allow_inbound_call : typing.Optional[bool]
- Whether the agent accepts inbound calls.
-
- visible_to_everyone : typing.Optional[bool]
- Whether the agent is visible to all members of the organization.
+ target_organization_id : str
+ MongoDB ObjectId of the target organization. Must be a 24-character hex string.
+ The authenticated user must be a member of this organization.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[UpdateAgentAgentsResponse]
- Agent updated successfully
+ AsyncHttpResponse[DuplicateAgentAgentsResponse]
+ Agent duplicated successfully
"""
_response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}",
+ f"agent/{encode_path_param(id)}/duplicate",
base_url=self._client_wrapper.get_environment().atoms,
- method="PATCH",
+ method="POST",
json={
- "name": name,
- "description": description,
- "avatarUrl": avatar_url,
- "telephonyProductId": telephony_product_id,
- "allowInboundCall": allow_inbound_call,
- "visibleToEveryone": visible_to_everyone,
+ "targetOrganizationId": target_organization_id,
},
headers={
"content-type": "application/json",
@@ -2306,9 +1831,9 @@ async def update_agent(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- UpdateAgentAgentsResponse,
+ DuplicateAgentAgentsResponse,
construct_type(
- type_=UpdateAgentAgentsResponse, # type: ignore
+ type_=DuplicateAgentAgentsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -2377,50 +1902,78 @@ async def update_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def archive_agent(
- self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[ArchiveAgentAgentsResponse]:
+ async def get_agent(
+ self,
+ id: str,
+ *,
+ draft_id: typing.Optional[str] = None,
+ version_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetAgentAgentsResponse]:
"""
- Soft-archives the agent โ it is excluded from listings and stops accepting calls,
- but all data is preserved and the operation is fully reversible.
+ Returns the agent document merged with the resolved config of the active version
+ under `_resolvedConfig`. Non-versioned fields (name, telephonyProductId, allowInboundCall,
+ etc.) sit at the top level; versioned fields (prompt, tools, language, synthesizer,
+ post-call analytics, โฆ) are resolved from the target version and exposed under `_resolvedConfig`.
- Pass `?on=false` to unarchive (restore) a previously archived agent.
+ **Previewing a draft or specific version**
- **409 is returned when:**
- - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
- - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
+ Pass `?draftId=` to resolve config from a specific draft instead of the active version.
+ Pass `?versionId=` to resolve config from a specific published version.
+ When either param is used, the response includes `_configSource: "draft" | "version" | "active"`
+ indicating which config was resolved.
+
+ Notable resolved fields in `_resolvedConfig`:
+
+ - `prompt` โ active version's single-prompt text
+ - `tools` โ configured tools on the resolved version
+ - `postCallAnalyticsConfig` โ disposition metrics + analytics model flags
+ - `modelName` โ LLM model name on the resolved version
+ - `defaultLanguage`, `supportedLanguages` โ active language config
+ - `firstMessage`, `globalPrompt` โ active messaging config
+ - `workflowGraph` โ full node graph for `workflow_graph` agents
+
+ To read prompt + tools alone, use `GET /agent/{id}/workflow` (deprecated for
+ new integrations but still live). To inspect a specific non-active version,
+ use `GET /agent/{id}/versions/{versionId}`.
+
+ **400 โ also used for "not found":** if the agent ID does not exist in the
+ organization, the API returns 400 with `errors: ["No agent found"]` rather than 404.
Parameters
----------
id : str
- on : typing.Optional[bool]
- `true` (default) โ archive the agent.
- `false` โ unarchive (restore) a previously archived agent.
+ draft_id : typing.Optional[str]
+ Resolve `_resolvedConfig` from this draft instead of the active version. Sets `_configSource` to `"draft"` in the response.
+
+ version_id : typing.Optional[str]
+ Resolve `_resolvedConfig` from this published version instead of the active version. Sets `_configSource` to `"version"` in the response.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ArchiveAgentAgentsResponse]
- Agent archived or unarchived successfully
+ AsyncHttpResponse[GetAgentAgentsResponse]
+ Successful response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/archive",
+ f"agent/{encode_path_param(id)}",
base_url=self._client_wrapper.get_environment().atoms,
- method="DELETE",
+ method="GET",
params={
- "on": on,
+ "draftId": draft_id,
+ "versionId": version_id,
},
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ArchiveAgentAgentsResponse,
+ GetAgentAgentsResponse,
construct_type(
- type_=ArchiveAgentAgentsResponse, # type: ignore
+ type_=GetAgentAgentsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -2458,28 +2011,6 @@ async def archive_agent(
),
),
)
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 409:
- raise ConflictError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -2500,45 +2031,76 @@ async def archive_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def duplicate_agent(
- self, id: str, *, target_organization_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[DuplicateAgentAgentsResponse]:
+ async def update_agent(
+ self,
+ id: str,
+ *,
+ name: typing.Optional[str] = OMIT,
+ description: typing.Optional[str] = OMIT,
+ avatar_url: typing.Optional[str] = OMIT,
+ telephony_product_id: typing.Optional[typing.Sequence[str]] = OMIT,
+ allow_inbound_call: typing.Optional[bool] = OMIT,
+ visible_to_everyone: typing.Optional[bool] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[UpdateAgentAgentsResponse]:
"""
- Duplicates a SINGLE_PROMPT agent's live active version into a target organization
- (can also be the same organization). Copies all versioned configuration but strips
- organization-specific resources: knowledge base tools are removed, default variable
- values are blanked, and a new avatar is generated. The duplicate starts with a
- published V1 as its active version.
+ Update agent fields. Behavior depends on whether the agent has versioning enabled:
+
+ **Versioned agents** (have an active published version): only metadata fields are accepted โ
+ `name`, `description`, `avatarUrl`, `telephonyProductId`, `allowInboundCall`, `visibleToEveryone`.
+ Submitting any config-level field returns 400 with
+ `"Agent has versioning enabled. Config changes must be made through drafts."`.
+ Use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
- **400 is returned when:**
- - The source agent is archived (`"Cannot duplicate an archived agent"`)
- - The agent has no `activeVersionId` (`"This agent has no active version and cannot be duplicated"`)
- - The active version exists but is not published/active (`"This agent has no active published version and cannot be duplicated"`)
- - The agent is not `SINGLE_PROMPT` workflow type
+ **Non-versioned agents** (no active version): all configuration fields are accepted,
+ the same full set as `POST /agent`.
+
+ **400 is also returned when a cross-field constraint is violated** (for example, `north_indic` language requires `transcriberType: pulse`).
+
+ **403** is returned when selecting a gated model (`gpt-5.2`, `electron-kogta`, `electron-kogta-v2`)
+ without org-level access.
Parameters
----------
id : str
- The ID of the source agent to duplicate
- target_organization_id : str
- MongoDB ObjectId of the target organization. Must be a 24-character hex string.
- The authenticated user must be a member of this organization.
+ name : typing.Optional[str]
+ Name of the agent.
+
+ description : typing.Optional[str]
+ Description of the agent.
+
+ avatar_url : typing.Optional[str]
+ URL of the agent's avatar image.
+
+ telephony_product_id : typing.Optional[typing.Sequence[str]]
+ IDs of telephony products (phone numbers) to associate with the agent.
+
+ allow_inbound_call : typing.Optional[bool]
+ Whether the agent accepts inbound calls.
+
+ visible_to_everyone : typing.Optional[bool]
+ Whether the agent is visible to all members of the organization.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[DuplicateAgentAgentsResponse]
- Agent duplicated successfully
+ AsyncHttpResponse[UpdateAgentAgentsResponse]
+ Agent updated successfully
"""
_response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/duplicate",
+ f"agent/{encode_path_param(id)}",
base_url=self._client_wrapper.get_environment().atoms,
- method="POST",
+ method="PATCH",
json={
- "targetOrganizationId": target_organization_id,
+ "name": name,
+ "description": description,
+ "avatarUrl": avatar_url,
+ "telephonyProductId": telephony_product_id,
+ "allowInboundCall": allow_inbound_call,
+ "visibleToEveryone": visible_to_everyone,
},
headers={
"content-type": "application/json",
@@ -2549,9 +2111,9 @@ async def duplicate_agent(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- DuplicateAgentAgentsResponse,
+ UpdateAgentAgentsResponse,
construct_type(
- type_=DuplicateAgentAgentsResponse, # type: ignore
+ type_=UpdateAgentAgentsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -2620,36 +2182,27 @@ async def duplicate_agent(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_agent_workflow(
+ async def get_agent_widget_config(
self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetAgentIdWorkflowResponse]:
+ ) -> AsyncHttpResponse[GetAgentWidgetConfigResponse]:
"""
- **Deprecated** โ prefer `GET /agent/{id}` (config is resolved into `_resolvedConfig`
- including prompt, tools, and post-call analytics).
-
- Returns the active version's prompt and tools for single-prompt agents, or the
- workflow graph data for workflow_graph agents. Customers still rely on this to
- fetch their current prompt + tools โ endpoint is kept live for now.
-
- **Caveat:** the `versionId` query param (if passed) is silently ignored.
- The response always reflects the currently-active version. To inspect a
- non-active version, use `GET /agent/{id}/versions/{versionId}`.
+ Returns the current web widget configuration for the agent. Also includes `assistantId` (same as the agent ID) as a convenience field for the widget embed code.
Parameters
----------
id : str
- The ID of the agent
+ Agent ObjectId
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[GetAgentIdWorkflowResponse]
- Successful response
+ AsyncHttpResponse[GetAgentWidgetConfigResponse]
+ Widget configuration
"""
_response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/workflow",
+ f"agent/{encode_path_param(id)}/widget-config",
base_url=self._client_wrapper.get_environment().atoms,
method="GET",
request_options=request_options,
@@ -2657,9 +2210,9 @@ async def get_agent_workflow(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetAgentIdWorkflowResponse,
+ GetAgentWidgetConfigResponse,
construct_type(
- type_=GetAgentIdWorkflowResponse, # type: ignore
+ type_=GetAgentWidgetConfigResponse, # type: ignore
object_=_response.json(),
),
)
@@ -2706,60 +2259,39 @@ async def get_agent_workflow(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def update_workflow_configuration(
+ async def update_agent_widget_config(
self,
id: str,
*,
- type: WorkflowType,
- workflow_graph: typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph] = OMIT,
- single_prompt_config: typing.Optional[SinglePromptConfig] = OMIT,
+ widget_config: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[None]:
+ ) -> AsyncHttpResponse[UpdateAgentWidgetConfigResponse]:
"""
- **Deprecated** โ use `PATCH /agent/{id}/drafts/{draftId}/config` instead.
-
- Directly mutates the legacy workflow document for an agent. This write path
- bypasses the versioning system entirely: the change is not captured as a
- new version, and future version activations may overwrite the legacy doc
- back to whatever the version snapshot contains.
-
- โ **Writing here on a versioned agent can silently wipe tools, prompt, or
- other fields that were missing from the PATCH payload.** Only use this if
- you know the agent is not using versioning, or if you are intentionally
- hot-patching the legacy doc.
+ Updates the web widget configuration for the agent. Only provided fields are updated (partial update). When `avatarUrl` is changed, the old CDN avatar is automatically deleted from S3. The `avatarUrl` must be a URL from the platform's CDN domain โ use `POST /agent/{id}/avatar/presigned-url` to upload first.
Parameters
----------
id : str
- The workflow ID (found at `agent.workflowId` on the agent document).
+ Agent ObjectId
- type : WorkflowType
-
- workflow_graph : typing.Optional[UpdateWorkflowConfigurationAgentsRequestWorkflowGraph]
- Required when `type = workflow_graph`. Exactly one of `workflowGraph` or `singlePromptConfig` must be provided.
-
- single_prompt_config : typing.Optional[SinglePromptConfig]
+ widget_config : typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfig]
+ All fields are optional โ only provided fields are updated
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[None]
+ AsyncHttpResponse[UpdateAgentWidgetConfigResponse]
+ Updated widget configuration
"""
_response = await self._client_wrapper.httpx_client.request(
- f"workflow/{encode_path_param(id)}",
+ f"agent/{encode_path_param(id)}/widget-config",
base_url=self._client_wrapper.get_environment().atoms,
method="PATCH",
json={
- "type": type,
- "workflowGraph": convert_and_respect_annotation_metadata(
- object_=workflow_graph,
- annotation=UpdateWorkflowConfigurationAgentsRequestWorkflowGraph,
- direction="write",
- ),
- "singlePromptConfig": convert_and_respect_annotation_metadata(
- object_=single_prompt_config, annotation=SinglePromptConfig, direction="write"
+ "widgetConfig": convert_and_respect_annotation_metadata(
+ object_=widget_config, annotation=UpdateAgentWidgetConfigRequestWidgetConfig, direction="write"
),
},
headers={
@@ -2770,7 +2302,14 @@ async def update_workflow_configuration(
)
try:
if 200 <= _response.status_code < 300:
- return AsyncHttpResponse(response=_response, data=None)
+ _data = typing.cast(
+ UpdateAgentWidgetConfigResponse,
+ construct_type(
+ type_=UpdateAgentWidgetConfigResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
if _response.status_code == 400:
raise BadRequestError(
headers=dict(_response.headers),
@@ -2824,80 +2363,48 @@ async def update_workflow_configuration(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def create_with_ai(
+ async def get_agent_avatar_presigned_url(
self,
+ id: str,
*,
- name: typing.Optional[str] = OMIT,
- description: typing.Optional[str] = OMIT,
- questions: typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]] = OMIT,
- type: typing.Optional[CreateWithAiAgentsRequestType] = OMIT,
- emotive_toggle: typing.Optional[bool] = OMIT,
- voice_id: typing.Optional[str] = OMIT,
- voice_model: typing.Optional[CreateWithAiAgentsRequestVoiceModel] = OMIT,
- knowledge_base_id: typing.Optional[str] = OMIT,
+ file_name: str,
+ content_type: str,
+ file_size: float,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[CreateWithAiAgentsResponse]:
+ ) -> AsyncHttpResponse[GetAgentAvatarPresignedUrlResponse]:
"""
- Create a new single-prompt agent from a natural-language brief or structured
- question/answer pairs. Atoms generates the system prompt for you.
-
- Provide either `description` (free-form brief) or a non-empty `questions` array,
- but not both. The `emotiveToggle`, `voiceId`, and `voiceModel` fields must be
- supplied as a 3-tuple or omitted entirely.
+ Generates a pre-signed S3 upload URL for the agent's widget avatar image. Upload the image directly to S3 using the returned `presignedUrl`, then save `cdnUrl` as the agent's avatar via `PATCH /agent/{id}/widget-config`.
Parameters
----------
- name : typing.Optional[str]
- Agent name (trimmed). Auto-generated when omitted.
-
- description : typing.Optional[str]
- Free-form natural-language description of what the agent should do.
- Atoms turns this into the system prompt. Use this OR `questions`, not both.
-
- questions : typing.Optional[typing.Sequence[CreateWithAiAgentsRequestQuestionsItem]]
- Structured question/answer pairs. Atoms uses these to compose the
- system prompt. Use this OR `description`, not both.
-
- type : typing.Optional[CreateWithAiAgentsRequestType]
- Currently the only supported agent type.
-
- emotive_toggle : typing.Optional[bool]
- Enable emotive synthesis. Must be paired with `voiceId` + `voiceModel`.
+ id : str
+ Agent ObjectId
- voice_id : typing.Optional[str]
- Voice ID for synthesis. Must be paired with `emotiveToggle` + `voiceModel`.
+ file_name : str
+ Original file name (used to construct the S3 key)
- voice_model : typing.Optional[CreateWithAiAgentsRequestVoiceModel]
- Synthesizer to use. Must be paired with `emotiveToggle` + `voiceId`.
+ content_type : str
+ MIME type โ must start with `image/`
- knowledge_base_id : typing.Optional[str]
- Optional knowledge-base ID to attach to the new agent.
+ file_size : float
+ File size in bytes โ must be > 0 and โค 2 MB (2,097,152 bytes)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[CreateWithAiAgentsResponse]
- Agent created. `data` is the new agent's `_id` (string).
+ AsyncHttpResponse[GetAgentAvatarPresignedUrlResponse]
+ Pre-signed upload URL and CDN URL
"""
_response = await self._client_wrapper.httpx_client.request(
- "agent/with-ai",
+ f"agent/{encode_path_param(id)}/avatar/presigned-url",
base_url=self._client_wrapper.get_environment().atoms,
method="POST",
json={
- "name": name,
- "description": description,
- "questions": convert_and_respect_annotation_metadata(
- object_=questions,
- annotation=typing.Sequence[CreateWithAiAgentsRequestQuestionsItem],
- direction="write",
- ),
- "type": type,
- "emotiveToggle": emotive_toggle,
- "voiceId": voice_id,
- "voiceModel": voice_model,
- "knowledgeBaseId": knowledge_base_id,
+ "fileName": file_name,
+ "contentType": content_type,
+ "fileSize": file_size,
},
headers={
"content-type": "application/json",
@@ -2908,9 +2415,9 @@ async def create_with_ai(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- CreateWithAiAgentsResponse,
+ GetAgentAvatarPresignedUrlResponse,
construct_type(
- type_=CreateWithAiAgentsResponse, # type: ignore
+ type_=GetAgentAvatarPresignedUrlResponse, # type: ignore
object_=_response.json(),
),
)
@@ -2937,6 +2444,17 @@ async def create_with_ai(
),
),
)
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -2957,37 +2475,35 @@ async def create_with_ai(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def list_call_logs(
+ async def get_agent_call_logs(
self,
id: str,
*,
- page: typing.Optional[str] = None,
- offset: typing.Optional[str] = None,
+ page: typing.Optional[int] = None,
+ offset: typing.Optional[int] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ListCallLogsAgentsResponse]:
+ ) -> AsyncHttpResponse[GetAgentCallLogsResponse]:
"""
- Returns paginated conversation logs (calls) for a specific agent in the caller's
- organization. Use `GET /conversation` for cross-agent log listing; use this when
- you already have an agent ID.
+ Returns paginated call logs for a specific agent.
Parameters
----------
id : str
- The agent ID
+ Agent ObjectId
- page : typing.Optional[str]
- Page number (string-encoded positive integer).
+ page : typing.Optional[int]
+ Page number (default 1)
- offset : typing.Optional[str]
- Page size (string-encoded positive integer).
+ offset : typing.Optional[int]
+ Records per page (default 10)
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ListCallLogsAgentsResponse]
- Paginated list of conversation logs for the agent.
+ AsyncHttpResponse[GetAgentCallLogsResponse]
+ Paginated call logs
"""
_response = await self._client_wrapper.httpx_client.request(
f"agent/{encode_path_param(id)}/call-logs",
@@ -3002,9 +2518,9 @@ async def list_call_logs(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ListCallLogsAgentsResponse,
+ GetAgentCallLogsResponse,
construct_type(
- type_=ListCallLogsAgentsResponse, # type: ignore
+ type_=GetAgentCallLogsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -3020,17 +2536,6 @@ async def list_call_logs(
),
),
)
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
@@ -3051,129 +2556,50 @@ async def list_call_logs(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_widget_config(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetWidgetConfigAgentsResponse]:
+ async def archive_agent(
+ self, id: str, *, on: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[ArchiveAgentAgentsResponse]:
"""
- Returns the embeddable web widget configuration for the agent (theme, copy,
- consent prompts, branding, voice/chat mode, allowlist). The response merges the
- stored config with `assistantId: ` injected.
-
- Parameters
- ----------
- id : str
- The agent ID
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
+ Soft-archives the agent โ it is excluded from listings and stops accepting calls,
+ but all data is preserved and the operation is fully reversible.
- Returns
- -------
- AsyncHttpResponse[GetWidgetConfigAgentsResponse]
- Widget configuration for the agent.
- """
- _response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/widget-config",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetWidgetConfigAgentsResponse,
- construct_type(
- type_=GetWidgetConfigAgentsResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ Pass `?on=false` to unarchive (restore) a previously archived agent.
- async def update_widget_config(
- self, id: str, *, widget_config: WidgetConfig, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[UpdateWidgetConfigAgentsResponse]:
- """
- Merge updates into the agent's embeddable widget config. Only the fields in the
- request body are overwritten; everything else is preserved. Returns the full
- widget config after merge.
+ **409 is returned when:**
+ - The agent is already in the requested state (`"Agent is already archived"` / `"Agent is already active"`)
+ - The agent is linked to an active campaign (`"Agent is associated with the [status] campaign "[name]". Complete or remove the campaign before archiving."`)
Parameters
----------
id : str
- The agent ID
- widget_config : WidgetConfig
+ on : typing.Optional[bool]
+ `true` (default) โ archive the agent.
+ `false` โ unarchive (restore) a previously archived agent.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[UpdateWidgetConfigAgentsResponse]
- Merged widget configuration.
+ AsyncHttpResponse[ArchiveAgentAgentsResponse]
+ Agent archived or unarchived successfully
"""
_response = await self._client_wrapper.httpx_client.request(
- f"agent/{encode_path_param(id)}/widget-config",
+ f"agent/{encode_path_param(id)}/archive",
base_url=self._client_wrapper.get_environment().atoms,
- method="PATCH",
- json={
- "widgetConfig": convert_and_respect_annotation_metadata(
- object_=widget_config, annotation=WidgetConfig, direction="write"
- ),
- },
- headers={
- "content-type": "application/json",
+ method="DELETE",
+ params={
+ "on": on,
},
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- UpdateWidgetConfigAgentsResponse,
+ ArchiveAgentAgentsResponse,
construct_type(
- type_=UpdateWidgetConfigAgentsResponse, # type: ignore
+ type_=ArchiveAgentAgentsResponse, # type: ignore
object_=_response.json(),
),
)
@@ -3200,8 +2626,8 @@ async def update_widget_config(
),
),
)
- if _response.status_code == 404:
- raise NotFoundError(
+ if _response.status_code == 403:
+ raise ForbiddenError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
@@ -3211,8 +2637,8 @@ async def update_widget_config(
),
),
)
- if _response.status_code == 500:
- raise InternalServerError(
+ if _response.status_code == 404:
+ raise NotFoundError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
@@ -3222,53 +2648,8 @@ async def update_widget_config(
),
),
)
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def get_prompt_config(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetPromptConfigAgentsResponse]:
- """
- Returns the canonical question definitions, option choices, and example labels
- used by the agent-builder UI when collecting input for `POST /agent/with-ai`.
-
- Use this to programmatically discover what questions to ask end-users when
- building agent-creation UIs.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[GetPromptConfigAgentsResponse]
- Prompt-config catalogue.
- """
- _response = await self._client_wrapper.httpx_client.request(
- "agent/prompt-config",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetPromptConfigAgentsResponse,
- construct_type(
- type_=GetPromptConfigAgentsResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 401:
- raise UnauthorizedError(
+ if _response.status_code == 409:
+ raise ConflictError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
diff --git a/src/smallestai/atoms/agents/types/__init__.py b/src/smallestai/atoms/agents/types/__init__.py
index 5cde0202..0e9848f5 100644
--- a/src/smallestai/atoms/agents/types/__init__.py
+++ b/src/smallestai/atoms/agents/types/__init__.py
@@ -2,3 +2,194 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .archive_agent_agents_response import ArchiveAgentAgentsResponse
+ from .archive_agent_agents_response_data import ArchiveAgentAgentsResponseData
+ from .archive_agent_agents_response_data_message import ArchiveAgentAgentsResponseDataMessage
+ from .create_agent_agents_response import CreateAgentAgentsResponse
+ from .create_agent_request_background_sound import CreateAgentRequestBackgroundSound
+ from .create_agent_request_denoising_config import CreateAgentRequestDenoisingConfig
+ from .create_agent_request_language import CreateAgentRequestLanguage
+ from .create_agent_request_language_default import CreateAgentRequestLanguageDefault
+ from .create_agent_request_language_supported_item import CreateAgentRequestLanguageSupportedItem
+ from .create_agent_request_language_switching import CreateAgentRequestLanguageSwitching
+ from .create_agent_request_llm_idle_timeout_config import CreateAgentRequestLlmIdleTimeoutConfig
+ from .create_agent_request_pre_call_api import CreateAgentRequestPreCallApi
+ from .create_agent_request_pre_call_api_method import CreateAgentRequestPreCallApiMethod
+ from .create_agent_request_pre_call_api_response_variables_item import (
+ CreateAgentRequestPreCallApiResponseVariablesItem,
+ )
+ from .create_agent_request_pronunciation_dicts_item import CreateAgentRequestPronunciationDictsItem
+ from .create_agent_request_redaction_config import CreateAgentRequestRedactionConfig
+ from .create_agent_request_session_timeout_config import CreateAgentRequestSessionTimeoutConfig
+ from .create_agent_request_slm_model import CreateAgentRequestSlmModel
+ from .create_agent_request_smart_turn_config import CreateAgentRequestSmartTurnConfig
+ from .create_agent_request_synthesizer import CreateAgentRequestSynthesizer
+ from .create_agent_request_synthesizer_voice_config import CreateAgentRequestSynthesizerVoiceConfig
+ from .create_agent_request_synthesizer_voice_config_gender import CreateAgentRequestSynthesizerVoiceConfigGender
+ from .create_agent_request_synthesizer_voice_config_model import CreateAgentRequestSynthesizerVoiceConfigModel
+ from .create_agent_request_timezone import CreateAgentRequestTimezone
+ from .create_agent_request_voice_detection_config import CreateAgentRequestVoiceDetectionConfig
+ from .create_agent_request_voice_mail_detection_config import CreateAgentRequestVoiceMailDetectionConfig
+ from .duplicate_agent_agents_response import DuplicateAgentAgentsResponse
+ from .duplicate_agent_agents_response_data import DuplicateAgentAgentsResponseData
+ from .get_agent_agents_response import GetAgentAgentsResponse
+ from .get_agent_avatar_presigned_url_response import GetAgentAvatarPresignedUrlResponse
+ from .get_agent_avatar_presigned_url_response_data import GetAgentAvatarPresignedUrlResponseData
+ from .get_agent_call_logs_response import GetAgentCallLogsResponse
+ from .get_agent_call_logs_response_data import GetAgentCallLogsResponseData
+ from .get_agent_call_logs_response_data_call_logs_item import GetAgentCallLogsResponseDataCallLogsItem
+ from .get_agent_widget_config_response import GetAgentWidgetConfigResponse
+ from .get_agent_widget_config_response_data import GetAgentWidgetConfigResponseData
+ from .get_agent_widget_config_response_data_mode import GetAgentWidgetConfigResponseDataMode
+ from .get_agent_widget_config_response_data_position import GetAgentWidgetConfigResponseDataPosition
+ from .get_agent_widget_config_response_data_size import GetAgentWidgetConfigResponseDataSize
+ from .get_agent_widget_config_response_data_theme import GetAgentWidgetConfigResponseDataTheme
+ from .list_agents_agents_request_sort_field import ListAgentsAgentsRequestSortField
+ from .list_agents_agents_request_sort_order import ListAgentsAgentsRequestSortOrder
+ from .list_agents_agents_request_type import ListAgentsAgentsRequestType
+ from .list_agents_agents_response import ListAgentsAgentsResponse
+ from .list_agents_agents_response_data import ListAgentsAgentsResponseData
+ from .update_agent_agents_response import UpdateAgentAgentsResponse
+ from .update_agent_widget_config_request_widget_config import UpdateAgentWidgetConfigRequestWidgetConfig
+ from .update_agent_widget_config_request_widget_config_mode import UpdateAgentWidgetConfigRequestWidgetConfigMode
+ from .update_agent_widget_config_request_widget_config_position import (
+ UpdateAgentWidgetConfigRequestWidgetConfigPosition,
+ )
+ from .update_agent_widget_config_request_widget_config_size import UpdateAgentWidgetConfigRequestWidgetConfigSize
+ from .update_agent_widget_config_request_widget_config_theme import UpdateAgentWidgetConfigRequestWidgetConfigTheme
+ from .update_agent_widget_config_response import UpdateAgentWidgetConfigResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "ArchiveAgentAgentsResponse": ".archive_agent_agents_response",
+ "ArchiveAgentAgentsResponseData": ".archive_agent_agents_response_data",
+ "ArchiveAgentAgentsResponseDataMessage": ".archive_agent_agents_response_data_message",
+ "CreateAgentAgentsResponse": ".create_agent_agents_response",
+ "CreateAgentRequestBackgroundSound": ".create_agent_request_background_sound",
+ "CreateAgentRequestDenoisingConfig": ".create_agent_request_denoising_config",
+ "CreateAgentRequestLanguage": ".create_agent_request_language",
+ "CreateAgentRequestLanguageDefault": ".create_agent_request_language_default",
+ "CreateAgentRequestLanguageSupportedItem": ".create_agent_request_language_supported_item",
+ "CreateAgentRequestLanguageSwitching": ".create_agent_request_language_switching",
+ "CreateAgentRequestLlmIdleTimeoutConfig": ".create_agent_request_llm_idle_timeout_config",
+ "CreateAgentRequestPreCallApi": ".create_agent_request_pre_call_api",
+ "CreateAgentRequestPreCallApiMethod": ".create_agent_request_pre_call_api_method",
+ "CreateAgentRequestPreCallApiResponseVariablesItem": ".create_agent_request_pre_call_api_response_variables_item",
+ "CreateAgentRequestPronunciationDictsItem": ".create_agent_request_pronunciation_dicts_item",
+ "CreateAgentRequestRedactionConfig": ".create_agent_request_redaction_config",
+ "CreateAgentRequestSessionTimeoutConfig": ".create_agent_request_session_timeout_config",
+ "CreateAgentRequestSlmModel": ".create_agent_request_slm_model",
+ "CreateAgentRequestSmartTurnConfig": ".create_agent_request_smart_turn_config",
+ "CreateAgentRequestSynthesizer": ".create_agent_request_synthesizer",
+ "CreateAgentRequestSynthesizerVoiceConfig": ".create_agent_request_synthesizer_voice_config",
+ "CreateAgentRequestSynthesizerVoiceConfigGender": ".create_agent_request_synthesizer_voice_config_gender",
+ "CreateAgentRequestSynthesizerVoiceConfigModel": ".create_agent_request_synthesizer_voice_config_model",
+ "CreateAgentRequestTimezone": ".create_agent_request_timezone",
+ "CreateAgentRequestVoiceDetectionConfig": ".create_agent_request_voice_detection_config",
+ "CreateAgentRequestVoiceMailDetectionConfig": ".create_agent_request_voice_mail_detection_config",
+ "DuplicateAgentAgentsResponse": ".duplicate_agent_agents_response",
+ "DuplicateAgentAgentsResponseData": ".duplicate_agent_agents_response_data",
+ "GetAgentAgentsResponse": ".get_agent_agents_response",
+ "GetAgentAvatarPresignedUrlResponse": ".get_agent_avatar_presigned_url_response",
+ "GetAgentAvatarPresignedUrlResponseData": ".get_agent_avatar_presigned_url_response_data",
+ "GetAgentCallLogsResponse": ".get_agent_call_logs_response",
+ "GetAgentCallLogsResponseData": ".get_agent_call_logs_response_data",
+ "GetAgentCallLogsResponseDataCallLogsItem": ".get_agent_call_logs_response_data_call_logs_item",
+ "GetAgentWidgetConfigResponse": ".get_agent_widget_config_response",
+ "GetAgentWidgetConfigResponseData": ".get_agent_widget_config_response_data",
+ "GetAgentWidgetConfigResponseDataMode": ".get_agent_widget_config_response_data_mode",
+ "GetAgentWidgetConfigResponseDataPosition": ".get_agent_widget_config_response_data_position",
+ "GetAgentWidgetConfigResponseDataSize": ".get_agent_widget_config_response_data_size",
+ "GetAgentWidgetConfigResponseDataTheme": ".get_agent_widget_config_response_data_theme",
+ "ListAgentsAgentsRequestSortField": ".list_agents_agents_request_sort_field",
+ "ListAgentsAgentsRequestSortOrder": ".list_agents_agents_request_sort_order",
+ "ListAgentsAgentsRequestType": ".list_agents_agents_request_type",
+ "ListAgentsAgentsResponse": ".list_agents_agents_response",
+ "ListAgentsAgentsResponseData": ".list_agents_agents_response_data",
+ "UpdateAgentAgentsResponse": ".update_agent_agents_response",
+ "UpdateAgentWidgetConfigRequestWidgetConfig": ".update_agent_widget_config_request_widget_config",
+ "UpdateAgentWidgetConfigRequestWidgetConfigMode": ".update_agent_widget_config_request_widget_config_mode",
+ "UpdateAgentWidgetConfigRequestWidgetConfigPosition": ".update_agent_widget_config_request_widget_config_position",
+ "UpdateAgentWidgetConfigRequestWidgetConfigSize": ".update_agent_widget_config_request_widget_config_size",
+ "UpdateAgentWidgetConfigRequestWidgetConfigTheme": ".update_agent_widget_config_request_widget_config_theme",
+ "UpdateAgentWidgetConfigResponse": ".update_agent_widget_config_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ArchiveAgentAgentsResponse",
+ "ArchiveAgentAgentsResponseData",
+ "ArchiveAgentAgentsResponseDataMessage",
+ "CreateAgentAgentsResponse",
+ "CreateAgentRequestBackgroundSound",
+ "CreateAgentRequestDenoisingConfig",
+ "CreateAgentRequestLanguage",
+ "CreateAgentRequestLanguageDefault",
+ "CreateAgentRequestLanguageSupportedItem",
+ "CreateAgentRequestLanguageSwitching",
+ "CreateAgentRequestLlmIdleTimeoutConfig",
+ "CreateAgentRequestPreCallApi",
+ "CreateAgentRequestPreCallApiMethod",
+ "CreateAgentRequestPreCallApiResponseVariablesItem",
+ "CreateAgentRequestPronunciationDictsItem",
+ "CreateAgentRequestRedactionConfig",
+ "CreateAgentRequestSessionTimeoutConfig",
+ "CreateAgentRequestSlmModel",
+ "CreateAgentRequestSmartTurnConfig",
+ "CreateAgentRequestSynthesizer",
+ "CreateAgentRequestSynthesizerVoiceConfig",
+ "CreateAgentRequestSynthesizerVoiceConfigGender",
+ "CreateAgentRequestSynthesizerVoiceConfigModel",
+ "CreateAgentRequestTimezone",
+ "CreateAgentRequestVoiceDetectionConfig",
+ "CreateAgentRequestVoiceMailDetectionConfig",
+ "DuplicateAgentAgentsResponse",
+ "DuplicateAgentAgentsResponseData",
+ "GetAgentAgentsResponse",
+ "GetAgentAvatarPresignedUrlResponse",
+ "GetAgentAvatarPresignedUrlResponseData",
+ "GetAgentCallLogsResponse",
+ "GetAgentCallLogsResponseData",
+ "GetAgentCallLogsResponseDataCallLogsItem",
+ "GetAgentWidgetConfigResponse",
+ "GetAgentWidgetConfigResponseData",
+ "GetAgentWidgetConfigResponseDataMode",
+ "GetAgentWidgetConfigResponseDataPosition",
+ "GetAgentWidgetConfigResponseDataSize",
+ "GetAgentWidgetConfigResponseDataTheme",
+ "ListAgentsAgentsRequestSortField",
+ "ListAgentsAgentsRequestSortOrder",
+ "ListAgentsAgentsRequestType",
+ "ListAgentsAgentsResponse",
+ "ListAgentsAgentsResponseData",
+ "UpdateAgentAgentsResponse",
+ "UpdateAgentWidgetConfigRequestWidgetConfig",
+ "UpdateAgentWidgetConfigRequestWidgetConfigMode",
+ "UpdateAgentWidgetConfigRequestWidgetConfigPosition",
+ "UpdateAgentWidgetConfigRequestWidgetConfigSize",
+ "UpdateAgentWidgetConfigRequestWidgetConfigTheme",
+ "UpdateAgentWidgetConfigResponse",
+]
diff --git a/src/smallestai/atoms/agents/types/create_agent_request_synthesizer.py b/src/smallestai/atoms/agents/types/create_agent_request_synthesizer.py
index bb9a6775..29f76b88 100644
--- a/src/smallestai/atoms/agents/types/create_agent_request_synthesizer.py
+++ b/src/smallestai/atoms/agents/types/create_agent_request_synthesizer.py
@@ -12,10 +12,11 @@
class CreateAgentRequestSynthesizer(UncheckedBaseModel):
"""
- Synthesizer (TTS) configuration for the agent.
- Models `waves`, `waves_lightning_large`, `waves_lightning_v2`, and `waves_lightning_v3_1`
- validate `voiceId` against the Waves API. All other models accept any voiceId.
- Cloned voices are regular voiceIds โ use them with any compatible Waves model.
+ Synthesizer (TTS) configuration for the agent. Model
+ `waves_lightning_v3_1` validates `voiceId` against the Waves
+ API. `gpt-realtime` and `gpt-realtime-mini` accept any voiceId.
+ Cloned voices are regular voiceIds. Use them with a compatible
+ Waves model.
"""
voice_config: typing_extensions.Annotated[
diff --git a/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config.py b/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config.py
index 1f01e59a..d13ce57e 100644
--- a/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config.py
+++ b/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config.py
@@ -22,11 +22,6 @@ class CreateAgentRequestSynthesizerVoiceConfig(UncheckedBaseModel):
recommended Waves voice path (default), or `gpt-realtime` /
`gpt-realtime-mini` for OpenAI realtime models (require
`workflowType: single_prompt`).
-
- The other tokens (`waves`, `waves_lightning_large`,
- `waves_lightning_v2`, `waves_lightning_v3`) are kept for
- backwards compatibility with existing agents and should
- not be used for new agents.
"""
voice_id: typing_extensions.Annotated[
diff --git a/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config_model.py b/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config_model.py
index a92af7d1..aa73151a 100644
--- a/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config_model.py
+++ b/src/smallestai/atoms/agents/types/create_agent_request_synthesizer_voice_config_model.py
@@ -3,14 +3,5 @@
import typing
CreateAgentRequestSynthesizerVoiceConfigModel = typing.Union[
- typing.Literal[
- "waves_lightning_v3_1",
- "gpt-realtime",
- "gpt-realtime-mini",
- "waves",
- "waves_lightning_large",
- "waves_lightning_v2",
- "waves_lightning_v3",
- ],
- typing.Any,
+ typing.Literal["waves_lightning_v3_1", "gpt-realtime", "gpt-realtime-mini"], typing.Any
]
diff --git a/src/smallestai/atoms/agents/types/create_with_ai_agents_request_type.py b/src/smallestai/atoms/agents/types/create_with_ai_agents_request_type.py
deleted file mode 100644
index 4bd70f4c..00000000
--- a/src/smallestai/atoms/agents/types/create_with_ai_agents_request_type.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-CreateWithAiAgentsRequestType = typing.Union[typing.Literal["single_prompt"], typing.Any]
diff --git a/src/smallestai/atoms/agents/types/create_with_ai_agents_request_voice_model.py b/src/smallestai/atoms/agents/types/create_with_ai_agents_request_voice_model.py
deleted file mode 100644
index 1c652e07..00000000
--- a/src/smallestai/atoms/agents/types/create_with_ai_agents_request_voice_model.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-CreateWithAiAgentsRequestVoiceModel = typing.Union[
- typing.Literal[
- "waves",
- "waves_lightning_large",
- "waves_lightning_large_voice_clone",
- "waves_lightning_v2",
- "waves_lightning_v3",
- "waves_lightning_v3_1",
- "gpt-realtime",
- "gpt-realtime-mini",
- "other",
- ],
- typing.Any,
-]
diff --git a/src/smallestai/atoms/agents/types/get_agent_avatar_presigned_url_response.py b/src/smallestai/atoms/agents/types/get_agent_avatar_presigned_url_response.py
new file mode 100644
index 00000000..152983c9
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/get_agent_avatar_presigned_url_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_agent_avatar_presigned_url_response_data import GetAgentAvatarPresignedUrlResponseData
+
+
+class GetAgentAvatarPresignedUrlResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetAgentAvatarPresignedUrlResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/post_campaign_id_start_response_data.py b/src/smallestai/atoms/agents/types/get_agent_avatar_presigned_url_response_data.py
similarity index 54%
rename from src/smallestai/atoms/campaigns/types/post_campaign_id_start_response_data.py
rename to src/smallestai/atoms/agents/types/get_agent_avatar_presigned_url_response_data.py
index 0c19253f..4a4ca9c5 100644
--- a/src/smallestai/atoms/campaigns/types/post_campaign_id_start_response_data.py
+++ b/src/smallestai/atoms/agents/types/get_agent_avatar_presigned_url_response_data.py
@@ -9,18 +9,21 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class PostCampaignIdStartResponseData(UncheckedBaseModel):
- message: typing.Optional[str] = None
- task_id: typing_extensions.Annotated[
+class GetAgentAvatarPresignedUrlResponseData(UncheckedBaseModel):
+ presigned_url: typing_extensions.Annotated[
typing.Optional[str],
- FieldMetadata(alias="taskId"),
- pydantic.Field(alias="taskId", description="Internal task identifier for the queued job"),
+ FieldMetadata(alias="presignedUrl"),
+ pydantic.Field(alias="presignedUrl", description="S3 pre-signed PUT URL (expires in 300 s)"),
] = None
- campaign_id: typing_extensions.Annotated[
+ cdn_url: typing_extensions.Annotated[
typing.Optional[str],
- FieldMetadata(alias="campaignId"),
- pydantic.Field(alias="campaignId", description="The ID of the campaign"),
+ FieldMetadata(alias="cdnUrl"),
+ pydantic.Field(alias="cdnUrl", description="Final public CDN URL to save on the agent after upload"),
] = None
+ key: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ S3 object key
+ """
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agents/types/get_agent_call_logs_response.py b/src/smallestai/atoms/agents/types/get_agent_call_logs_response.py
new file mode 100644
index 00000000..687bb25b
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/get_agent_call_logs_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_agent_call_logs_response_data import GetAgentCallLogsResponseData
+
+
+class GetAgentCallLogsResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetAgentCallLogsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/list_call_logs_agents_response_data.py b/src/smallestai/atoms/agents/types/get_agent_call_logs_response_data.py
similarity index 59%
rename from src/smallestai/atoms/agents/types/list_call_logs_agents_response_data.py
rename to src/smallestai/atoms/agents/types/get_agent_call_logs_response_data.py
index 3dfe8b2b..7094ba84 100644
--- a/src/smallestai/atoms/agents/types/list_call_logs_agents_response_data.py
+++ b/src/smallestai/atoms/agents/types/get_agent_call_logs_response_data.py
@@ -7,17 +7,20 @@
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.serialization import FieldMetadata
from ....core.unchecked_base_model import UncheckedBaseModel
-from .list_call_logs_agents_response_data_call_logs_item import ListCallLogsAgentsResponseDataCallLogsItem
-from .list_call_logs_agents_response_data_pagination import ListCallLogsAgentsResponseDataPagination
+from .get_agent_call_logs_response_data_call_logs_item import GetAgentCallLogsResponseDataCallLogsItem
-class ListCallLogsAgentsResponseData(UncheckedBaseModel):
+class GetAgentCallLogsResponseData(UncheckedBaseModel):
call_logs: typing_extensions.Annotated[
- typing.Optional[typing.List[ListCallLogsAgentsResponseDataCallLogsItem]],
+ typing.Optional[typing.List[GetAgentCallLogsResponseDataCallLogsItem]],
FieldMetadata(alias="callLogs"),
pydantic.Field(alias="callLogs"),
] = None
- pagination: typing.Optional[ListCallLogsAgentsResponseDataPagination] = None
+ total_count: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCount"), pydantic.Field(alias="totalCount")
+ ] = None
+ page: typing.Optional[float] = None
+ offset: typing.Optional[float] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/agents/types/list_call_logs_agents_response_data_call_logs_item.py b/src/smallestai/atoms/agents/types/get_agent_call_logs_response_data_call_logs_item.py
similarity index 61%
rename from src/smallestai/atoms/agents/types/list_call_logs_agents_response_data_call_logs_item.py
rename to src/smallestai/atoms/agents/types/get_agent_call_logs_response_data_call_logs_item.py
index 22d2dd6f..9a5538ad 100644
--- a/src/smallestai/atoms/agents/types/list_call_logs_agents_response_data_call_logs_item.py
+++ b/src/smallestai/atoms/agents/types/get_agent_call_logs_response_data_call_logs_item.py
@@ -10,44 +10,29 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class ListCallLogsAgentsResponseDataCallLogsItem(UncheckedBaseModel):
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime], FieldMetadata(alias="createdAt"), pydantic.Field(alias="createdAt")
- ] = None
+class GetAgentCallLogsResponseDataCallLogsItem(UncheckedBaseModel):
call_id: typing_extensions.Annotated[
typing.Optional[str], FieldMetadata(alias="callId"), pydantic.Field(alias="callId")
] = None
- call_duration: typing_extensions.Annotated[
- typing.Optional[float], FieldMetadata(alias="callDuration"), pydantic.Field(alias="callDuration")
- ] = None
- recording_url: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="recordingUrl"), pydantic.Field(alias="recordingUrl")
- ] = None
call_status: typing_extensions.Annotated[
typing.Optional[str], FieldMetadata(alias="callStatus"), pydantic.Field(alias="callStatus")
] = None
call_type: typing_extensions.Annotated[
typing.Optional[str], FieldMetadata(alias="callType"), pydantic.Field(alias="callType")
] = None
- call_cost: typing_extensions.Annotated[
- typing.Optional[float], FieldMetadata(alias="callCost"), pydantic.Field(alias="callCost")
- ] = None
- status_details: typing_extensions.Annotated[
- typing.Optional[typing.List[str]], FieldMetadata(alias="statusDetails"), pydantic.Field(alias="statusDetails")
- ] = None
from_number: typing_extensions.Annotated[
typing.Optional[str], FieldMetadata(alias="fromNumber"), pydantic.Field(alias="fromNumber")
] = None
to_number: typing_extensions.Annotated[
typing.Optional[str], FieldMetadata(alias="toNumber"), pydantic.Field(alias="toNumber")
] = None
- plivo_data: typing_extensions.Annotated[
- typing.Optional[typing.Dict[str, typing.Any]],
- FieldMetadata(alias="plivoData"),
- pydantic.Field(alias="plivoData"),
+ created_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="createdAt"), pydantic.Field(alias="createdAt")
+ ] = None
+ call_duration: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="callDuration"),
+ pydantic.Field(alias="callDuration", description="Call duration in milliseconds"),
] = None
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data.py b/src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data.py
deleted file mode 100644
index 7a28660c..00000000
--- a/src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.tool import Tool
-from ...types.workflow_type import WorkflowType
-from .get_agent_id_workflow_response_data_data import GetAgentIdWorkflowResponseDataData
-
-
-class GetAgentIdWorkflowResponseData(UncheckedBaseModel):
- """
- The active version's workflow.
- """
-
- prompt: typing.Optional[str] = pydantic.Field(default=None)
- """
- Active prompt (single-prompt agents only).
- """
-
- tools: typing.Optional[typing.List[Tool]] = pydantic.Field(default=None)
- """
- Active tool list (single-prompt agents only).
- """
-
- type: typing.Optional[WorkflowType] = pydantic.Field(default=None)
- """
- Workflow type. Present for workflow_graph agents.
- """
-
- data: typing.Optional[GetAgentIdWorkflowResponseDataData] = pydantic.Field(default=None)
- """
- Graph data for workflow_graph agents.
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data_data.py b/src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data_data.py
deleted file mode 100644
index b5ab24ca..00000000
--- a/src/smallestai/atoms/agents/types/get_agent_id_workflow_response_data_data.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-from ...types.single_prompt_data import SinglePromptData
-from ...types.workflow_graph_data import WorkflowGraphData
-
-GetAgentIdWorkflowResponseDataData = typing.Union[WorkflowGraphData, SinglePromptData]
diff --git a/src/smallestai/atoms/agents/types/get_agent_id_workflow_response.py b/src/smallestai/atoms/agents/types/get_agent_widget_config_response.py
similarity index 61%
rename from src/smallestai/atoms/agents/types/get_agent_id_workflow_response.py
rename to src/smallestai/atoms/agents/types/get_agent_widget_config_response.py
index bd5ebf9c..41bdfc47 100644
--- a/src/smallestai/atoms/agents/types/get_agent_id_workflow_response.py
+++ b/src/smallestai/atoms/agents/types/get_agent_widget_config_response.py
@@ -5,14 +5,14 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_agent_id_workflow_response_data import GetAgentIdWorkflowResponseData
+from .get_agent_widget_config_response_data import GetAgentWidgetConfigResponseData
-class GetAgentIdWorkflowResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[GetAgentIdWorkflowResponseData] = pydantic.Field(default=None)
+class GetAgentWidgetConfigResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetAgentWidgetConfigResponseData] = pydantic.Field(default=None)
"""
- The active version's workflow.
+ All fields may be absent if not yet configured
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data.py b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data.py
new file mode 100644
index 00000000..ab02c43f
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data.py
@@ -0,0 +1,65 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_agent_widget_config_response_data_mode import GetAgentWidgetConfigResponseDataMode
+from .get_agent_widget_config_response_data_position import GetAgentWidgetConfigResponseDataPosition
+from .get_agent_widget_config_response_data_size import GetAgentWidgetConfigResponseDataSize
+from .get_agent_widget_config_response_data_theme import GetAgentWidgetConfigResponseDataTheme
+
+
+class GetAgentWidgetConfigResponseData(UncheckedBaseModel):
+ """
+ All fields may be absent if not yet configured
+ """
+
+ assistant_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="assistantId"),
+ pydantic.Field(
+ alias="assistantId", description="Same as the agent ID โ convenience field for the widget embed code"
+ ),
+ ] = None
+ position: typing.Optional[GetAgentWidgetConfigResponseDataPosition] = None
+ size: typing.Optional[GetAgentWidgetConfigResponseDataSize] = None
+ border_radius: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="borderRadius"), pydantic.Field(alias="borderRadius")
+ ] = None
+ mode: typing.Optional[GetAgentWidgetConfigResponseDataMode] = None
+ theme: typing.Optional[GetAgentWidgetConfigResponseDataTheme] = None
+ base_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="baseColor"), pydantic.Field(alias="baseColor")
+ ] = None
+ accent_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="accentColor"), pydantic.Field(alias="accentColor")
+ ] = None
+ title: typing.Optional[str] = None
+ start_button_text: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="startButtonText"), pydantic.Field(alias="startButtonText")
+ ] = None
+ end_button_text: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="endButtonText"), pydantic.Field(alias="endButtonText")
+ ] = None
+ avatar_url: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="avatarUrl"), pydantic.Field(alias="avatarUrl")
+ ] = None
+ voice_show_transcript: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="voiceShowTranscript"), pydantic.Field(alias="voiceShowTranscript")
+ ] = None
+ consent_required: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="consentRequired"), pydantic.Field(alias="consentRequired")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_mode.py b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_mode.py
new file mode 100644
index 00000000..30ed5356
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_mode.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+GetAgentWidgetConfigResponseDataMode = typing.Union[typing.Literal["chat", "voice"], typing.Any]
diff --git a/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_position.py b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_position.py
new file mode 100644
index 00000000..5e3d93c7
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_position.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+GetAgentWidgetConfigResponseDataPosition = typing.Union[
+ typing.Literal["bottom-right", "bottom-left", "top-right", "top-left"], typing.Any
+]
diff --git a/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_size.py b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_size.py
new file mode 100644
index 00000000..93d139f7
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_size.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+GetAgentWidgetConfigResponseDataSize = typing.Union[typing.Literal["tiny", "compact", "full"], typing.Any]
diff --git a/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_theme.py b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_theme.py
new file mode 100644
index 00000000..1042188f
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/get_agent_widget_config_response_data_theme.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+GetAgentWidgetConfigResponseDataTheme = typing.Union[typing.Literal["light", "dark"], typing.Any]
diff --git a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data.py b/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data.py
deleted file mode 100644
index 61ec921f..00000000
--- a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_prompt_config_agents_response_data_questions_item import GetPromptConfigAgentsResponseDataQuestionsItem
-
-
-class GetPromptConfigAgentsResponseData(UncheckedBaseModel):
- questions: typing.Optional[typing.List[GetPromptConfigAgentsResponseDataQuestionsItem]] = None
- example_labels: typing_extensions.Annotated[
- typing.Optional[typing.List[str]], FieldMetadata(alias="exampleLabels"), pydantic.Field(alias="exampleLabels")
- ] = None
- default_label: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="defaultLabel"), pydantic.Field(alias="defaultLabel")
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item.py b/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item.py
deleted file mode 100644
index fb72e51c..00000000
--- a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_prompt_config_agents_response_data_questions_item_options_item import (
- GetPromptConfigAgentsResponseDataQuestionsItemOptionsItem,
-)
-from .get_prompt_config_agents_response_data_questions_item_type import (
- GetPromptConfigAgentsResponseDataQuestionsItemType,
-)
-
-
-class GetPromptConfigAgentsResponseDataQuestionsItem(UncheckedBaseModel):
- text: typing.Optional[str] = None
- type: typing.Optional[GetPromptConfigAgentsResponseDataQuestionsItemType] = None
- options: typing.Optional[typing.List[GetPromptConfigAgentsResponseDataQuestionsItemOptionsItem]] = None
- examples: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None)
- """
- Map of `` โ answer text. Always includes a `default` key.
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item_type.py b/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item_type.py
deleted file mode 100644
index c52a6258..00000000
--- a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item_type.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetPromptConfigAgentsResponseDataQuestionsItemType = typing.Union[
- typing.Literal["textarea", "select", "multiselect"], typing.Any
-]
diff --git a/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config.py b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config.py
new file mode 100644
index 00000000..a2c5748d
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config.py
@@ -0,0 +1,119 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .update_agent_widget_config_request_widget_config_mode import UpdateAgentWidgetConfigRequestWidgetConfigMode
+from .update_agent_widget_config_request_widget_config_position import (
+ UpdateAgentWidgetConfigRequestWidgetConfigPosition,
+)
+from .update_agent_widget_config_request_widget_config_size import UpdateAgentWidgetConfigRequestWidgetConfigSize
+from .update_agent_widget_config_request_widget_config_theme import UpdateAgentWidgetConfigRequestWidgetConfigTheme
+
+
+class UpdateAgentWidgetConfigRequestWidgetConfig(UncheckedBaseModel):
+ """
+ All fields are optional โ only provided fields are updated
+ """
+
+ position: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfigPosition] = None
+ size: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfigSize] = None
+ border_radius: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="borderRadius"), pydantic.Field(alias="borderRadius")
+ ] = None
+ mode: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfigMode] = None
+ theme: typing.Optional[UpdateAgentWidgetConfigRequestWidgetConfigTheme] = None
+ base_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="baseColor"), pydantic.Field(alias="baseColor")
+ ] = None
+ accent_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="accentColor"), pydantic.Field(alias="accentColor")
+ ] = None
+ agent_bubble_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="agentBubbleColor"), pydantic.Field(alias="agentBubbleColor")
+ ] = None
+ text_on_accent_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="textOnAccentColor"), pydantic.Field(alias="textOnAccentColor")
+ ] = None
+ secondary_text_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="secondaryTextColor"), pydantic.Field(alias="secondaryTextColor")
+ ] = None
+ primary_text_color: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="primaryTextColor"), pydantic.Field(alias="primaryTextColor")
+ ] = None
+ title: typing.Optional[str] = None
+ start_button_text: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="startButtonText"), pydantic.Field(alias="startButtonText")
+ ] = None
+ end_button_text: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="endButtonText"), pydantic.Field(alias="endButtonText")
+ ] = None
+ cta_title: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="ctaTitle"), pydantic.Field(alias="ctaTitle")
+ ] = None
+ cta_subtitle: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="ctaSubtitle"), pydantic.Field(alias="ctaSubtitle")
+ ] = None
+ cta_name: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="ctaName"), pydantic.Field(alias="ctaName")
+ ] = None
+ widget_name: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="widgetName"), pydantic.Field(alias="widgetName")
+ ] = None
+ avatar_url: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="avatarUrl"),
+ pydantic.Field(
+ alias="avatarUrl",
+ description="Must be a platform CDN URL. Use `POST /agent/{id}/avatar/presigned-url` to obtain one.",
+ ),
+ ] = None
+ voice_empty_message: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="voiceEmptyMessage"), pydantic.Field(alias="voiceEmptyMessage")
+ ] = None
+ voice_active_empty_message: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="voiceActiveEmptyMessage"),
+ pydantic.Field(alias="voiceActiveEmptyMessage"),
+ ] = None
+ chat_empty_message: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="chatEmptyMessage"), pydantic.Field(alias="chatEmptyMessage")
+ ] = None
+ chat_first_message: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="chatFirstMessage"), pydantic.Field(alias="chatFirstMessage")
+ ] = None
+ chat_placeholder: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="chatPlaceholder"), pydantic.Field(alias="chatPlaceholder")
+ ] = None
+ voice_show_transcript: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="voiceShowTranscript"), pydantic.Field(alias="voiceShowTranscript")
+ ] = None
+ consent_required: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="consentRequired"), pydantic.Field(alias="consentRequired")
+ ] = None
+ consent_title: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="consentTitle"), pydantic.Field(alias="consentTitle")
+ ] = None
+ consent_content: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="consentContent"), pydantic.Field(alias="consentContent")
+ ] = None
+ consent_storage_key: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="consentStorageKey"), pydantic.Field(alias="consentStorageKey")
+ ] = None
+ public_key: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="publicKey"), pydantic.Field(alias="publicKey")
+ ] = None
+ allowlist: typing.Optional[typing.List[str]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_mode.py b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_mode.py
new file mode 100644
index 00000000..80f8711b
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_mode.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateAgentWidgetConfigRequestWidgetConfigMode = typing.Union[typing.Literal["chat", "voice"], typing.Any]
diff --git a/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_position.py b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_position.py
new file mode 100644
index 00000000..6034eee8
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_position.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateAgentWidgetConfigRequestWidgetConfigPosition = typing.Union[
+ typing.Literal["bottom-right", "bottom-left", "top-right", "top-left"], typing.Any
+]
diff --git a/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_size.py b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_size.py
new file mode 100644
index 00000000..6b248177
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_size.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateAgentWidgetConfigRequestWidgetConfigSize = typing.Union[typing.Literal["tiny", "compact", "full"], typing.Any]
diff --git a/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_theme.py b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_theme.py
new file mode 100644
index 00000000..4111c06c
--- /dev/null
+++ b/src/smallestai/atoms/agents/types/update_agent_widget_config_request_widget_config_theme.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateAgentWidgetConfigRequestWidgetConfigTheme = typing.Union[typing.Literal["light", "dark"], typing.Any]
diff --git a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_id_scrape_urls_response.py b/src/smallestai/atoms/agents/types/update_agent_widget_config_response.py
similarity index 76%
rename from src/smallestai/atoms/knowledge_base/types/post_knowledgebase_id_scrape_urls_response.py
rename to src/smallestai/atoms/agents/types/update_agent_widget_config_response.py
index 398fb4b6..4cd57060 100644
--- a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_id_scrape_urls_response.py
+++ b/src/smallestai/atoms/agents/types/update_agent_widget_config_response.py
@@ -7,11 +7,11 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class PostKnowledgebaseIdScrapeUrlsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
+class UpdateAgentWidgetConfigResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
data: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None)
"""
- Async job acknowledgement. Inspect `/scraped-urls` for per-URL progress.
+ The updated widgetConfig object
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/agents/types/update_widget_config_agents_response.py b/src/smallestai/atoms/agents/types/update_widget_config_agents_response.py
deleted file mode 100644
index 3c8b5b3a..00000000
--- a/src/smallestai/atoms/agents/types/update_widget_config_agents_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.widget_config import WidgetConfig
-
-
-class UpdateWidgetConfigAgentsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[WidgetConfig] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/update_workflow_configuration_agents_request_workflow_graph.py b/src/smallestai/atoms/agents/types/update_workflow_configuration_agents_request_workflow_graph.py
deleted file mode 100644
index c2616d1e..00000000
--- a/src/smallestai/atoms/agents/types/update_workflow_configuration_agents_request_workflow_graph.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class UpdateWorkflowConfigurationAgentsRequestWorkflowGraph(UncheckedBaseModel):
- """
- Required when `type = workflow_graph`. Exactly one of `workflowGraph` or `singlePromptConfig` must be provided.
- """
-
- nodes: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = None
- edges: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/__init__.py b/src/smallestai/atoms/analytics/__init__.py
new file mode 100644
index 00000000..a37cca1e
--- /dev/null
+++ b/src/smallestai/atoms/analytics/__init__.py
@@ -0,0 +1,208 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ GetAgentPerformanceRequestSortOrder,
+ GetAgentPerformanceResponse,
+ GetAgentPerformanceResponseData,
+ GetAgentPerformanceResponseDataAgentsItem,
+ GetAnalyticsConcurrencyRequestIncludeAgents,
+ GetAnalyticsConcurrencyResponse,
+ GetAnalyticsConcurrencyResponseData,
+ GetAnalyticsConcurrencyResponseDataByAgentItem,
+ GetAnalyticsConcurrencyResponseDataDataPointsItem,
+ GetAnalyticsSummaryResponse,
+ GetAnalyticsSummaryResponseData,
+ GetAttemptCohortResponse,
+ GetAttemptCohortResponseData,
+ GetAttemptCohortResponseDataCohortsItem,
+ GetCallCountsByDayResponse,
+ GetCallCountsByDayResponseData,
+ GetCallCountsByDayResponseDataCallCountsByDayItem,
+ GetCallCountsLogResponse,
+ GetCallCountsLogResponseData,
+ GetCallCountsLogResponseDataCallsItem,
+ GetCallOutcomesTimeseriesResponse,
+ GetCallOutcomesTimeseriesResponseData,
+ GetCallOutcomesTimeseriesResponseDataDataPointsItem,
+ GetCallOutcomesTimeseriesResponseDataTotals,
+ GetCallStartDistributionResponse,
+ GetCallStartDistributionResponseData,
+ GetCallStartDistributionResponseDataDataPointsItem,
+ GetCallVolumeTimeseriesResponse,
+ GetCallVolumeTimeseriesResponseData,
+ GetCallVolumeTimeseriesResponseDataDataPointsItem,
+ GetConversationDetailsResponse,
+ GetConversationDetailsResponseData,
+ GetConversationDetailsResponseDataEventsItem,
+ GetConversationDetailsResponseDataTranscriptItem,
+ GetDailyCallSummaryResponse,
+ GetDailyCallSummaryResponseData,
+ GetDashboardResponse,
+ GetDashboardResponseData,
+ GetDurationStatsResponse,
+ GetDurationStatsResponseData,
+ GetHourlyPerformanceResponse,
+ GetHourlyPerformanceResponseData,
+ GetHourlyPerformanceResponseDataHoursItem,
+ GetPhoneNumberTrendsResponse,
+ GetPhoneNumberTrendsResponseData,
+ GetPhoneNumberTrendsResponseDataTrendsItem,
+ GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem,
+ GetPickupRateByNumberResponse,
+ GetPickupRateByNumberResponseData,
+ GetPickupRateByNumberResponseDataNumbersItem,
+ GetUsageTimeseriesResponse,
+ GetUsageTimeseriesResponseData,
+ GetUsageTimeseriesResponseDataDateRange,
+ GetUsageTimeseriesResponseDataDayWiseCreditsItem,
+ GetWeeklyTrendsResponse,
+ GetWeeklyTrendsResponseData,
+ GetWeeklyTrendsResponseDataWeeksItem,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetAgentPerformanceRequestSortOrder": ".types",
+ "GetAgentPerformanceResponse": ".types",
+ "GetAgentPerformanceResponseData": ".types",
+ "GetAgentPerformanceResponseDataAgentsItem": ".types",
+ "GetAnalyticsConcurrencyRequestIncludeAgents": ".types",
+ "GetAnalyticsConcurrencyResponse": ".types",
+ "GetAnalyticsConcurrencyResponseData": ".types",
+ "GetAnalyticsConcurrencyResponseDataByAgentItem": ".types",
+ "GetAnalyticsConcurrencyResponseDataDataPointsItem": ".types",
+ "GetAnalyticsSummaryResponse": ".types",
+ "GetAnalyticsSummaryResponseData": ".types",
+ "GetAttemptCohortResponse": ".types",
+ "GetAttemptCohortResponseData": ".types",
+ "GetAttemptCohortResponseDataCohortsItem": ".types",
+ "GetCallCountsByDayResponse": ".types",
+ "GetCallCountsByDayResponseData": ".types",
+ "GetCallCountsByDayResponseDataCallCountsByDayItem": ".types",
+ "GetCallCountsLogResponse": ".types",
+ "GetCallCountsLogResponseData": ".types",
+ "GetCallCountsLogResponseDataCallsItem": ".types",
+ "GetCallOutcomesTimeseriesResponse": ".types",
+ "GetCallOutcomesTimeseriesResponseData": ".types",
+ "GetCallOutcomesTimeseriesResponseDataDataPointsItem": ".types",
+ "GetCallOutcomesTimeseriesResponseDataTotals": ".types",
+ "GetCallStartDistributionResponse": ".types",
+ "GetCallStartDistributionResponseData": ".types",
+ "GetCallStartDistributionResponseDataDataPointsItem": ".types",
+ "GetCallVolumeTimeseriesResponse": ".types",
+ "GetCallVolumeTimeseriesResponseData": ".types",
+ "GetCallVolumeTimeseriesResponseDataDataPointsItem": ".types",
+ "GetConversationDetailsResponse": ".types",
+ "GetConversationDetailsResponseData": ".types",
+ "GetConversationDetailsResponseDataEventsItem": ".types",
+ "GetConversationDetailsResponseDataTranscriptItem": ".types",
+ "GetDailyCallSummaryResponse": ".types",
+ "GetDailyCallSummaryResponseData": ".types",
+ "GetDashboardResponse": ".types",
+ "GetDashboardResponseData": ".types",
+ "GetDurationStatsResponse": ".types",
+ "GetDurationStatsResponseData": ".types",
+ "GetHourlyPerformanceResponse": ".types",
+ "GetHourlyPerformanceResponseData": ".types",
+ "GetHourlyPerformanceResponseDataHoursItem": ".types",
+ "GetPhoneNumberTrendsResponse": ".types",
+ "GetPhoneNumberTrendsResponseData": ".types",
+ "GetPhoneNumberTrendsResponseDataTrendsItem": ".types",
+ "GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem": ".types",
+ "GetPickupRateByNumberResponse": ".types",
+ "GetPickupRateByNumberResponseData": ".types",
+ "GetPickupRateByNumberResponseDataNumbersItem": ".types",
+ "GetUsageTimeseriesResponse": ".types",
+ "GetUsageTimeseriesResponseData": ".types",
+ "GetUsageTimeseriesResponseDataDateRange": ".types",
+ "GetUsageTimeseriesResponseDataDayWiseCreditsItem": ".types",
+ "GetWeeklyTrendsResponse": ".types",
+ "GetWeeklyTrendsResponseData": ".types",
+ "GetWeeklyTrendsResponseDataWeeksItem": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetAgentPerformanceRequestSortOrder",
+ "GetAgentPerformanceResponse",
+ "GetAgentPerformanceResponseData",
+ "GetAgentPerformanceResponseDataAgentsItem",
+ "GetAnalyticsConcurrencyRequestIncludeAgents",
+ "GetAnalyticsConcurrencyResponse",
+ "GetAnalyticsConcurrencyResponseData",
+ "GetAnalyticsConcurrencyResponseDataByAgentItem",
+ "GetAnalyticsConcurrencyResponseDataDataPointsItem",
+ "GetAnalyticsSummaryResponse",
+ "GetAnalyticsSummaryResponseData",
+ "GetAttemptCohortResponse",
+ "GetAttemptCohortResponseData",
+ "GetAttemptCohortResponseDataCohortsItem",
+ "GetCallCountsByDayResponse",
+ "GetCallCountsByDayResponseData",
+ "GetCallCountsByDayResponseDataCallCountsByDayItem",
+ "GetCallCountsLogResponse",
+ "GetCallCountsLogResponseData",
+ "GetCallCountsLogResponseDataCallsItem",
+ "GetCallOutcomesTimeseriesResponse",
+ "GetCallOutcomesTimeseriesResponseData",
+ "GetCallOutcomesTimeseriesResponseDataDataPointsItem",
+ "GetCallOutcomesTimeseriesResponseDataTotals",
+ "GetCallStartDistributionResponse",
+ "GetCallStartDistributionResponseData",
+ "GetCallStartDistributionResponseDataDataPointsItem",
+ "GetCallVolumeTimeseriesResponse",
+ "GetCallVolumeTimeseriesResponseData",
+ "GetCallVolumeTimeseriesResponseDataDataPointsItem",
+ "GetConversationDetailsResponse",
+ "GetConversationDetailsResponseData",
+ "GetConversationDetailsResponseDataEventsItem",
+ "GetConversationDetailsResponseDataTranscriptItem",
+ "GetDailyCallSummaryResponse",
+ "GetDailyCallSummaryResponseData",
+ "GetDashboardResponse",
+ "GetDashboardResponseData",
+ "GetDurationStatsResponse",
+ "GetDurationStatsResponseData",
+ "GetHourlyPerformanceResponse",
+ "GetHourlyPerformanceResponseData",
+ "GetHourlyPerformanceResponseDataHoursItem",
+ "GetPhoneNumberTrendsResponse",
+ "GetPhoneNumberTrendsResponseData",
+ "GetPhoneNumberTrendsResponseDataTrendsItem",
+ "GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem",
+ "GetPickupRateByNumberResponse",
+ "GetPickupRateByNumberResponseData",
+ "GetPickupRateByNumberResponseDataNumbersItem",
+ "GetUsageTimeseriesResponse",
+ "GetUsageTimeseriesResponseData",
+ "GetUsageTimeseriesResponseDataDateRange",
+ "GetUsageTimeseriesResponseDataDayWiseCreditsItem",
+ "GetWeeklyTrendsResponse",
+ "GetWeeklyTrendsResponseData",
+ "GetWeeklyTrendsResponseDataWeeksItem",
+]
diff --git a/src/smallestai/atoms/analytics/client.py b/src/smallestai/atoms/analytics/client.py
new file mode 100644
index 00000000..4a38ce6e
--- /dev/null
+++ b/src/smallestai/atoms/analytics/client.py
@@ -0,0 +1,2163 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from .raw_client import AsyncRawAnalyticsClient, RawAnalyticsClient
+from .types.get_agent_performance_request_sort_order import GetAgentPerformanceRequestSortOrder
+from .types.get_agent_performance_response import GetAgentPerformanceResponse
+from .types.get_analytics_concurrency_request_include_agents import GetAnalyticsConcurrencyRequestIncludeAgents
+from .types.get_analytics_concurrency_response import GetAnalyticsConcurrencyResponse
+from .types.get_analytics_summary_response import GetAnalyticsSummaryResponse
+from .types.get_attempt_cohort_response import GetAttemptCohortResponse
+from .types.get_call_counts_by_day_response import GetCallCountsByDayResponse
+from .types.get_call_counts_log_response import GetCallCountsLogResponse
+from .types.get_call_outcomes_timeseries_response import GetCallOutcomesTimeseriesResponse
+from .types.get_call_start_distribution_response import GetCallStartDistributionResponse
+from .types.get_call_volume_timeseries_response import GetCallVolumeTimeseriesResponse
+from .types.get_conversation_details_response import GetConversationDetailsResponse
+from .types.get_daily_call_summary_response import GetDailyCallSummaryResponse
+from .types.get_dashboard_response import GetDashboardResponse
+from .types.get_duration_stats_response import GetDurationStatsResponse
+from .types.get_hourly_performance_response import GetHourlyPerformanceResponse
+from .types.get_phone_number_trends_response import GetPhoneNumberTrendsResponse
+from .types.get_pickup_rate_by_number_response import GetPickupRateByNumberResponse
+from .types.get_usage_timeseries_response import GetUsageTimeseriesResponse
+from .types.get_weekly_trends_response import GetWeeklyTrendsResponse
+
+
+class AnalyticsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawAnalyticsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawAnalyticsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawAnalyticsClient
+ """
+ return self._raw_client
+
+ def get_call_counts_log(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallCountsLogResponse:
+ """
+ Paginated listing of call records for the organization, with optional filtering by agent, campaign, call type, and date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallCountsLogResponse
+ Paginated call records
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_call_counts_log()
+ """
+ _response = self._raw_client.get_call_counts_log(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ page=page,
+ limit=limit,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_call_counts_by_day(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallCountsByDayResponse:
+ """
+ Returns call counts aggregated per calendar day, suitable for bar chart visualizations.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallCountsByDayResponse
+ Daily call counts
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_call_counts_by_day()
+ """
+ _response = self._raw_client.get_call_counts_by_day(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_conversation_details(
+ self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetConversationDetailsResponse:
+ """
+ Returns the full transcript and event stream for a specific call, reconstructed from ClickHouse event data.
+
+ Parameters
+ ----------
+ call_id : str
+ Unique identifier for the call
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetConversationDetailsResponse
+ Call transcript and events
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_conversation_details(
+ call_id="callId",
+ )
+ """
+ _response = self._raw_client.get_conversation_details(call_id, request_options=request_options)
+ return _response.data
+
+ def get_usage_timeseries(
+ self,
+ *,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetUsageTimeseriesResponse:
+ """
+ Returns daily credit usage over a date range.
+
+ Parameters
+ ----------
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetUsageTimeseriesResponse
+ Daily credit usage
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_usage_timeseries()
+ """
+ _response = self._raw_client.get_usage_timeseries(
+ date_from=date_from, date_to=date_to, request_options=request_options
+ )
+ return _response.data
+
+ def get_dashboard(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetDashboardResponse:
+ """
+ Batched endpoint that fetches all dashboard panels in a single request by running six sub-queries in parallel. Equivalent to calling `summary`, `call-volume-timeseries`, `call-outcomes-timeseries`, `pickup-rate-by-number`, `hourly-performance`, and `duration-stats` individually. Each field may be absent if that sub-query failed; partial data is still returned.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDashboardResponse
+ All dashboard panel data
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_dashboard()
+ """
+ _response = self._raw_client.get_dashboard(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_analytics_summary(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAnalyticsSummaryResponse:
+ """
+ Returns high-level KPI metrics with current period value, previous period value, and percent change for trend comparison.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAnalyticsSummaryResponse
+ KPI summary
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_analytics_summary()
+ """
+ _response = self._raw_client.get_analytics_summary(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_call_volume_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallVolumeTimeseriesResponse:
+ """
+ Returns daily call volume broken down by outcome (answered, no-answer, failed, cancelled) over the selected period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallVolumeTimeseriesResponse
+ Daily call volume by outcome
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_call_volume_timeseries()
+ """
+ _response = self._raw_client.get_call_volume_timeseries(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_pickup_rate_by_number(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetPickupRateByNumberResponse:
+ """
+ Returns pickup rate and call volume broken down per originating phone number.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetPickupRateByNumberResponse
+ Per-number pickup rate and call volume
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_pickup_rate_by_number()
+ """
+ _response = self._raw_client.get_pickup_rate_by_number(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_phone_number_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetPhoneNumberTrendsResponse:
+ """
+ Returns per-phone-number call volume and pickup rate as a daily timeseries, useful for spotting number-level degradation over time.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetPhoneNumberTrendsResponse
+ Per-number daily timeseries
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_phone_number_trends()
+ """
+ _response = self._raw_client.get_phone_number_trends(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_hourly_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetHourlyPerformanceResponse:
+ """
+ Returns call volume and performance metrics broken down by hour of day (0โ23), aggregated across the selected date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetHourlyPerformanceResponse
+ Hourly performance breakdown
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_hourly_performance()
+ """
+ _response = self._raw_client.get_hourly_performance(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_call_outcomes_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallOutcomesTimeseriesResponse:
+ """
+ Returns a daily breakdown of call outcomes (answered, no-answer, failed, cancelled) over time, plus totals for the entire period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallOutcomesTimeseriesResponse
+ Daily call outcomes and period totals
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_call_outcomes_timeseries()
+ """
+ _response = self._raw_client.get_call_outcomes_timeseries(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_duration_stats(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetDurationStatsResponse:
+ """
+ Returns call duration statistics including average, median, p90, p95 percentiles, and the proportion of short vs. long calls.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDurationStatsResponse
+ Duration statistics
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_duration_stats()
+ """
+ _response = self._raw_client.get_duration_stats(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_weekly_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetWeeklyTrendsResponse:
+ """
+ Returns per-week call performance metrics including volume, pickup rate, and duration percentiles (p50, p90). Each week starts on Monday.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetWeeklyTrendsResponse
+ Weekly performance metrics
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_weekly_trends()
+ """
+ _response = self._raw_client.get_weekly_trends(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_agent_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ sort_by: typing.Optional[str] = None,
+ sort_order: typing.Optional[GetAgentPerformanceRequestSortOrder] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAgentPerformanceResponse:
+ """
+ Returns per-agent call performance metrics. Supports sorting and limiting results.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ sort_by : typing.Optional[str]
+ Field to sort by (e.g. `totalCalls`, `pickupRate`, `avgDuration`)
+
+ sort_order : typing.Optional[GetAgentPerformanceRequestSortOrder]
+ Sort direction
+
+ limit : typing.Optional[int]
+ Maximum number of agents to return
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAgentPerformanceResponse
+ Per-agent performance metrics
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_agent_performance()
+ """
+ _response = self._raw_client.get_agent_performance(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ sort_by=sort_by,
+ sort_order=sort_order,
+ limit=limit,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_analytics_concurrency(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ include_agents: typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAnalyticsConcurrencyResponse:
+ """
+ Returns minute-by-minute concurrent call counts for a specific day. Optionally broken down per agent.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ include_agents : typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents]
+ Pass `true` to include a per-agent breakdown in the response
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAnalyticsConcurrencyResponse
+ Minute-level concurrency data
+
+ Examples
+ --------
+ import datetime
+
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_analytics_concurrency(
+ date=datetime.date.fromisoformat(
+ "2023-01-15",
+ ),
+ )
+ """
+ _response = self._raw_client.get_analytics_concurrency(
+ date=date, agent_id=agent_id, include_agents=include_agents, request_options=request_options
+ )
+ return _response.data
+
+ def get_call_start_distribution(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallStartDistributionResponse:
+ """
+ Returns the distribution of call start times as minute-level buckets for a specific day, showing when calls were initiated throughout the day.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallStartDistributionResponse
+ Minute-level call start distribution
+
+ Examples
+ --------
+ import datetime
+
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_call_start_distribution(
+ date=datetime.date.fromisoformat(
+ "2023-01-15",
+ ),
+ )
+ """
+ _response = self._raw_client.get_call_start_distribution(
+ date=date, agent_id=agent_id, request_options=request_options
+ )
+ return _response.data
+
+ def get_daily_call_summary(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetDailyCallSummaryResponse:
+ """
+ Returns an aggregate call summary for a specific day. Live in-progress and in-queue counts are merged from real-time data on top of the historical data.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDailyCallSummaryResponse
+ Daily call summary
+
+ Examples
+ --------
+ import datetime
+
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_daily_call_summary(
+ date=datetime.date.fromisoformat(
+ "2023-01-15",
+ ),
+ )
+ """
+ _response = self._raw_client.get_daily_call_summary(
+ date=date, agent_id=agent_id, request_options=request_options
+ )
+ return _response.data
+
+ def get_attempt_cohort(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAttemptCohortResponse:
+ """
+ Returns cohort analysis of call attempt numbers, showing how pickup rate changes across the 1st, 2nd, 3rd (etc.) attempts to reach the same number, including cumulative rates and marginal gain per additional attempt.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAttemptCohortResponse
+ Attempt cohort data
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.analytics.get_attempt_cohort()
+ """
+ _response = self._raw_client.get_attempt_cohort(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+
+class AsyncAnalyticsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawAnalyticsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawAnalyticsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawAnalyticsClient
+ """
+ return self._raw_client
+
+ async def get_call_counts_log(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallCountsLogResponse:
+ """
+ Paginated listing of call records for the organization, with optional filtering by agent, campaign, call type, and date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallCountsLogResponse
+ Paginated call records
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_call_counts_log()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_call_counts_log(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ page=page,
+ limit=limit,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_call_counts_by_day(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallCountsByDayResponse:
+ """
+ Returns call counts aggregated per calendar day, suitable for bar chart visualizations.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallCountsByDayResponse
+ Daily call counts
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_call_counts_by_day()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_call_counts_by_day(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_conversation_details(
+ self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetConversationDetailsResponse:
+ """
+ Returns the full transcript and event stream for a specific call, reconstructed from ClickHouse event data.
+
+ Parameters
+ ----------
+ call_id : str
+ Unique identifier for the call
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetConversationDetailsResponse
+ Call transcript and events
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_conversation_details(
+ call_id="callId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_conversation_details(call_id, request_options=request_options)
+ return _response.data
+
+ async def get_usage_timeseries(
+ self,
+ *,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetUsageTimeseriesResponse:
+ """
+ Returns daily credit usage over a date range.
+
+ Parameters
+ ----------
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetUsageTimeseriesResponse
+ Daily credit usage
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_usage_timeseries()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_usage_timeseries(
+ date_from=date_from, date_to=date_to, request_options=request_options
+ )
+ return _response.data
+
+ async def get_dashboard(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetDashboardResponse:
+ """
+ Batched endpoint that fetches all dashboard panels in a single request by running six sub-queries in parallel. Equivalent to calling `summary`, `call-volume-timeseries`, `call-outcomes-timeseries`, `pickup-rate-by-number`, `hourly-performance`, and `duration-stats` individually. Each field may be absent if that sub-query failed; partial data is still returned.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDashboardResponse
+ All dashboard panel data
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_dashboard()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_dashboard(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_analytics_summary(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAnalyticsSummaryResponse:
+ """
+ Returns high-level KPI metrics with current period value, previous period value, and percent change for trend comparison.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAnalyticsSummaryResponse
+ KPI summary
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_analytics_summary()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_analytics_summary(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_call_volume_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallVolumeTimeseriesResponse:
+ """
+ Returns daily call volume broken down by outcome (answered, no-answer, failed, cancelled) over the selected period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallVolumeTimeseriesResponse
+ Daily call volume by outcome
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_call_volume_timeseries()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_call_volume_timeseries(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_pickup_rate_by_number(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetPickupRateByNumberResponse:
+ """
+ Returns pickup rate and call volume broken down per originating phone number.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetPickupRateByNumberResponse
+ Per-number pickup rate and call volume
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_pickup_rate_by_number()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_pickup_rate_by_number(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_phone_number_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetPhoneNumberTrendsResponse:
+ """
+ Returns per-phone-number call volume and pickup rate as a daily timeseries, useful for spotting number-level degradation over time.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetPhoneNumberTrendsResponse
+ Per-number daily timeseries
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_phone_number_trends()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_phone_number_trends(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_hourly_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetHourlyPerformanceResponse:
+ """
+ Returns call volume and performance metrics broken down by hour of day (0โ23), aggregated across the selected date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetHourlyPerformanceResponse
+ Hourly performance breakdown
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_hourly_performance()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_hourly_performance(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_call_outcomes_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallOutcomesTimeseriesResponse:
+ """
+ Returns a daily breakdown of call outcomes (answered, no-answer, failed, cancelled) over time, plus totals for the entire period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallOutcomesTimeseriesResponse
+ Daily call outcomes and period totals
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_call_outcomes_timeseries()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_call_outcomes_timeseries(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_duration_stats(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetDurationStatsResponse:
+ """
+ Returns call duration statistics including average, median, p90, p95 percentiles, and the proportion of short vs. long calls.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDurationStatsResponse
+ Duration statistics
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_duration_stats()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_duration_stats(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_weekly_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetWeeklyTrendsResponse:
+ """
+ Returns per-week call performance metrics including volume, pickup rate, and duration percentiles (p50, p90). Each week starts on Monday.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetWeeklyTrendsResponse
+ Weekly performance metrics
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_weekly_trends()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_weekly_trends(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_agent_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ sort_by: typing.Optional[str] = None,
+ sort_order: typing.Optional[GetAgentPerformanceRequestSortOrder] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAgentPerformanceResponse:
+ """
+ Returns per-agent call performance metrics. Supports sorting and limiting results.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ sort_by : typing.Optional[str]
+ Field to sort by (e.g. `totalCalls`, `pickupRate`, `avgDuration`)
+
+ sort_order : typing.Optional[GetAgentPerformanceRequestSortOrder]
+ Sort direction
+
+ limit : typing.Optional[int]
+ Maximum number of agents to return
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAgentPerformanceResponse
+ Per-agent performance metrics
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_agent_performance()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_agent_performance(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ sort_by=sort_by,
+ sort_order=sort_order,
+ limit=limit,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_analytics_concurrency(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ include_agents: typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAnalyticsConcurrencyResponse:
+ """
+ Returns minute-by-minute concurrent call counts for a specific day. Optionally broken down per agent.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ include_agents : typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents]
+ Pass `true` to include a per-agent breakdown in the response
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAnalyticsConcurrencyResponse
+ Minute-level concurrency data
+
+ Examples
+ --------
+ import asyncio
+ import datetime
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_analytics_concurrency(
+ date=datetime.date.fromisoformat(
+ "2023-01-15",
+ ),
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_analytics_concurrency(
+ date=date, agent_id=agent_id, include_agents=include_agents, request_options=request_options
+ )
+ return _response.data
+
+ async def get_call_start_distribution(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetCallStartDistributionResponse:
+ """
+ Returns the distribution of call start times as minute-level buckets for a specific day, showing when calls were initiated throughout the day.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallStartDistributionResponse
+ Minute-level call start distribution
+
+ Examples
+ --------
+ import asyncio
+ import datetime
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_call_start_distribution(
+ date=datetime.date.fromisoformat(
+ "2023-01-15",
+ ),
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_call_start_distribution(
+ date=date, agent_id=agent_id, request_options=request_options
+ )
+ return _response.data
+
+ async def get_daily_call_summary(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetDailyCallSummaryResponse:
+ """
+ Returns an aggregate call summary for a specific day. Live in-progress and in-queue counts are merged from real-time data on top of the historical data.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetDailyCallSummaryResponse
+ Daily call summary
+
+ Examples
+ --------
+ import asyncio
+ import datetime
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_daily_call_summary(
+ date=datetime.date.fromisoformat(
+ "2023-01-15",
+ ),
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_daily_call_summary(
+ date=date, agent_id=agent_id, request_options=request_options
+ )
+ return _response.data
+
+ async def get_attempt_cohort(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> GetAttemptCohortResponse:
+ """
+ Returns cohort analysis of call attempt numbers, showing how pickup rate changes across the 1st, 2nd, 3rd (etc.) attempts to reach the same number, including cumulative rates and marginal gain per additional attempt.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetAttemptCohortResponse
+ Attempt cohort data
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.analytics.get_attempt_cohort()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_attempt_cohort(
+ agent_id=agent_id,
+ campaign_id=campaign_id,
+ call_type=call_type,
+ date_from=date_from,
+ date_to=date_to,
+ request_options=request_options,
+ )
+ return _response.data
diff --git a/src/smallestai/atoms/analytics/raw_client.py b/src/smallestai/atoms/analytics/raw_client.py
new file mode 100644
index 00000000..d8391f1c
--- /dev/null
+++ b/src/smallestai/atoms/analytics/raw_client.py
@@ -0,0 +1,3667 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.datetime_utils import serialize_datetime
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.jsonable_encoder import encode_path_param
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.unchecked_base_model import construct_type
+from ..errors.bad_request_error import BadRequestError
+from ..errors.internal_server_error import InternalServerError
+from ..errors.not_found_error import NotFoundError
+from ..errors.unauthorized_error import UnauthorizedError
+from .types.get_agent_performance_request_sort_order import GetAgentPerformanceRequestSortOrder
+from .types.get_agent_performance_response import GetAgentPerformanceResponse
+from .types.get_analytics_concurrency_request_include_agents import GetAnalyticsConcurrencyRequestIncludeAgents
+from .types.get_analytics_concurrency_response import GetAnalyticsConcurrencyResponse
+from .types.get_analytics_summary_response import GetAnalyticsSummaryResponse
+from .types.get_attempt_cohort_response import GetAttemptCohortResponse
+from .types.get_call_counts_by_day_response import GetCallCountsByDayResponse
+from .types.get_call_counts_log_response import GetCallCountsLogResponse
+from .types.get_call_outcomes_timeseries_response import GetCallOutcomesTimeseriesResponse
+from .types.get_call_start_distribution_response import GetCallStartDistributionResponse
+from .types.get_call_volume_timeseries_response import GetCallVolumeTimeseriesResponse
+from .types.get_conversation_details_response import GetConversationDetailsResponse
+from .types.get_daily_call_summary_response import GetDailyCallSummaryResponse
+from .types.get_dashboard_response import GetDashboardResponse
+from .types.get_duration_stats_response import GetDurationStatsResponse
+from .types.get_hourly_performance_response import GetHourlyPerformanceResponse
+from .types.get_phone_number_trends_response import GetPhoneNumberTrendsResponse
+from .types.get_pickup_rate_by_number_response import GetPickupRateByNumberResponse
+from .types.get_usage_timeseries_response import GetUsageTimeseriesResponse
+from .types.get_weekly_trends_response import GetWeeklyTrendsResponse
+from pydantic import ValidationError
+
+
+class RawAnalyticsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def get_call_counts_log(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetCallCountsLogResponse]:
+ """
+ Paginated listing of call records for the organization, with optional filtering by agent, campaign, call type, and date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetCallCountsLogResponse]
+ Paginated call records
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/call-counts-log",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ "page": page,
+ "limit": limit,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallCountsLogResponse,
+ construct_type(
+ type_=GetCallCountsLogResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_call_counts_by_day(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetCallCountsByDayResponse]:
+ """
+ Returns call counts aggregated per calendar day, suitable for bar chart visualizations.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetCallCountsByDayResponse]
+ Daily call counts
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/call-counts-by-day",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallCountsByDayResponse,
+ construct_type(
+ type_=GetCallCountsByDayResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_conversation_details(
+ self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetConversationDetailsResponse]:
+ """
+ Returns the full transcript and event stream for a specific call, reconstructed from ClickHouse event data.
+
+ Parameters
+ ----------
+ call_id : str
+ Unique identifier for the call
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetConversationDetailsResponse]
+ Call transcript and events
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"analytics/conversation-details/{encode_path_param(call_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetConversationDetailsResponse,
+ construct_type(
+ type_=GetConversationDetailsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_usage_timeseries(
+ self,
+ *,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetUsageTimeseriesResponse]:
+ """
+ Returns daily credit usage over a date range.
+
+ Parameters
+ ----------
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetUsageTimeseriesResponse]
+ Daily credit usage
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/usage/timeseries",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetUsageTimeseriesResponse,
+ construct_type(
+ type_=GetUsageTimeseriesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_dashboard(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetDashboardResponse]:
+ """
+ Batched endpoint that fetches all dashboard panels in a single request by running six sub-queries in parallel. Equivalent to calling `summary`, `call-volume-timeseries`, `call-outcomes-timeseries`, `pickup-rate-by-number`, `hourly-performance`, and `duration-stats` individually. Each field may be absent if that sub-query failed; partial data is still returned.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetDashboardResponse]
+ All dashboard panel data
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/dashboard",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDashboardResponse,
+ construct_type(
+ type_=GetDashboardResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_analytics_summary(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetAnalyticsSummaryResponse]:
+ """
+ Returns high-level KPI metrics with current period value, previous period value, and percent change for trend comparison.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetAnalyticsSummaryResponse]
+ KPI summary
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/summary",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAnalyticsSummaryResponse,
+ construct_type(
+ type_=GetAnalyticsSummaryResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_call_volume_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetCallVolumeTimeseriesResponse]:
+ """
+ Returns daily call volume broken down by outcome (answered, no-answer, failed, cancelled) over the selected period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetCallVolumeTimeseriesResponse]
+ Daily call volume by outcome
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/call-volume-timeseries",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallVolumeTimeseriesResponse,
+ construct_type(
+ type_=GetCallVolumeTimeseriesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_pickup_rate_by_number(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetPickupRateByNumberResponse]:
+ """
+ Returns pickup rate and call volume broken down per originating phone number.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetPickupRateByNumberResponse]
+ Per-number pickup rate and call volume
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/pickup-rate-by-number",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetPickupRateByNumberResponse,
+ construct_type(
+ type_=GetPickupRateByNumberResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_phone_number_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetPhoneNumberTrendsResponse]:
+ """
+ Returns per-phone-number call volume and pickup rate as a daily timeseries, useful for spotting number-level degradation over time.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetPhoneNumberTrendsResponse]
+ Per-number daily timeseries
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/phone-number-trends",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetPhoneNumberTrendsResponse,
+ construct_type(
+ type_=GetPhoneNumberTrendsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_hourly_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetHourlyPerformanceResponse]:
+ """
+ Returns call volume and performance metrics broken down by hour of day (0โ23), aggregated across the selected date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetHourlyPerformanceResponse]
+ Hourly performance breakdown
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/hourly-performance",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetHourlyPerformanceResponse,
+ construct_type(
+ type_=GetHourlyPerformanceResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_call_outcomes_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetCallOutcomesTimeseriesResponse]:
+ """
+ Returns a daily breakdown of call outcomes (answered, no-answer, failed, cancelled) over time, plus totals for the entire period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetCallOutcomesTimeseriesResponse]
+ Daily call outcomes and period totals
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/call-outcomes-timeseries",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallOutcomesTimeseriesResponse,
+ construct_type(
+ type_=GetCallOutcomesTimeseriesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_duration_stats(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetDurationStatsResponse]:
+ """
+ Returns call duration statistics including average, median, p90, p95 percentiles, and the proportion of short vs. long calls.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetDurationStatsResponse]
+ Duration statistics
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/duration-stats",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDurationStatsResponse,
+ construct_type(
+ type_=GetDurationStatsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_weekly_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetWeeklyTrendsResponse]:
+ """
+ Returns per-week call performance metrics including volume, pickup rate, and duration percentiles (p50, p90). Each week starts on Monday.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetWeeklyTrendsResponse]
+ Weekly performance metrics
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/weekly-trends",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetWeeklyTrendsResponse,
+ construct_type(
+ type_=GetWeeklyTrendsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_agent_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ sort_by: typing.Optional[str] = None,
+ sort_order: typing.Optional[GetAgentPerformanceRequestSortOrder] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetAgentPerformanceResponse]:
+ """
+ Returns per-agent call performance metrics. Supports sorting and limiting results.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ sort_by : typing.Optional[str]
+ Field to sort by (e.g. `totalCalls`, `pickupRate`, `avgDuration`)
+
+ sort_order : typing.Optional[GetAgentPerformanceRequestSortOrder]
+ Sort direction
+
+ limit : typing.Optional[int]
+ Maximum number of agents to return
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetAgentPerformanceResponse]
+ Per-agent performance metrics
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/agent-performance",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ "sortBy": sort_by,
+ "sortOrder": sort_order,
+ "limit": limit,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAgentPerformanceResponse,
+ construct_type(
+ type_=GetAgentPerformanceResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_analytics_concurrency(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ include_agents: typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetAnalyticsConcurrencyResponse]:
+ """
+ Returns minute-by-minute concurrent call counts for a specific day. Optionally broken down per agent.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ include_agents : typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents]
+ Pass `true` to include a per-agent breakdown in the response
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetAnalyticsConcurrencyResponse]
+ Minute-level concurrency data
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/concurrency",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "date": str(date),
+ "agentId": agent_id,
+ "includeAgents": include_agents,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAnalyticsConcurrencyResponse,
+ construct_type(
+ type_=GetAnalyticsConcurrencyResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_call_start_distribution(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetCallStartDistributionResponse]:
+ """
+ Returns the distribution of call start times as minute-level buckets for a specific day, showing when calls were initiated throughout the day.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetCallStartDistributionResponse]
+ Minute-level call start distribution
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/call-start-distribution",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "date": str(date),
+ "agentId": agent_id,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallStartDistributionResponse,
+ construct_type(
+ type_=GetCallStartDistributionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_daily_call_summary(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetDailyCallSummaryResponse]:
+ """
+ Returns an aggregate call summary for a specific day. Live in-progress and in-queue counts are merged from real-time data on top of the historical data.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetDailyCallSummaryResponse]
+ Daily call summary
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/daily-call-summary",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "date": str(date),
+ "agentId": agent_id,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDailyCallSummaryResponse,
+ construct_type(
+ type_=GetDailyCallSummaryResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_attempt_cohort(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[GetAttemptCohortResponse]:
+ """
+ Returns cohort analysis of call attempt numbers, showing how pickup rate changes across the 1st, 2nd, 3rd (etc.) attempts to reach the same number, including cumulative rates and marginal gain per additional attempt.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetAttemptCohortResponse]
+ Attempt cohort data
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "analytics/attempt-cohort",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAttemptCohortResponse,
+ construct_type(
+ type_=GetAttemptCohortResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawAnalyticsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def get_call_counts_log(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetCallCountsLogResponse]:
+ """
+ Paginated listing of call records for the organization, with optional filtering by agent, campaign, call type, and date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetCallCountsLogResponse]
+ Paginated call records
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/call-counts-log",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ "page": page,
+ "limit": limit,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallCountsLogResponse,
+ construct_type(
+ type_=GetCallCountsLogResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_call_counts_by_day(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetCallCountsByDayResponse]:
+ """
+ Returns call counts aggregated per calendar day, suitable for bar chart visualizations.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetCallCountsByDayResponse]
+ Daily call counts
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/call-counts-by-day",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallCountsByDayResponse,
+ construct_type(
+ type_=GetCallCountsByDayResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_conversation_details(
+ self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetConversationDetailsResponse]:
+ """
+ Returns the full transcript and event stream for a specific call, reconstructed from ClickHouse event data.
+
+ Parameters
+ ----------
+ call_id : str
+ Unique identifier for the call
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetConversationDetailsResponse]
+ Call transcript and events
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"analytics/conversation-details/{encode_path_param(call_id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetConversationDetailsResponse,
+ construct_type(
+ type_=GetConversationDetailsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_usage_timeseries(
+ self,
+ *,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetUsageTimeseriesResponse]:
+ """
+ Returns daily credit usage over a date range.
+
+ Parameters
+ ----------
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetUsageTimeseriesResponse]
+ Daily credit usage
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/usage/timeseries",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetUsageTimeseriesResponse,
+ construct_type(
+ type_=GetUsageTimeseriesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_dashboard(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetDashboardResponse]:
+ """
+ Batched endpoint that fetches all dashboard panels in a single request by running six sub-queries in parallel. Equivalent to calling `summary`, `call-volume-timeseries`, `call-outcomes-timeseries`, `pickup-rate-by-number`, `hourly-performance`, and `duration-stats` individually. Each field may be absent if that sub-query failed; partial data is still returned.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetDashboardResponse]
+ All dashboard panel data
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/dashboard",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDashboardResponse,
+ construct_type(
+ type_=GetDashboardResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_analytics_summary(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetAnalyticsSummaryResponse]:
+ """
+ Returns high-level KPI metrics with current period value, previous period value, and percent change for trend comparison.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetAnalyticsSummaryResponse]
+ KPI summary
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/summary",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAnalyticsSummaryResponse,
+ construct_type(
+ type_=GetAnalyticsSummaryResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_call_volume_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetCallVolumeTimeseriesResponse]:
+ """
+ Returns daily call volume broken down by outcome (answered, no-answer, failed, cancelled) over the selected period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetCallVolumeTimeseriesResponse]
+ Daily call volume by outcome
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/call-volume-timeseries",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallVolumeTimeseriesResponse,
+ construct_type(
+ type_=GetCallVolumeTimeseriesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_pickup_rate_by_number(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetPickupRateByNumberResponse]:
+ """
+ Returns pickup rate and call volume broken down per originating phone number.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetPickupRateByNumberResponse]
+ Per-number pickup rate and call volume
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/pickup-rate-by-number",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetPickupRateByNumberResponse,
+ construct_type(
+ type_=GetPickupRateByNumberResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_phone_number_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetPhoneNumberTrendsResponse]:
+ """
+ Returns per-phone-number call volume and pickup rate as a daily timeseries, useful for spotting number-level degradation over time.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetPhoneNumberTrendsResponse]
+ Per-number daily timeseries
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/phone-number-trends",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetPhoneNumberTrendsResponse,
+ construct_type(
+ type_=GetPhoneNumberTrendsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_hourly_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetHourlyPerformanceResponse]:
+ """
+ Returns call volume and performance metrics broken down by hour of day (0โ23), aggregated across the selected date range.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetHourlyPerformanceResponse]
+ Hourly performance breakdown
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/hourly-performance",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetHourlyPerformanceResponse,
+ construct_type(
+ type_=GetHourlyPerformanceResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_call_outcomes_timeseries(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetCallOutcomesTimeseriesResponse]:
+ """
+ Returns a daily breakdown of call outcomes (answered, no-answer, failed, cancelled) over time, plus totals for the entire period.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetCallOutcomesTimeseriesResponse]
+ Daily call outcomes and period totals
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/call-outcomes-timeseries",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallOutcomesTimeseriesResponse,
+ construct_type(
+ type_=GetCallOutcomesTimeseriesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_duration_stats(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetDurationStatsResponse]:
+ """
+ Returns call duration statistics including average, median, p90, p95 percentiles, and the proportion of short vs. long calls.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetDurationStatsResponse]
+ Duration statistics
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/duration-stats",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDurationStatsResponse,
+ construct_type(
+ type_=GetDurationStatsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_weekly_trends(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetWeeklyTrendsResponse]:
+ """
+ Returns per-week call performance metrics including volume, pickup rate, and duration percentiles (p50, p90). Each week starts on Monday.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetWeeklyTrendsResponse]
+ Weekly performance metrics
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/weekly-trends",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetWeeklyTrendsResponse,
+ construct_type(
+ type_=GetWeeklyTrendsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_agent_performance(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ sort_by: typing.Optional[str] = None,
+ sort_order: typing.Optional[GetAgentPerformanceRequestSortOrder] = None,
+ limit: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetAgentPerformanceResponse]:
+ """
+ Returns per-agent call performance metrics. Supports sorting and limiting results.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ sort_by : typing.Optional[str]
+ Field to sort by (e.g. `totalCalls`, `pickupRate`, `avgDuration`)
+
+ sort_order : typing.Optional[GetAgentPerformanceRequestSortOrder]
+ Sort direction
+
+ limit : typing.Optional[int]
+ Maximum number of agents to return
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetAgentPerformanceResponse]
+ Per-agent performance metrics
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/agent-performance",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ "sortBy": sort_by,
+ "sortOrder": sort_order,
+ "limit": limit,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAgentPerformanceResponse,
+ construct_type(
+ type_=GetAgentPerformanceResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_analytics_concurrency(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ include_agents: typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetAnalyticsConcurrencyResponse]:
+ """
+ Returns minute-by-minute concurrent call counts for a specific day. Optionally broken down per agent.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ include_agents : typing.Optional[GetAnalyticsConcurrencyRequestIncludeAgents]
+ Pass `true` to include a per-agent breakdown in the response
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetAnalyticsConcurrencyResponse]
+ Minute-level concurrency data
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/concurrency",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "date": str(date),
+ "agentId": agent_id,
+ "includeAgents": include_agents,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAnalyticsConcurrencyResponse,
+ construct_type(
+ type_=GetAnalyticsConcurrencyResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_call_start_distribution(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetCallStartDistributionResponse]:
+ """
+ Returns the distribution of call start times as minute-level buckets for a specific day, showing when calls were initiated throughout the day.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetCallStartDistributionResponse]
+ Minute-level call start distribution
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/call-start-distribution",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "date": str(date),
+ "agentId": agent_id,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallStartDistributionResponse,
+ construct_type(
+ type_=GetCallStartDistributionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_daily_call_summary(
+ self,
+ *,
+ date: dt.date,
+ agent_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetDailyCallSummaryResponse]:
+ """
+ Returns an aggregate call summary for a specific day. Live in-progress and in-queue counts are merged from real-time data on top of the historical data.
+
+ Parameters
+ ----------
+ date : dt.date
+ Date to query (YYYY-MM-DD)
+
+ agent_id : typing.Optional[str]
+ Filter to a specific agent
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetDailyCallSummaryResponse]
+ Daily call summary
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/daily-call-summary",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "date": str(date),
+ "agentId": agent_id,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetDailyCallSummaryResponse,
+ construct_type(
+ type_=GetDailyCallSummaryResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_attempt_cohort(
+ self,
+ *,
+ agent_id: typing.Optional[str] = None,
+ campaign_id: typing.Optional[str] = None,
+ call_type: typing.Optional[str] = None,
+ date_from: typing.Optional[dt.datetime] = None,
+ date_to: typing.Optional[dt.datetime] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetAttemptCohortResponse]:
+ """
+ Returns cohort analysis of call attempt numbers, showing how pickup rate changes across the 1st, 2nd, 3rd (etc.) attempts to reach the same number, including cumulative rates and marginal gain per additional attempt.
+
+ Parameters
+ ----------
+ agent_id : typing.Optional[str]
+ Comma-separated agent IDs to filter results
+
+ campaign_id : typing.Optional[str]
+ Campaign ID to filter results
+
+ call_type : typing.Optional[str]
+ Type of call to filter (e.g. `inbound`, `outbound`)
+
+ date_from : typing.Optional[dt.datetime]
+ Start of date range (ISO 8601)
+
+ date_to : typing.Optional[dt.datetime]
+ End of date range (ISO 8601)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetAttemptCohortResponse]
+ Attempt cohort data
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "analytics/attempt-cohort",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "campaignId": campaign_id,
+ "callType": call_type,
+ "dateFrom": serialize_datetime(date_from) if date_from is not None else None,
+ "dateTo": serialize_datetime(date_to) if date_to is not None else None,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetAttemptCohortResponse,
+ construct_type(
+ type_=GetAttemptCohortResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/analytics/types/__init__.py b/src/smallestai/atoms/analytics/types/__init__.py
new file mode 100644
index 00000000..98c5fe94
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/__init__.py
@@ -0,0 +1,220 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_agent_performance_request_sort_order import GetAgentPerformanceRequestSortOrder
+ from .get_agent_performance_response import GetAgentPerformanceResponse
+ from .get_agent_performance_response_data import GetAgentPerformanceResponseData
+ from .get_agent_performance_response_data_agents_item import GetAgentPerformanceResponseDataAgentsItem
+ from .get_analytics_concurrency_request_include_agents import GetAnalyticsConcurrencyRequestIncludeAgents
+ from .get_analytics_concurrency_response import GetAnalyticsConcurrencyResponse
+ from .get_analytics_concurrency_response_data import GetAnalyticsConcurrencyResponseData
+ from .get_analytics_concurrency_response_data_by_agent_item import GetAnalyticsConcurrencyResponseDataByAgentItem
+ from .get_analytics_concurrency_response_data_data_points_item import (
+ GetAnalyticsConcurrencyResponseDataDataPointsItem,
+ )
+ from .get_analytics_summary_response import GetAnalyticsSummaryResponse
+ from .get_analytics_summary_response_data import GetAnalyticsSummaryResponseData
+ from .get_attempt_cohort_response import GetAttemptCohortResponse
+ from .get_attempt_cohort_response_data import GetAttemptCohortResponseData
+ from .get_attempt_cohort_response_data_cohorts_item import GetAttemptCohortResponseDataCohortsItem
+ from .get_call_counts_by_day_response import GetCallCountsByDayResponse
+ from .get_call_counts_by_day_response_data import GetCallCountsByDayResponseData
+ from .get_call_counts_by_day_response_data_call_counts_by_day_item import (
+ GetCallCountsByDayResponseDataCallCountsByDayItem,
+ )
+ from .get_call_counts_log_response import GetCallCountsLogResponse
+ from .get_call_counts_log_response_data import GetCallCountsLogResponseData
+ from .get_call_counts_log_response_data_calls_item import GetCallCountsLogResponseDataCallsItem
+ from .get_call_outcomes_timeseries_response import GetCallOutcomesTimeseriesResponse
+ from .get_call_outcomes_timeseries_response_data import GetCallOutcomesTimeseriesResponseData
+ from .get_call_outcomes_timeseries_response_data_data_points_item import (
+ GetCallOutcomesTimeseriesResponseDataDataPointsItem,
+ )
+ from .get_call_outcomes_timeseries_response_data_totals import GetCallOutcomesTimeseriesResponseDataTotals
+ from .get_call_start_distribution_response import GetCallStartDistributionResponse
+ from .get_call_start_distribution_response_data import GetCallStartDistributionResponseData
+ from .get_call_start_distribution_response_data_data_points_item import (
+ GetCallStartDistributionResponseDataDataPointsItem,
+ )
+ from .get_call_volume_timeseries_response import GetCallVolumeTimeseriesResponse
+ from .get_call_volume_timeseries_response_data import GetCallVolumeTimeseriesResponseData
+ from .get_call_volume_timeseries_response_data_data_points_item import (
+ GetCallVolumeTimeseriesResponseDataDataPointsItem,
+ )
+ from .get_conversation_details_response import GetConversationDetailsResponse
+ from .get_conversation_details_response_data import GetConversationDetailsResponseData
+ from .get_conversation_details_response_data_events_item import GetConversationDetailsResponseDataEventsItem
+ from .get_conversation_details_response_data_transcript_item import GetConversationDetailsResponseDataTranscriptItem
+ from .get_daily_call_summary_response import GetDailyCallSummaryResponse
+ from .get_daily_call_summary_response_data import GetDailyCallSummaryResponseData
+ from .get_dashboard_response import GetDashboardResponse
+ from .get_dashboard_response_data import GetDashboardResponseData
+ from .get_duration_stats_response import GetDurationStatsResponse
+ from .get_duration_stats_response_data import GetDurationStatsResponseData
+ from .get_hourly_performance_response import GetHourlyPerformanceResponse
+ from .get_hourly_performance_response_data import GetHourlyPerformanceResponseData
+ from .get_hourly_performance_response_data_hours_item import GetHourlyPerformanceResponseDataHoursItem
+ from .get_phone_number_trends_response import GetPhoneNumberTrendsResponse
+ from .get_phone_number_trends_response_data import GetPhoneNumberTrendsResponseData
+ from .get_phone_number_trends_response_data_trends_item import GetPhoneNumberTrendsResponseDataTrendsItem
+ from .get_phone_number_trends_response_data_trends_item_data_points_item import (
+ GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem,
+ )
+ from .get_pickup_rate_by_number_response import GetPickupRateByNumberResponse
+ from .get_pickup_rate_by_number_response_data import GetPickupRateByNumberResponseData
+ from .get_pickup_rate_by_number_response_data_numbers_item import GetPickupRateByNumberResponseDataNumbersItem
+ from .get_usage_timeseries_response import GetUsageTimeseriesResponse
+ from .get_usage_timeseries_response_data import GetUsageTimeseriesResponseData
+ from .get_usage_timeseries_response_data_date_range import GetUsageTimeseriesResponseDataDateRange
+ from .get_usage_timeseries_response_data_day_wise_credits_item import (
+ GetUsageTimeseriesResponseDataDayWiseCreditsItem,
+ )
+ from .get_weekly_trends_response import GetWeeklyTrendsResponse
+ from .get_weekly_trends_response_data import GetWeeklyTrendsResponseData
+ from .get_weekly_trends_response_data_weeks_item import GetWeeklyTrendsResponseDataWeeksItem
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetAgentPerformanceRequestSortOrder": ".get_agent_performance_request_sort_order",
+ "GetAgentPerformanceResponse": ".get_agent_performance_response",
+ "GetAgentPerformanceResponseData": ".get_agent_performance_response_data",
+ "GetAgentPerformanceResponseDataAgentsItem": ".get_agent_performance_response_data_agents_item",
+ "GetAnalyticsConcurrencyRequestIncludeAgents": ".get_analytics_concurrency_request_include_agents",
+ "GetAnalyticsConcurrencyResponse": ".get_analytics_concurrency_response",
+ "GetAnalyticsConcurrencyResponseData": ".get_analytics_concurrency_response_data",
+ "GetAnalyticsConcurrencyResponseDataByAgentItem": ".get_analytics_concurrency_response_data_by_agent_item",
+ "GetAnalyticsConcurrencyResponseDataDataPointsItem": ".get_analytics_concurrency_response_data_data_points_item",
+ "GetAnalyticsSummaryResponse": ".get_analytics_summary_response",
+ "GetAnalyticsSummaryResponseData": ".get_analytics_summary_response_data",
+ "GetAttemptCohortResponse": ".get_attempt_cohort_response",
+ "GetAttemptCohortResponseData": ".get_attempt_cohort_response_data",
+ "GetAttemptCohortResponseDataCohortsItem": ".get_attempt_cohort_response_data_cohorts_item",
+ "GetCallCountsByDayResponse": ".get_call_counts_by_day_response",
+ "GetCallCountsByDayResponseData": ".get_call_counts_by_day_response_data",
+ "GetCallCountsByDayResponseDataCallCountsByDayItem": ".get_call_counts_by_day_response_data_call_counts_by_day_item",
+ "GetCallCountsLogResponse": ".get_call_counts_log_response",
+ "GetCallCountsLogResponseData": ".get_call_counts_log_response_data",
+ "GetCallCountsLogResponseDataCallsItem": ".get_call_counts_log_response_data_calls_item",
+ "GetCallOutcomesTimeseriesResponse": ".get_call_outcomes_timeseries_response",
+ "GetCallOutcomesTimeseriesResponseData": ".get_call_outcomes_timeseries_response_data",
+ "GetCallOutcomesTimeseriesResponseDataDataPointsItem": ".get_call_outcomes_timeseries_response_data_data_points_item",
+ "GetCallOutcomesTimeseriesResponseDataTotals": ".get_call_outcomes_timeseries_response_data_totals",
+ "GetCallStartDistributionResponse": ".get_call_start_distribution_response",
+ "GetCallStartDistributionResponseData": ".get_call_start_distribution_response_data",
+ "GetCallStartDistributionResponseDataDataPointsItem": ".get_call_start_distribution_response_data_data_points_item",
+ "GetCallVolumeTimeseriesResponse": ".get_call_volume_timeseries_response",
+ "GetCallVolumeTimeseriesResponseData": ".get_call_volume_timeseries_response_data",
+ "GetCallVolumeTimeseriesResponseDataDataPointsItem": ".get_call_volume_timeseries_response_data_data_points_item",
+ "GetConversationDetailsResponse": ".get_conversation_details_response",
+ "GetConversationDetailsResponseData": ".get_conversation_details_response_data",
+ "GetConversationDetailsResponseDataEventsItem": ".get_conversation_details_response_data_events_item",
+ "GetConversationDetailsResponseDataTranscriptItem": ".get_conversation_details_response_data_transcript_item",
+ "GetDailyCallSummaryResponse": ".get_daily_call_summary_response",
+ "GetDailyCallSummaryResponseData": ".get_daily_call_summary_response_data",
+ "GetDashboardResponse": ".get_dashboard_response",
+ "GetDashboardResponseData": ".get_dashboard_response_data",
+ "GetDurationStatsResponse": ".get_duration_stats_response",
+ "GetDurationStatsResponseData": ".get_duration_stats_response_data",
+ "GetHourlyPerformanceResponse": ".get_hourly_performance_response",
+ "GetHourlyPerformanceResponseData": ".get_hourly_performance_response_data",
+ "GetHourlyPerformanceResponseDataHoursItem": ".get_hourly_performance_response_data_hours_item",
+ "GetPhoneNumberTrendsResponse": ".get_phone_number_trends_response",
+ "GetPhoneNumberTrendsResponseData": ".get_phone_number_trends_response_data",
+ "GetPhoneNumberTrendsResponseDataTrendsItem": ".get_phone_number_trends_response_data_trends_item",
+ "GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem": ".get_phone_number_trends_response_data_trends_item_data_points_item",
+ "GetPickupRateByNumberResponse": ".get_pickup_rate_by_number_response",
+ "GetPickupRateByNumberResponseData": ".get_pickup_rate_by_number_response_data",
+ "GetPickupRateByNumberResponseDataNumbersItem": ".get_pickup_rate_by_number_response_data_numbers_item",
+ "GetUsageTimeseriesResponse": ".get_usage_timeseries_response",
+ "GetUsageTimeseriesResponseData": ".get_usage_timeseries_response_data",
+ "GetUsageTimeseriesResponseDataDateRange": ".get_usage_timeseries_response_data_date_range",
+ "GetUsageTimeseriesResponseDataDayWiseCreditsItem": ".get_usage_timeseries_response_data_day_wise_credits_item",
+ "GetWeeklyTrendsResponse": ".get_weekly_trends_response",
+ "GetWeeklyTrendsResponseData": ".get_weekly_trends_response_data",
+ "GetWeeklyTrendsResponseDataWeeksItem": ".get_weekly_trends_response_data_weeks_item",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetAgentPerformanceRequestSortOrder",
+ "GetAgentPerformanceResponse",
+ "GetAgentPerformanceResponseData",
+ "GetAgentPerformanceResponseDataAgentsItem",
+ "GetAnalyticsConcurrencyRequestIncludeAgents",
+ "GetAnalyticsConcurrencyResponse",
+ "GetAnalyticsConcurrencyResponseData",
+ "GetAnalyticsConcurrencyResponseDataByAgentItem",
+ "GetAnalyticsConcurrencyResponseDataDataPointsItem",
+ "GetAnalyticsSummaryResponse",
+ "GetAnalyticsSummaryResponseData",
+ "GetAttemptCohortResponse",
+ "GetAttemptCohortResponseData",
+ "GetAttemptCohortResponseDataCohortsItem",
+ "GetCallCountsByDayResponse",
+ "GetCallCountsByDayResponseData",
+ "GetCallCountsByDayResponseDataCallCountsByDayItem",
+ "GetCallCountsLogResponse",
+ "GetCallCountsLogResponseData",
+ "GetCallCountsLogResponseDataCallsItem",
+ "GetCallOutcomesTimeseriesResponse",
+ "GetCallOutcomesTimeseriesResponseData",
+ "GetCallOutcomesTimeseriesResponseDataDataPointsItem",
+ "GetCallOutcomesTimeseriesResponseDataTotals",
+ "GetCallStartDistributionResponse",
+ "GetCallStartDistributionResponseData",
+ "GetCallStartDistributionResponseDataDataPointsItem",
+ "GetCallVolumeTimeseriesResponse",
+ "GetCallVolumeTimeseriesResponseData",
+ "GetCallVolumeTimeseriesResponseDataDataPointsItem",
+ "GetConversationDetailsResponse",
+ "GetConversationDetailsResponseData",
+ "GetConversationDetailsResponseDataEventsItem",
+ "GetConversationDetailsResponseDataTranscriptItem",
+ "GetDailyCallSummaryResponse",
+ "GetDailyCallSummaryResponseData",
+ "GetDashboardResponse",
+ "GetDashboardResponseData",
+ "GetDurationStatsResponse",
+ "GetDurationStatsResponseData",
+ "GetHourlyPerformanceResponse",
+ "GetHourlyPerformanceResponseData",
+ "GetHourlyPerformanceResponseDataHoursItem",
+ "GetPhoneNumberTrendsResponse",
+ "GetPhoneNumberTrendsResponseData",
+ "GetPhoneNumberTrendsResponseDataTrendsItem",
+ "GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem",
+ "GetPickupRateByNumberResponse",
+ "GetPickupRateByNumberResponseData",
+ "GetPickupRateByNumberResponseDataNumbersItem",
+ "GetUsageTimeseriesResponse",
+ "GetUsageTimeseriesResponseData",
+ "GetUsageTimeseriesResponseDataDateRange",
+ "GetUsageTimeseriesResponseDataDayWiseCreditsItem",
+ "GetWeeklyTrendsResponse",
+ "GetWeeklyTrendsResponseData",
+ "GetWeeklyTrendsResponseDataWeeksItem",
+]
diff --git a/src/smallestai/atoms/analytics/types/get_agent_performance_request_sort_order.py b/src/smallestai/atoms/analytics/types/get_agent_performance_request_sort_order.py
new file mode 100644
index 00000000..c07ae918
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_agent_performance_request_sort_order.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+GetAgentPerformanceRequestSortOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any]
diff --git a/src/smallestai/atoms/campaigns/types/post_campaign_id_start_response.py b/src/smallestai/atoms/analytics/types/get_agent_performance_response.py
similarity index 67%
rename from src/smallestai/atoms/campaigns/types/post_campaign_id_start_response.py
rename to src/smallestai/atoms/analytics/types/get_agent_performance_response.py
index 0a0c557e..aa6d5ec7 100644
--- a/src/smallestai/atoms/campaigns/types/post_campaign_id_start_response.py
+++ b/src/smallestai/atoms/analytics/types/get_agent_performance_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_campaign_id_start_response_data import PostCampaignIdStartResponseData
+from .get_agent_performance_response_data import GetAgentPerformanceResponseData
-class PostCampaignIdStartResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[PostCampaignIdStartResponseData] = None
+class GetAgentPerformanceResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetAgentPerformanceResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_agent.py b/src/smallestai/atoms/analytics/types/get_agent_performance_response_data.py
similarity index 54%
rename from src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_agent.py
rename to src/smallestai/atoms/analytics/types/get_agent_performance_response_data.py
index cc59c010..5b5b9fe1 100644
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_agent.py
+++ b/src/smallestai/atoms/analytics/types/get_agent_performance_response_data.py
@@ -7,20 +7,16 @@
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.serialization import FieldMetadata
from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_agent_performance_response_data_agents_item import GetAgentPerformanceResponseDataAgentsItem
-class GetCampaignIdResponseDataCampaignAgent(UncheckedBaseModel):
- """
- The agent assigned to this campaign
- """
-
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- name: typing.Optional[str] = None
- workflow_type: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="workflowType"), pydantic.Field(alias="workflowType")
+class GetAgentPerformanceResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
] = None
+ agents: typing.Optional[typing.List[GetAgentPerformanceResponseDataAgentsItem]] = None
+ period: typing.Optional[AnalyticsDateRange] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_agent_performance_response_data_agents_item.py b/src/smallestai/atoms/analytics/types/get_agent_performance_response_data_agents_item.py
new file mode 100644
index 00000000..4dff2ecf
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_agent_performance_response_data_agents_item.py
@@ -0,0 +1,41 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetAgentPerformanceResponseDataAgentsItem(UncheckedBaseModel):
+ agent_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="agentId"), pydantic.Field(alias="agentId")
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+ avg_duration: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="avgDuration"),
+ pydantic.Field(alias="avgDuration", description="Average call duration in seconds"),
+ ] = None
+ total_cost: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCost"), pydantic.Field(alias="totalCost")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_analytics_concurrency_request_include_agents.py b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_request_include_agents.py
new file mode 100644
index 00000000..bd69edf7
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_request_include_agents.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+GetAnalyticsConcurrencyRequestIncludeAgents = typing.Union[typing.Literal["true", "false"], typing.Any]
diff --git a/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response.py b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response.py
new file mode 100644
index 00000000..7f573acd
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_analytics_concurrency_response_data import GetAnalyticsConcurrencyResponseData
+
+
+class GetAnalyticsConcurrencyResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetAnalyticsConcurrencyResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data.py b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data.py
new file mode 100644
index 00000000..782d5b70
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data.py
@@ -0,0 +1,44 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_analytics_concurrency_response_data_by_agent_item import GetAnalyticsConcurrencyResponseDataByAgentItem
+from .get_analytics_concurrency_response_data_data_points_item import GetAnalyticsConcurrencyResponseDataDataPointsItem
+
+
+class GetAnalyticsConcurrencyResponseData(UncheckedBaseModel):
+ date: typing.Optional[dt.date] = None
+ data_points: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetAnalyticsConcurrencyResponseDataDataPointsItem]],
+ FieldMetadata(alias="dataPoints"),
+ pydantic.Field(alias="dataPoints"),
+ ] = None
+ peak_concurrent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="peakConcurrent"), pydantic.Field(alias="peakConcurrent")
+ ] = None
+ avg_concurrent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="avgConcurrent"), pydantic.Field(alias="avgConcurrent")
+ ] = None
+ total_minutes: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalMinutes"), pydantic.Field(alias="totalMinutes")
+ ] = None
+ by_agent: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetAnalyticsConcurrencyResponseDataByAgentItem]],
+ FieldMetadata(alias="byAgent"),
+ pydantic.Field(alias="byAgent", description="Only present when `includeAgents=true`"),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data_by_agent_item.py b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data_by_agent_item.py
new file mode 100644
index 00000000..bc26418c
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data_by_agent_item.py
@@ -0,0 +1,33 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetAnalyticsConcurrencyResponseDataByAgentItem(UncheckedBaseModel):
+ agent_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="agentId"), pydantic.Field(alias="agentId")
+ ] = None
+ peak_concurrent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="peakConcurrent"), pydantic.Field(alias="peakConcurrent")
+ ] = None
+ avg_concurrent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="avgConcurrent"), pydantic.Field(alias="avgConcurrent")
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_get_sitemap_urls_response_data.py b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data_data_points_item.py
similarity index 62%
rename from src/smallestai/atoms/knowledge_base/types/post_knowledgebase_get_sitemap_urls_response_data.py
rename to src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data_data_points_item.py
index 90b765d0..85250d3e 100644
--- a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_get_sitemap_urls_response_data.py
+++ b/src/smallestai/atoms/analytics/types/get_analytics_concurrency_response_data_data_points_item.py
@@ -1,6 +1,5 @@
# This file was auto-generated by Fern from our API Definition.
-import datetime as dt
import typing
import pydantic
@@ -10,14 +9,14 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class PostKnowledgebaseGetSitemapUrlsResponseData(UncheckedBaseModel):
- urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
+class GetAnalyticsConcurrencyResponseDataDataPointsItem(UncheckedBaseModel):
+ minute: typing.Optional[str] = pydantic.Field(default=None)
"""
- All URLs discovered in the sitemap.
+ Time in HH:MM format
"""
- extracted_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime], FieldMetadata(alias="extractedAt"), pydantic.Field(alias="extractedAt")
+ concurrent_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="concurrentCalls"), pydantic.Field(alias="concurrentCalls")
] = None
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/user/types/get_subscription_user_response.py b/src/smallestai/atoms/analytics/types/get_analytics_summary_response.py
similarity index 67%
rename from src/smallestai/atoms/user/types/get_subscription_user_response.py
rename to src/smallestai/atoms/analytics/types/get_analytics_summary_response.py
index 44ec954f..ae407736 100644
--- a/src/smallestai/atoms/user/types/get_subscription_user_response.py
+++ b/src/smallestai/atoms/analytics/types/get_analytics_summary_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_subscription_user_response_data import GetSubscriptionUserResponseData
+from .get_analytics_summary_response_data import GetAnalyticsSummaryResponseData
-class GetSubscriptionUserResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[GetSubscriptionUserResponseData] = None
+class GetAnalyticsSummaryResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetAnalyticsSummaryResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_analytics_summary_response_data.py b/src/smallestai/atoms/analytics/types/get_analytics_summary_response_data.py
new file mode 100644
index 00000000..cd39cbb1
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_analytics_summary_response_data.py
@@ -0,0 +1,59 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from ...types.analytics_trend_metric import AnalyticsTrendMetric
+
+
+class GetAnalyticsSummaryResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[AnalyticsTrendMetric], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[AnalyticsTrendMetric], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+ avg_duration_ms: typing_extensions.Annotated[
+ typing.Optional[AnalyticsTrendMetric],
+ FieldMetadata(alias="avgDurationMs"),
+ pydantic.Field(alias="avgDurationMs"),
+ ] = None
+ median_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="medianDurationMs"), pydantic.Field(alias="medianDurationMs")
+ ] = None
+ total_duration_ms: typing_extensions.Annotated[
+ typing.Optional[AnalyticsTrendMetric],
+ FieldMetadata(alias="totalDurationMs"),
+ pydantic.Field(alias="totalDurationMs"),
+ ] = None
+ unique_users_reached: typing_extensions.Annotated[
+ typing.Optional[AnalyticsTrendMetric],
+ FieldMetadata(alias="uniqueUsersReached"),
+ pydantic.Field(alias="uniqueUsersReached"),
+ ] = None
+ total_cost: typing_extensions.Annotated[
+ typing.Optional[AnalyticsTrendMetric], FieldMetadata(alias="totalCost"), pydantic.Field(alias="totalCost")
+ ] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+ previous_period: typing_extensions.Annotated[
+ typing.Optional[AnalyticsDateRange],
+ FieldMetadata(alias="previousPeriod"),
+ pydantic.Field(alias="previousPeriod"),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_attempt_cohort_response.py b/src/smallestai/atoms/analytics/types/get_attempt_cohort_response.py
new file mode 100644
index 00000000..08f80270
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_attempt_cohort_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_attempt_cohort_response_data import GetAttemptCohortResponseData
+
+
+class GetAttemptCohortResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetAttemptCohortResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data.py b/src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data.py
new file mode 100644
index 00000000..fcc58e5a
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data.py
@@ -0,0 +1,37 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_attempt_cohort_response_data_cohorts_item import GetAttemptCohortResponseDataCohortsItem
+
+
+class GetAttemptCohortResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ cohorts: typing.Optional[typing.List[GetAttemptCohortResponseDataCohortsItem]] = None
+ total_unique_numbers: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalUniqueNumbers"), pydantic.Field(alias="totalUniqueNumbers")
+ ] = None
+ max_attempts_seen: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="maxAttemptsSeen"), pydantic.Field(alias="maxAttemptsSeen")
+ ] = None
+ avg_attempts_to_pickup: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="avgAttemptsToPickup"), pydantic.Field(alias="avgAttemptsToPickup")
+ ] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data_cohorts_item.py b/src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data_cohorts_item.py
new file mode 100644
index 00000000..481c3b9a
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_attempt_cohort_response_data_cohorts_item.py
@@ -0,0 +1,42 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetAttemptCohortResponseDataCohortsItem(UncheckedBaseModel):
+ attempt_number: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="attemptNumber"), pydantic.Field(alias="attemptNumber")
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+ cumulative_answered: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="cumulativeAnswered"), pydantic.Field(alias="cumulativeAnswered")
+ ] = None
+ cumulative_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="cumulativeRate"), pydantic.Field(alias="cumulativeRate")
+ ] = None
+ marginal_gain: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="marginalGain"), pydantic.Field(alias="marginalGain")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/compliance/types/patch_compliance_applications_id_response.py b/src/smallestai/atoms/analytics/types/get_call_counts_by_day_response.py
similarity index 67%
rename from src/smallestai/atoms/compliance/types/patch_compliance_applications_id_response.py
rename to src/smallestai/atoms/analytics/types/get_call_counts_by_day_response.py
index df0cb29e..3b0d0fe8 100644
--- a/src/smallestai/atoms/compliance/types/patch_compliance_applications_id_response.py
+++ b/src/smallestai/atoms/analytics/types/get_call_counts_by_day_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.compliance_application import ComplianceApplication
+from .get_call_counts_by_day_response_data import GetCallCountsByDayResponseData
-class PatchComplianceApplicationsIdResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[ComplianceApplication] = None
+class GetCallCountsByDayResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetCallCountsByDayResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/organization/types/get_account_details_organization_response_organizations_item.py b/src/smallestai/atoms/analytics/types/get_call_counts_by_day_response_data.py
similarity index 55%
rename from src/smallestai/atoms/organization/types/get_account_details_organization_response_organizations_item.py
rename to src/smallestai/atoms/analytics/types/get_call_counts_by_day_response_data.py
index d58dc456..891302eb 100644
--- a/src/smallestai/atoms/organization/types/get_account_details_organization_response_organizations_item.py
+++ b/src/smallestai/atoms/analytics/types/get_call_counts_by_day_response_data.py
@@ -7,21 +7,19 @@
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.serialization import FieldMetadata
from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_call_counts_by_day_response_data_call_counts_by_day_item import (
+ GetCallCountsByDayResponseDataCallCountsByDayItem,
+)
-class GetAccountDetailsOrganizationResponseOrganizationsItem(UncheckedBaseModel):
+class GetCallCountsByDayResponseData(UncheckedBaseModel):
org_id: typing_extensions.Annotated[
typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
] = None
- role_id: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="roleId"), pydantic.Field(alias="roleId")
- ] = None
- name: typing.Optional[str] = None
- owner_email: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="ownerEmail"), pydantic.Field(alias="ownerEmail")
- ] = None
- on_prem_enabled: typing_extensions.Annotated[
- typing.Optional[bool], FieldMetadata(alias="onPremEnabled"), pydantic.Field(alias="onPremEnabled")
+ call_counts_by_day: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetCallCountsByDayResponseDataCallCountsByDayItem]],
+ FieldMetadata(alias="callCountsByDay"),
+ pydantic.Field(alias="callCountsByDay"),
] = None
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item_options_item.py b/src/smallestai/atoms/analytics/types/get_call_counts_by_day_response_data_call_counts_by_day_item.py
similarity index 73%
rename from src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item_options_item.py
rename to src/smallestai/atoms/analytics/types/get_call_counts_by_day_response_data_call_counts_by_day_item.py
index a1d8e0c8..2466f57b 100644
--- a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response_data_questions_item_options_item.py
+++ b/src/smallestai/atoms/analytics/types/get_call_counts_by_day_response_data_call_counts_by_day_item.py
@@ -1,5 +1,6 @@
# This file was auto-generated by Fern from our API Definition.
+import datetime as dt
import typing
import pydantic
@@ -7,9 +8,9 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class GetPromptConfigAgentsResponseDataQuestionsItemOptionsItem(UncheckedBaseModel):
- value: typing.Optional[str] = None
- label: typing.Optional[str] = None
+class GetCallCountsByDayResponseDataCallCountsByDayItem(UncheckedBaseModel):
+ day: typing.Optional[dt.date] = None
+ count: typing.Optional[float] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_call_counts_log_response.py b/src/smallestai/atoms/analytics/types/get_call_counts_log_response.py
new file mode 100644
index 00000000..62c167d4
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_counts_log_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_call_counts_log_response_data import GetCallCountsLogResponseData
+
+
+class GetCallCountsLogResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetCallCountsLogResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_call_counts_log_response_data.py b/src/smallestai/atoms/analytics/types/get_call_counts_log_response_data.py
new file mode 100644
index 00000000..52f31d23
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_counts_log_response_data.py
@@ -0,0 +1,33 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_call_counts_log_response_data_calls_item import GetCallCountsLogResponseDataCallsItem
+
+
+class GetCallCountsLogResponseData(UncheckedBaseModel):
+ calls: typing.Optional[typing.List[GetCallCountsLogResponseDataCallsItem]] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ total_pages: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalPages"), pydantic.Field(alias="totalPages")
+ ] = None
+ current_page: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="currentPage"), pydantic.Field(alias="currentPage")
+ ] = None
+ limit: typing.Optional[float] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_call_counts_log_response_data_calls_item.py b/src/smallestai/atoms/analytics/types/get_call_counts_log_response_data_calls_item.py
new file mode 100644
index 00000000..629eef55
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_counts_log_response_data_calls_item.py
@@ -0,0 +1,69 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetCallCountsLogResponseDataCallsItem(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ call_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="callId"), pydantic.Field(alias="callId")
+ ] = None
+ agent_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="agentId"), pydantic.Field(alias="agentId")
+ ] = None
+ agent_name: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="agentName"), pydantic.Field(alias="agentName")
+ ] = None
+ campaign_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="campaignId"), pydantic.Field(alias="campaignId")
+ ] = None
+ campaign_name: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="campaignName"), pydantic.Field(alias="campaignName")
+ ] = None
+ call_type: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="callType"), pydantic.Field(alias="callType")
+ ] = None
+ timestamp: typing.Optional[dt.datetime] = None
+ call_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="callDurationMs"), pydantic.Field(alias="callDurationMs")
+ ] = None
+ call_latency_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="callLatencyMs"), pydantic.Field(alias="callLatencyMs")
+ ] = None
+ cost_spent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="costSpent"), pydantic.Field(alias="costSpent")
+ ] = None
+ disconnection_reason: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="disconnectionReason"), pydantic.Field(alias="disconnectionReason")
+ ] = None
+ source: typing.Optional[str] = None
+ recording_url: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="recordingUrl"), pydantic.Field(alias="recordingUrl")
+ ] = None
+ call_status: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="callStatus"), pydantic.Field(alias="callStatus")
+ ] = None
+ from_number: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="fromNumber"), pydantic.Field(alias="fromNumber")
+ ] = None
+ to_number: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="toNumber"), pydantic.Field(alias="toNumber")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response.py b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response.py
new file mode 100644
index 00000000..76b1230e
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_call_outcomes_timeseries_response_data import GetCallOutcomesTimeseriesResponseData
+
+
+class GetCallOutcomesTimeseriesResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetCallOutcomesTimeseriesResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data.py b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data.py
new file mode 100644
index 00000000..228e8839
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_call_outcomes_timeseries_response_data_data_points_item import (
+ GetCallOutcomesTimeseriesResponseDataDataPointsItem,
+)
+from .get_call_outcomes_timeseries_response_data_totals import GetCallOutcomesTimeseriesResponseDataTotals
+
+
+class GetCallOutcomesTimeseriesResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ data_points: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetCallOutcomesTimeseriesResponseDataDataPointsItem]],
+ FieldMetadata(alias="dataPoints"),
+ pydantic.Field(alias="dataPoints"),
+ ] = None
+ totals: typing.Optional[GetCallOutcomesTimeseriesResponseDataTotals] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/audience/types/get_audience_response_data_item_campaigns_item.py b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data_data_points_item.py
similarity index 58%
rename from src/smallestai/atoms/audience/types/get_audience_response_data_item_campaigns_item.py
rename to src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data_data_points_item.py
index 6ba8d9af..9164a23a 100644
--- a/src/smallestai/atoms/audience/types/get_audience_response_data_item_campaigns_item.py
+++ b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data_data_points_item.py
@@ -10,15 +10,15 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class GetAudienceResponseDataItemCampaignsItem(UncheckedBaseModel):
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- name: typing.Optional[str] = None
- status: typing.Optional[str] = None
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime], FieldMetadata(alias="createdAt"), pydantic.Field(alias="createdAt")
+class GetCallOutcomesTimeseriesResponseDataDataPointsItem(UncheckedBaseModel):
+ date: typing.Optional[dt.date] = None
+ answered: typing.Optional[float] = None
+ no_answer: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="noAnswer"), pydantic.Field(alias="noAnswer")
] = None
+ failed: typing.Optional[float] = None
+ cancelled: typing.Optional[float] = None
+ total: typing.Optional[float] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_response_data.py b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data_totals.py
similarity index 60%
rename from src/smallestai/atoms/phone_numbers/types/post_product_rent_number_response_data.py
rename to src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data_totals.py
index 6016d641..03141c5a 100644
--- a/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_response_data.py
+++ b/src/smallestai/atoms/analytics/types/get_call_outcomes_timeseries_response_data_totals.py
@@ -9,15 +9,14 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class PostProductRentNumberResponseData(UncheckedBaseModel):
- requires_action: typing_extensions.Annotated[
- typing.Optional[bool],
- FieldMetadata(alias="requiresAction"),
- pydantic.Field(
- alias="requiresAction",
- description="If `true`, payment requires further customer action (3-D Secure / SCA). Surface the client-secret flow.",
- ),
+class GetCallOutcomesTimeseriesResponseDataTotals(UncheckedBaseModel):
+ answered: typing.Optional[float] = None
+ no_answer: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="noAnswer"), pydantic.Field(alias="noAnswer")
] = None
+ failed: typing.Optional[float] = None
+ cancelled: typing.Optional[float] = None
+ total: typing.Optional[float] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_release_number_response.py b/src/smallestai/atoms/analytics/types/get_call_start_distribution_response.py
similarity index 66%
rename from src/smallestai/atoms/phone_numbers/types/post_product_release_number_response.py
rename to src/smallestai/atoms/analytics/types/get_call_start_distribution_response.py
index 6a3fd4fd..85344657 100644
--- a/src/smallestai/atoms/phone_numbers/types/post_product_release_number_response.py
+++ b/src/smallestai/atoms/analytics/types/get_call_start_distribution_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_product_release_number_response_data import PostProductReleaseNumberResponseData
+from .get_call_start_distribution_response_data import GetCallStartDistributionResponseData
-class PostProductReleaseNumberResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[PostProductReleaseNumberResponseData] = None
+class GetCallStartDistributionResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetCallStartDistributionResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_call_start_distribution_response_data.py b/src/smallestai/atoms/analytics/types/get_call_start_distribution_response_data.py
new file mode 100644
index 00000000..31074a71
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_start_distribution_response_data.py
@@ -0,0 +1,34 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_call_start_distribution_response_data_data_points_item import (
+ GetCallStartDistributionResponseDataDataPointsItem,
+)
+
+
+class GetCallStartDistributionResponseData(UncheckedBaseModel):
+ date: typing.Optional[dt.date] = None
+ data_points: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetCallStartDistributionResponseDataDataPointsItem]],
+ FieldMetadata(alias="dataPoints"),
+ pydantic.Field(alias="dataPoints"),
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_audience.py b/src/smallestai/atoms/analytics/types/get_call_start_distribution_response_data_data_points_item.py
similarity index 64%
rename from src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_audience.py
rename to src/smallestai/atoms/analytics/types/get_call_start_distribution_response_data_data_points_item.py
index f3089858..7494bc65 100644
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_audience.py
+++ b/src/smallestai/atoms/analytics/types/get_call_start_distribution_response_data_data_points_item.py
@@ -9,15 +9,15 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class GetCampaignIdResponseDataCampaignAudience(UncheckedBaseModel):
+class GetCallStartDistributionResponseDataDataPointsItem(UncheckedBaseModel):
+ minute: typing.Optional[str] = pydantic.Field(default=None)
"""
- The audience assigned to this campaign
+ Time in HH:MM format
"""
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- name: typing.Optional[str] = None
+ call_count: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="callCount"), pydantic.Field(alias="callCount")
+ ] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response.py b/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response.py
new file mode 100644
index 00000000..a1cb9f4c
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_call_volume_timeseries_response_data import GetCallVolumeTimeseriesResponseData
+
+
+class GetCallVolumeTimeseriesResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetCallVolumeTimeseriesResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data.py b/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data.py
new file mode 100644
index 00000000..ef372a84
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data.py
@@ -0,0 +1,32 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_call_volume_timeseries_response_data_data_points_item import GetCallVolumeTimeseriesResponseDataDataPointsItem
+
+
+class GetCallVolumeTimeseriesResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ data_points: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetCallVolumeTimeseriesResponseDataDataPointsItem]],
+ FieldMetadata(alias="dataPoints"),
+ pydantic.Field(alias="dataPoints"),
+ ] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data_data_points_item.py b/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data_data_points_item.py
new file mode 100644
index 00000000..dea7c2ad
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_call_volume_timeseries_response_data_data_points_item.py
@@ -0,0 +1,41 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetCallVolumeTimeseriesResponseDataDataPointsItem(UncheckedBaseModel):
+ date: typing.Optional[dt.date] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ no_answer_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="noAnswerCalls"), pydantic.Field(alias="noAnswerCalls")
+ ] = None
+ failed_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="failedCalls"), pydantic.Field(alias="failedCalls")
+ ] = None
+ cancelled_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="cancelledCalls"), pydantic.Field(alias="cancelledCalls")
+ ] = None
+ total_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalDurationMs"), pydantic.Field(alias="totalDurationMs")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_conversation_details_response.py b/src/smallestai/atoms/analytics/types/get_conversation_details_response.py
new file mode 100644
index 00000000..d72d55b6
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_conversation_details_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_conversation_details_response_data import GetConversationDetailsResponseData
+
+
+class GetConversationDetailsResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetConversationDetailsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_conversation_details_response_data.py b/src/smallestai/atoms/analytics/types/get_conversation_details_response_data.py
new file mode 100644
index 00000000..7e4098a2
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_conversation_details_response_data.py
@@ -0,0 +1,46 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_conversation_details_response_data_events_item import GetConversationDetailsResponseDataEventsItem
+from .get_conversation_details_response_data_transcript_item import GetConversationDetailsResponseDataTranscriptItem
+
+
+class GetConversationDetailsResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ call_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="callId"), pydantic.Field(alias="callId")
+ ] = None
+ agent_name: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="agentName"), pydantic.Field(alias="agentName")
+ ] = None
+ workflow_type: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="workflowType"), pydantic.Field(alias="workflowType")
+ ] = None
+ transcript: typing.Optional[typing.List[GetConversationDetailsResponseDataTranscriptItem]] = None
+ events: typing.Optional[typing.List[GetConversationDetailsResponseDataEventsItem]] = None
+ from_number: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="fromNumber"), pydantic.Field(alias="fromNumber")
+ ] = None
+ to_number: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="toNumber"), pydantic.Field(alias="toNumber")
+ ] = None
+ call_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="callDurationMs"), pydantic.Field(alias="callDurationMs")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_audience.py b/src/smallestai/atoms/analytics/types/get_conversation_details_response_data_events_item.py
similarity index 66%
rename from src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_audience.py
rename to src/smallestai/atoms/analytics/types/get_conversation_details_response_data_events_item.py
index eedfdab5..8d858f0d 100644
--- a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_audience.py
+++ b/src/smallestai/atoms/analytics/types/get_conversation_details_response_data_events_item.py
@@ -1,5 +1,6 @@
# This file was auto-generated by Fern from our API Definition.
+import datetime as dt
import typing
import pydantic
@@ -9,15 +10,11 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class GetCampaignResponseDataCampaignsItemAudience(UncheckedBaseModel):
- """
- The audience assigned to this campaign
- """
-
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- name: typing.Optional[str] = None
+class GetConversationDetailsResponseDataEventsItem(UncheckedBaseModel):
+ event_type: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="eventType"), pydantic.Field(alias="eventType")
+ ] = None
+ timestamp: typing.Optional[dt.datetime] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_conversation_details_response_data_transcript_item.py b/src/smallestai/atoms/analytics/types/get_conversation_details_response_data_transcript_item.py
new file mode 100644
index 00000000..e1a42b94
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_conversation_details_response_data_transcript_item.py
@@ -0,0 +1,23 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetConversationDetailsResponseDataTranscriptItem(UncheckedBaseModel):
+ role: typing.Optional[str] = None
+ text: typing.Optional[str] = None
+ timestamp: typing.Optional[dt.datetime] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_daily_call_summary_response.py b/src/smallestai/atoms/analytics/types/get_daily_call_summary_response.py
new file mode 100644
index 00000000..3998e0e8
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_daily_call_summary_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_daily_call_summary_response_data import GetDailyCallSummaryResponseData
+
+
+class GetDailyCallSummaryResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetDailyCallSummaryResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_daily_call_summary_response_data.py b/src/smallestai/atoms/analytics/types/get_daily_call_summary_response_data.py
new file mode 100644
index 00000000..503bf2e6
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_daily_call_summary_response_data.py
@@ -0,0 +1,47 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetDailyCallSummaryResponseData(UncheckedBaseModel):
+ date: typing.Optional[dt.date] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ unanswered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="unansweredCalls"), pydantic.Field(alias="unansweredCalls")
+ ] = None
+ failed_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="failedCalls"), pydantic.Field(alias="failedCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+ in_progress_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="inProgressCalls"), pydantic.Field(alias="inProgressCalls")
+ ] = None
+ in_queue_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="inQueueCalls"), pydantic.Field(alias="inQueueCalls")
+ ] = None
+ total_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalDurationMs"), pydantic.Field(alias="totalDurationMs")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/post_campaign_response.py b/src/smallestai/atoms/analytics/types/get_dashboard_response.py
similarity index 70%
rename from src/smallestai/atoms/campaigns/types/post_campaign_response.py
rename to src/smallestai/atoms/analytics/types/get_dashboard_response.py
index 1bd08cd3..c786848c 100644
--- a/src/smallestai/atoms/campaigns/types/post_campaign_response.py
+++ b/src/smallestai/atoms/analytics/types/get_dashboard_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_campaign_response_data import PostCampaignResponseData
+from .get_dashboard_response_data import GetDashboardResponseData
-class PostCampaignResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[PostCampaignResponseData] = None
+class GetDashboardResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetDashboardResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_dashboard_response_data.py b/src/smallestai/atoms/analytics/types/get_dashboard_response_data.py
new file mode 100644
index 00000000..24973deb
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_dashboard_response_data.py
@@ -0,0 +1,51 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetDashboardResponseData(UncheckedBaseModel):
+ summary: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None)
+ """
+ See GET /analytics/summary
+ """
+
+ call_volume_timeseries: typing_extensions.Annotated[
+ typing.Optional[typing.Dict[str, typing.Any]],
+ FieldMetadata(alias="callVolumeTimeseries"),
+ pydantic.Field(alias="callVolumeTimeseries", description="See GET /analytics/call-volume-timeseries"),
+ ] = None
+ call_outcomes_timeseries: typing_extensions.Annotated[
+ typing.Optional[typing.Dict[str, typing.Any]],
+ FieldMetadata(alias="callOutcomesTimeseries"),
+ pydantic.Field(alias="callOutcomesTimeseries", description="See GET /analytics/call-outcomes-timeseries"),
+ ] = None
+ pickup_rate_by_number: typing_extensions.Annotated[
+ typing.Optional[typing.Dict[str, typing.Any]],
+ FieldMetadata(alias="pickupRateByNumber"),
+ pydantic.Field(alias="pickupRateByNumber", description="See GET /analytics/pickup-rate-by-number"),
+ ] = None
+ hourly_performance: typing_extensions.Annotated[
+ typing.Optional[typing.Dict[str, typing.Any]],
+ FieldMetadata(alias="hourlyPerformance"),
+ pydantic.Field(alias="hourlyPerformance", description="See GET /analytics/hourly-performance"),
+ ] = None
+ duration_stats: typing_extensions.Annotated[
+ typing.Optional[typing.Dict[str, typing.Any]],
+ FieldMetadata(alias="durationStats"),
+ pydantic.Field(alias="durationStats", description="See GET /analytics/duration-stats"),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_duration_stats_response.py b/src/smallestai/atoms/analytics/types/get_duration_stats_response.py
new file mode 100644
index 00000000..024558fd
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_duration_stats_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_duration_stats_response_data import GetDurationStatsResponseData
+
+
+class GetDurationStatsResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetDurationStatsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_duration_stats_response_data.py b/src/smallestai/atoms/analytics/types/get_duration_stats_response_data.py
new file mode 100644
index 00000000..302d6b85
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_duration_stats_response_data.py
@@ -0,0 +1,50 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+
+
+class GetDurationStatsResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ avg_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="avgDurationMs"), pydantic.Field(alias="avgDurationMs")
+ ] = None
+ median_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="medianDurationMs"), pydantic.Field(alias="medianDurationMs")
+ ] = None
+ p90duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="p90DurationMs"), pydantic.Field(alias="p90DurationMs")
+ ] = None
+ p95duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="p95DurationMs"), pydantic.Field(alias="p95DurationMs")
+ ] = None
+ short_calls_percent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="shortCallsPercent"), pydantic.Field(alias="shortCallsPercent")
+ ] = None
+ long_calls_percent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="longCallsPercent"), pydantic.Field(alias="longCallsPercent")
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ total_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalDurationMs"), pydantic.Field(alias="totalDurationMs")
+ ] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_hourly_performance_response.py b/src/smallestai/atoms/analytics/types/get_hourly_performance_response.py
new file mode 100644
index 00000000..253f5f0c
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_hourly_performance_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_hourly_performance_response_data import GetHourlyPerformanceResponseData
+
+
+class GetHourlyPerformanceResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetHourlyPerformanceResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_hourly_performance_response_data.py b/src/smallestai/atoms/analytics/types/get_hourly_performance_response_data.py
new file mode 100644
index 00000000..f016d38c
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_hourly_performance_response_data.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_hourly_performance_response_data_hours_item import GetHourlyPerformanceResponseDataHoursItem
+
+
+class GetHourlyPerformanceResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ hours: typing.Optional[typing.List[GetHourlyPerformanceResponseDataHoursItem]] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_hourly_performance_response_data_hours_item.py b/src/smallestai/atoms/analytics/types/get_hourly_performance_response_data_hours_item.py
new file mode 100644
index 00000000..0e69464a
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_hourly_performance_response_data_hours_item.py
@@ -0,0 +1,41 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetHourlyPerformanceResponseDataHoursItem(UncheckedBaseModel):
+ hour: typing.Optional[int] = pydantic.Field(default=None)
+ """
+ Hour of day (0โ23)
+ """
+
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+ total_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalDurationMs"), pydantic.Field(alias="totalDurationMs")
+ ] = None
+ avg_duration_ms: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="avgDurationMs"), pydantic.Field(alias="avgDurationMs")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_phone_number_trends_response.py b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response.py
new file mode 100644
index 00000000..1819fb1d
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_phone_number_trends_response_data import GetPhoneNumberTrendsResponseData
+
+
+class GetPhoneNumberTrendsResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetPhoneNumberTrendsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data.py b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data.py
new file mode 100644
index 00000000..32553438
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_phone_number_trends_response_data_trends_item import GetPhoneNumberTrendsResponseDataTrendsItem
+
+
+class GetPhoneNumberTrendsResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ trends: typing.Optional[typing.List[GetPhoneNumberTrendsResponseDataTrendsItem]] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_pagination.py b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data_trends_item.py
similarity index 50%
rename from src/smallestai/atoms/campaigns/types/get_campaign_response_data_pagination.py
rename to src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data_trends_item.py
index 689c6d81..cd6a7c53 100644
--- a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_pagination.py
+++ b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data_trends_item.py
@@ -7,17 +7,19 @@
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.serialization import FieldMetadata
from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_phone_number_trends_response_data_trends_item_data_points_item import (
+ GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem,
+)
-class GetCampaignResponseDataPagination(UncheckedBaseModel):
- total: typing.Optional[int] = None
- page: typing.Optional[int] = None
- offset: typing.Optional[int] = None
- has_more: typing_extensions.Annotated[
- typing.Optional[bool], FieldMetadata(alias="hasMore"), pydantic.Field(alias="hasMore")
+class GetPhoneNumberTrendsResponseDataTrendsItem(UncheckedBaseModel):
+ phone_number: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="phoneNumber"), pydantic.Field(alias="phoneNumber")
] = None
- total_pages: typing_extensions.Annotated[
- typing.Optional[int], FieldMetadata(alias="totalPages"), pydantic.Field(alias="totalPages")
+ data_points: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem]],
+ FieldMetadata(alias="dataPoints"),
+ pydantic.Field(alias="dataPoints"),
] = None
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data_trends_item_data_points_item.py b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data_trends_item_data_points_item.py
new file mode 100644
index 00000000..500503e4
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_phone_number_trends_response_data_trends_item_data_points_item.py
@@ -0,0 +1,32 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem(UncheckedBaseModel):
+ date: typing.Optional[dt.date] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response.py b/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response.py
similarity index 67%
rename from src/smallestai/atoms/agents/types/get_prompt_config_agents_response.py
rename to src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response.py
index a13af62f..06a10d84 100644
--- a/src/smallestai/atoms/agents/types/get_prompt_config_agents_response.py
+++ b/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_prompt_config_agents_response_data import GetPromptConfigAgentsResponseData
+from .get_pickup_rate_by_number_response_data import GetPickupRateByNumberResponseData
-class GetPromptConfigAgentsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[GetPromptConfigAgentsResponseData] = None
+class GetPickupRateByNumberResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetPickupRateByNumberResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data.py b/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data.py
new file mode 100644
index 00000000..e3027c1b
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_pickup_rate_by_number_response_data_numbers_item import GetPickupRateByNumberResponseDataNumbersItem
+
+
+class GetPickupRateByNumberResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
+ ] = None
+ numbers: typing.Optional[typing.List[GetPickupRateByNumberResponseDataNumbersItem]] = None
+ period: typing.Optional[AnalyticsDateRange] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data_numbers_item.py b/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data_numbers_item.py
new file mode 100644
index 00000000..eae04f5e
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_pickup_rate_by_number_response_data_numbers_item.py
@@ -0,0 +1,37 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetPickupRateByNumberResponseDataNumbersItem(UncheckedBaseModel):
+ phone_number: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="phoneNumber"), pydantic.Field(alias="phoneNumber")
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+ last_active_date: typing_extensions.Annotated[
+ typing.Optional[dt.date], FieldMetadata(alias="lastActiveDate"), pydantic.Field(alias="lastActiveDate")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/list_call_logs_agents_response.py b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response.py
similarity index 68%
rename from src/smallestai/atoms/agents/types/list_call_logs_agents_response.py
rename to src/smallestai/atoms/analytics/types/get_usage_timeseries_response.py
index 4801d99e..a14a66ab 100644
--- a/src/smallestai/atoms/agents/types/list_call_logs_agents_response.py
+++ b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response.py
@@ -5,12 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
-from .list_call_logs_agents_response_data import ListCallLogsAgentsResponseData
+from .get_usage_timeseries_response_data import GetUsageTimeseriesResponseData
-class ListCallLogsAgentsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[ListCallLogsAgentsResponseData] = None
+class GetUsageTimeseriesResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetUsageTimeseriesResponseData] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data.py b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data.py
new file mode 100644
index 00000000..04458cd0
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_usage_timeseries_response_data_date_range import GetUsageTimeseriesResponseDataDateRange
+from .get_usage_timeseries_response_data_day_wise_credits_item import GetUsageTimeseriesResponseDataDayWiseCreditsItem
+
+
+class GetUsageTimeseriesResponseData(UncheckedBaseModel):
+ date_range: typing_extensions.Annotated[
+ typing.Optional[GetUsageTimeseriesResponseDataDateRange],
+ FieldMetadata(alias="dateRange"),
+ pydantic.Field(alias="dateRange"),
+ ] = None
+ day_wise_credits: typing_extensions.Annotated[
+ typing.Optional[typing.List[GetUsageTimeseriesResponseDataDayWiseCreditsItem]],
+ FieldMetadata(alias="dayWiseCredits"),
+ pydantic.Field(alias="dayWiseCredits"),
+ ] = None
+ total_credits: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCredits"), pydantic.Field(alias="totalCredits")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response_data.py b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data_date_range.py
similarity index 69%
rename from src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response_data.py
rename to src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data_date_range.py
index 5712e712..6e8c4894 100644
--- a/src/smallestai/atoms/agent_versioning_drafts/types/patch_agent_id_drafts_draft_id_response_data.py
+++ b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data_date_range.py
@@ -1,5 +1,6 @@
# This file was auto-generated by Fern from our API Definition.
+import datetime as dt
import typing
import pydantic
@@ -9,10 +10,11 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class PatchAgentIdDraftsDraftIdResponseData(UncheckedBaseModel):
- draft_name: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="draftName"), pydantic.Field(alias="draftName")
+class GetUsageTimeseriesResponseDataDateRange(UncheckedBaseModel):
+ from_: typing_extensions.Annotated[
+ typing.Optional[dt.date], FieldMetadata(alias="from"), pydantic.Field(alias="from")
] = None
+ to: typing.Optional[dt.date] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data_day_wise_credits_item.py b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data_day_wise_credits_item.py
new file mode 100644
index 00000000..5c821125
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_usage_timeseries_response_data_day_wise_credits_item.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetUsageTimeseriesResponseDataDayWiseCreditsItem(UncheckedBaseModel):
+ date: typing.Optional[dt.date] = None
+ credits: typing.Optional[float] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/analytics/types/get_weekly_trends_response.py b/src/smallestai/atoms/analytics/types/get_weekly_trends_response.py
new file mode 100644
index 00000000..f83255a3
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_weekly_trends_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_weekly_trends_response_data import GetWeeklyTrendsResponseData
+
+
+class GetWeeklyTrendsResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetWeeklyTrendsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_agent.py b/src/smallestai/atoms/analytics/types/get_weekly_trends_response_data.py
similarity index 55%
rename from src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_agent.py
rename to src/smallestai/atoms/analytics/types/get_weekly_trends_response_data.py
index a3191f29..4fdc4e47 100644
--- a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_agent.py
+++ b/src/smallestai/atoms/analytics/types/get_weekly_trends_response_data.py
@@ -7,20 +7,16 @@
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.serialization import FieldMetadata
from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.analytics_date_range import AnalyticsDateRange
+from .get_weekly_trends_response_data_weeks_item import GetWeeklyTrendsResponseDataWeeksItem
-class GetCampaignResponseDataCampaignsItemAgent(UncheckedBaseModel):
- """
- The agent assigned to this campaign
- """
-
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- name: typing.Optional[str] = None
- workflow_type: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="workflowType"), pydantic.Field(alias="workflowType")
+class GetWeeklyTrendsResponseData(UncheckedBaseModel):
+ org_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="orgId"), pydantic.Field(alias="orgId")
] = None
+ weeks: typing.Optional[typing.List[GetWeeklyTrendsResponseDataWeeksItem]] = None
+ period: typing.Optional[AnalyticsDateRange] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/analytics/types/get_weekly_trends_response_data_weeks_item.py b/src/smallestai/atoms/analytics/types/get_weekly_trends_response_data_weeks_item.py
new file mode 100644
index 00000000..77dcaf69
--- /dev/null
+++ b/src/smallestai/atoms/analytics/types/get_weekly_trends_response_data_weeks_item.py
@@ -0,0 +1,57 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetWeeklyTrendsResponseDataWeeksItem(UncheckedBaseModel):
+ week_start: typing_extensions.Annotated[
+ typing.Optional[dt.date],
+ FieldMetadata(alias="weekStart"),
+ pydantic.Field(alias="weekStart", description="Monday that starts this week"),
+ ] = None
+ total_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="totalCalls"), pydantic.Field(alias="totalCalls")
+ ] = None
+ answered_calls: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="answeredCalls"), pydantic.Field(alias="answeredCalls")
+ ] = None
+ pickup_rate: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="pickupRate"), pydantic.Field(alias="pickupRate")
+ ] = None
+ avg_duration: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="avgDuration"),
+ pydantic.Field(alias="avgDuration", description="Average call duration in seconds"),
+ ] = None
+ median_duration: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="medianDuration"),
+ pydantic.Field(alias="medianDuration", description="Median call duration in seconds"),
+ ] = None
+ p90duration: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="p90Duration"),
+ pydantic.Field(alias="p90Duration", description="p90 call duration in seconds"),
+ ] = None
+ short_calls_percent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="shortCallsPercent"), pydantic.Field(alias="shortCallsPercent")
+ ] = None
+ long_calls_percent: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="longCallsPercent"), pydantic.Field(alias="longCallsPercent")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/audience/__init__.py b/src/smallestai/atoms/audience/__init__.py
index 5cde0202..d5f50917 100644
--- a/src/smallestai/atoms/audience/__init__.py
+++ b/src/smallestai/atoms/audience/__init__.py
@@ -2,3 +2,99 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ DeleteAudienceIdMembersResponse,
+ DeleteAudienceIdMembersResponseData,
+ DeleteAudienceIdResponse,
+ GetAudienceIdMembersResponse,
+ GetAudienceIdMembersResponseData,
+ GetAudienceIdMembersResponseDataMembersItem,
+ GetAudienceIdMembersSearchResponse,
+ GetAudienceIdMembersSearchResponseData,
+ GetAudienceIdMembersSearchResponseDataMembersItem,
+ GetAudienceIdMembersSearchResponseDataSearchInfo,
+ GetAudienceIdMembersSearchResponseDataSearchInfoSearchType,
+ GetAudienceResponse,
+ GetAudienceResponseData,
+ ListAudienceResponse,
+ ListAudienceResponseDataItem,
+ ListAudienceResponseDataItemCampaignsItem,
+ PostAudienceIdMembersResponse,
+ PostAudienceIdMembersResponseDataItem,
+ PostAudienceIdMembersResponseDataItemData,
+ PostAudienceResponse,
+ PostAudienceResponseData,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "DeleteAudienceIdMembersResponse": ".types",
+ "DeleteAudienceIdMembersResponseData": ".types",
+ "DeleteAudienceIdResponse": ".types",
+ "GetAudienceIdMembersResponse": ".types",
+ "GetAudienceIdMembersResponseData": ".types",
+ "GetAudienceIdMembersResponseDataMembersItem": ".types",
+ "GetAudienceIdMembersSearchResponse": ".types",
+ "GetAudienceIdMembersSearchResponseData": ".types",
+ "GetAudienceIdMembersSearchResponseDataMembersItem": ".types",
+ "GetAudienceIdMembersSearchResponseDataSearchInfo": ".types",
+ "GetAudienceIdMembersSearchResponseDataSearchInfoSearchType": ".types",
+ "GetAudienceResponse": ".types",
+ "GetAudienceResponseData": ".types",
+ "ListAudienceResponse": ".types",
+ "ListAudienceResponseDataItem": ".types",
+ "ListAudienceResponseDataItemCampaignsItem": ".types",
+ "PostAudienceIdMembersResponse": ".types",
+ "PostAudienceIdMembersResponseDataItem": ".types",
+ "PostAudienceIdMembersResponseDataItemData": ".types",
+ "PostAudienceResponse": ".types",
+ "PostAudienceResponseData": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "DeleteAudienceIdMembersResponse",
+ "DeleteAudienceIdMembersResponseData",
+ "DeleteAudienceIdResponse",
+ "GetAudienceIdMembersResponse",
+ "GetAudienceIdMembersResponseData",
+ "GetAudienceIdMembersResponseDataMembersItem",
+ "GetAudienceIdMembersSearchResponse",
+ "GetAudienceIdMembersSearchResponseData",
+ "GetAudienceIdMembersSearchResponseDataMembersItem",
+ "GetAudienceIdMembersSearchResponseDataSearchInfo",
+ "GetAudienceIdMembersSearchResponseDataSearchInfoSearchType",
+ "GetAudienceResponse",
+ "GetAudienceResponseData",
+ "ListAudienceResponse",
+ "ListAudienceResponseDataItem",
+ "ListAudienceResponseDataItemCampaignsItem",
+ "PostAudienceIdMembersResponse",
+ "PostAudienceIdMembersResponseDataItem",
+ "PostAudienceIdMembersResponseDataItemData",
+ "PostAudienceResponse",
+ "PostAudienceResponseData",
+]
diff --git a/src/smallestai/atoms/audience/types/__init__.py b/src/smallestai/atoms/audience/types/__init__.py
index 5cde0202..4907350f 100644
--- a/src/smallestai/atoms/audience/types/__init__.py
+++ b/src/smallestai/atoms/audience/types/__init__.py
@@ -2,3 +2,103 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .delete_audience_id_members_response import DeleteAudienceIdMembersResponse
+ from .delete_audience_id_members_response_data import DeleteAudienceIdMembersResponseData
+ from .delete_audience_id_response import DeleteAudienceIdResponse
+ from .get_audience_id_members_response import GetAudienceIdMembersResponse
+ from .get_audience_id_members_response_data import GetAudienceIdMembersResponseData
+ from .get_audience_id_members_response_data_members_item import GetAudienceIdMembersResponseDataMembersItem
+ from .get_audience_id_members_search_response import GetAudienceIdMembersSearchResponse
+ from .get_audience_id_members_search_response_data import GetAudienceIdMembersSearchResponseData
+ from .get_audience_id_members_search_response_data_members_item import (
+ GetAudienceIdMembersSearchResponseDataMembersItem,
+ )
+ from .get_audience_id_members_search_response_data_search_info import (
+ GetAudienceIdMembersSearchResponseDataSearchInfo,
+ )
+ from .get_audience_id_members_search_response_data_search_info_search_type import (
+ GetAudienceIdMembersSearchResponseDataSearchInfoSearchType,
+ )
+ from .get_audience_response import GetAudienceResponse
+ from .get_audience_response_data import GetAudienceResponseData
+ from .list_audience_response import ListAudienceResponse
+ from .list_audience_response_data_item import ListAudienceResponseDataItem
+ from .list_audience_response_data_item_campaigns_item import ListAudienceResponseDataItemCampaignsItem
+ from .post_audience_id_members_response import PostAudienceIdMembersResponse
+ from .post_audience_id_members_response_data_item import PostAudienceIdMembersResponseDataItem
+ from .post_audience_id_members_response_data_item_data import PostAudienceIdMembersResponseDataItemData
+ from .post_audience_response import PostAudienceResponse
+ from .post_audience_response_data import PostAudienceResponseData
+_dynamic_imports: typing.Dict[str, str] = {
+ "DeleteAudienceIdMembersResponse": ".delete_audience_id_members_response",
+ "DeleteAudienceIdMembersResponseData": ".delete_audience_id_members_response_data",
+ "DeleteAudienceIdResponse": ".delete_audience_id_response",
+ "GetAudienceIdMembersResponse": ".get_audience_id_members_response",
+ "GetAudienceIdMembersResponseData": ".get_audience_id_members_response_data",
+ "GetAudienceIdMembersResponseDataMembersItem": ".get_audience_id_members_response_data_members_item",
+ "GetAudienceIdMembersSearchResponse": ".get_audience_id_members_search_response",
+ "GetAudienceIdMembersSearchResponseData": ".get_audience_id_members_search_response_data",
+ "GetAudienceIdMembersSearchResponseDataMembersItem": ".get_audience_id_members_search_response_data_members_item",
+ "GetAudienceIdMembersSearchResponseDataSearchInfo": ".get_audience_id_members_search_response_data_search_info",
+ "GetAudienceIdMembersSearchResponseDataSearchInfoSearchType": ".get_audience_id_members_search_response_data_search_info_search_type",
+ "GetAudienceResponse": ".get_audience_response",
+ "GetAudienceResponseData": ".get_audience_response_data",
+ "ListAudienceResponse": ".list_audience_response",
+ "ListAudienceResponseDataItem": ".list_audience_response_data_item",
+ "ListAudienceResponseDataItemCampaignsItem": ".list_audience_response_data_item_campaigns_item",
+ "PostAudienceIdMembersResponse": ".post_audience_id_members_response",
+ "PostAudienceIdMembersResponseDataItem": ".post_audience_id_members_response_data_item",
+ "PostAudienceIdMembersResponseDataItemData": ".post_audience_id_members_response_data_item_data",
+ "PostAudienceResponse": ".post_audience_response",
+ "PostAudienceResponseData": ".post_audience_response_data",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "DeleteAudienceIdMembersResponse",
+ "DeleteAudienceIdMembersResponseData",
+ "DeleteAudienceIdResponse",
+ "GetAudienceIdMembersResponse",
+ "GetAudienceIdMembersResponseData",
+ "GetAudienceIdMembersResponseDataMembersItem",
+ "GetAudienceIdMembersSearchResponse",
+ "GetAudienceIdMembersSearchResponseData",
+ "GetAudienceIdMembersSearchResponseDataMembersItem",
+ "GetAudienceIdMembersSearchResponseDataSearchInfo",
+ "GetAudienceIdMembersSearchResponseDataSearchInfoSearchType",
+ "GetAudienceResponse",
+ "GetAudienceResponseData",
+ "ListAudienceResponse",
+ "ListAudienceResponseDataItem",
+ "ListAudienceResponseDataItemCampaignsItem",
+ "PostAudienceIdMembersResponse",
+ "PostAudienceIdMembersResponseDataItem",
+ "PostAudienceIdMembersResponseDataItemData",
+ "PostAudienceResponse",
+ "PostAudienceResponseData",
+]
diff --git a/src/smallestai/atoms/audience/types/get_audience_id_response.py b/src/smallestai/atoms/audience/types/get_audience_id_response.py
deleted file mode 100644
index 4ef8dbd9..00000000
--- a/src/smallestai/atoms/audience/types/get_audience_id_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_audience_id_response_data import GetAudienceIdResponseData
-
-
-class GetAudienceIdResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[GetAudienceIdResponseData] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/audience/types/get_audience_id_response_data.py b/src/smallestai/atoms/audience/types/get_audience_id_response_data.py
deleted file mode 100644
index 49a8675b..00000000
--- a/src/smallestai/atoms/audience/types/get_audience_id_response_data.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class GetAudienceIdResponseData(UncheckedBaseModel):
- id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="_id"),
- pydantic.Field(alias="_id", description="The unique identifier for the audience"),
- ] = None
- name: typing.Optional[str] = pydantic.Field(default=None)
- """
- The name of the audience
- """
-
- description: typing.Optional[str] = pydantic.Field(default=None)
- """
- The description of the audience
- """
-
- phone_number_column_name: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="phoneNumberColumnName"),
- pydantic.Field(
- alias="phoneNumberColumnName", description="The name of the column in the CSV that contains phone numbers"
- ),
- ] = None
- organization: typing.Optional[str] = pydantic.Field(default=None)
- """
- The organization ID
- """
-
- created_by: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="createdBy"),
- pydantic.Field(alias="createdBy", description="The user ID who created the audience"),
- ] = None
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="The date and time when the audience was created"),
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="updatedAt"),
- pydantic.Field(alias="updatedAt", description="The date and time when the audience was last updated"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/audience/types/get_audience_response_data_item.py b/src/smallestai/atoms/audience/types/get_audience_response_data_item.py
deleted file mode 100644
index e7df5029..00000000
--- a/src/smallestai/atoms/audience/types/get_audience_response_data_item.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_audience_response_data_item_campaigns_item import GetAudienceResponseDataItemCampaignsItem
-
-
-class GetAudienceResponseDataItem(UncheckedBaseModel):
- id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="_id"),
- pydantic.Field(alias="_id", description="The unique identifier for the audience"),
- ] = None
- name: typing.Optional[str] = pydantic.Field(default=None)
- """
- The name of the audience
- """
-
- description: typing.Optional[str] = pydantic.Field(default=None)
- """
- The description of the audience
- """
-
- phone_number_column_name: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="phoneNumberColumnName"),
- pydantic.Field(
- alias="phoneNumberColumnName", description="The name of the column in the CSV that contains phone numbers"
- ),
- ] = None
- organization: typing.Optional[str] = pydantic.Field(default=None)
- """
- The organization ID
- """
-
- created_by: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="createdBy"),
- pydantic.Field(alias="createdBy", description="The user ID who created the audience"),
- ] = None
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="The date and time when the audience was created"),
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="updatedAt"),
- pydantic.Field(alias="updatedAt", description="The date and time when the audience was last updated"),
- ] = None
- member_count: typing_extensions.Annotated[
- typing.Optional[float],
- FieldMetadata(alias="memberCount"),
- pydantic.Field(alias="memberCount", description="Current number of members in the audience"),
- ] = None
- has_campaigns: typing_extensions.Annotated[
- typing.Optional[bool],
- FieldMetadata(alias="hasCampaigns"),
- pydantic.Field(alias="hasCampaigns", description="Whether any campaigns are currently using this audience"),
- ] = None
- campaigns: typing.Optional[typing.List[GetAudienceResponseDataItemCampaignsItem]] = pydantic.Field(default=None)
- """
- Active campaigns using this audience
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/call_actions/__init__.py b/src/smallestai/atoms/call_actions/__init__.py
new file mode 100644
index 00000000..ba8b1f53
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/__init__.py
@@ -0,0 +1,79 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ CreateCallActionRequestActionType,
+ CreateCallActionRequestCategory,
+ CreateCallActionRequestConfig,
+ CreateCallActionResponse,
+ DeleteCallActionResponse,
+ DeleteCallActionResponseData,
+ GetCallActionResponse,
+ ListCallActionsRequestCategory,
+ ListCallActionsResponse,
+ ListCallActionsResponseData,
+ UpdateCallActionRequestActionType,
+ UpdateCallActionRequestCategory,
+ UpdateCallActionRequestConfig,
+ UpdateCallActionResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateCallActionRequestActionType": ".types",
+ "CreateCallActionRequestCategory": ".types",
+ "CreateCallActionRequestConfig": ".types",
+ "CreateCallActionResponse": ".types",
+ "DeleteCallActionResponse": ".types",
+ "DeleteCallActionResponseData": ".types",
+ "GetCallActionResponse": ".types",
+ "ListCallActionsRequestCategory": ".types",
+ "ListCallActionsResponse": ".types",
+ "ListCallActionsResponseData": ".types",
+ "UpdateCallActionRequestActionType": ".types",
+ "UpdateCallActionRequestCategory": ".types",
+ "UpdateCallActionRequestConfig": ".types",
+ "UpdateCallActionResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateCallActionRequestActionType",
+ "CreateCallActionRequestCategory",
+ "CreateCallActionRequestConfig",
+ "CreateCallActionResponse",
+ "DeleteCallActionResponse",
+ "DeleteCallActionResponseData",
+ "GetCallActionResponse",
+ "ListCallActionsRequestCategory",
+ "ListCallActionsResponse",
+ "ListCallActionsResponseData",
+ "UpdateCallActionRequestActionType",
+ "UpdateCallActionRequestCategory",
+ "UpdateCallActionRequestConfig",
+ "UpdateCallActionResponse",
+]
diff --git a/src/smallestai/atoms/call_actions/client.py b/src/smallestai/atoms/call_actions/client.py
new file mode 100644
index 00000000..e3555e19
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/client.py
@@ -0,0 +1,610 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from .raw_client import AsyncRawCallActionsClient, RawCallActionsClient
+from .types.create_call_action_request_action_type import CreateCallActionRequestActionType
+from .types.create_call_action_request_category import CreateCallActionRequestCategory
+from .types.create_call_action_request_config import CreateCallActionRequestConfig
+from .types.create_call_action_response import CreateCallActionResponse
+from .types.delete_call_action_response import DeleteCallActionResponse
+from .types.get_call_action_response import GetCallActionResponse
+from .types.list_call_actions_request_category import ListCallActionsRequestCategory
+from .types.list_call_actions_response import ListCallActionsResponse
+from .types.update_call_action_request_action_type import UpdateCallActionRequestActionType
+from .types.update_call_action_request_category import UpdateCallActionRequestCategory
+from .types.update_call_action_request_config import UpdateCallActionRequestConfig
+from .types.update_call_action_response import UpdateCallActionResponse
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class CallActionsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawCallActionsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawCallActionsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawCallActionsClient
+ """
+ return self._raw_client
+
+ def list_call_actions(
+ self,
+ *,
+ agent_id: str,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ category: typing.Optional[ListCallActionsRequestCategory] = None,
+ provider: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ListCallActionsResponse:
+ """
+ Returns a paginated list of call actions for the organization, filtered by agent. Optionally filter by category or provider.
+
+ Parameters
+ ----------
+ agent_id : str
+ Filter by agent (ObjectId)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ category : typing.Optional[ListCallActionsRequestCategory]
+ Filter by category
+
+ provider : typing.Optional[str]
+ Filter by provider name
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListCallActionsResponse
+ Paginated list of call actions
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.call_actions.list_call_actions(
+ agent_id="agentId",
+ )
+ """
+ _response = self._raw_client.list_call_actions(
+ agent_id=agent_id,
+ page=page,
+ limit=limit,
+ category=category,
+ provider=provider,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def create_call_action(
+ self,
+ *,
+ agent_id: str,
+ category: CreateCallActionRequestCategory,
+ provider: str,
+ config: CreateCallActionRequestConfig,
+ action_type: typing.Optional[CreateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> CreateCallActionResponse:
+ """
+ Creates a new call action for an agent. Call actions define automated behaviors that fire at specific points in a call lifecycle.
+
+ - **`trigger`** actions fire to initiate an outbound call and require `config.phoneNumberFieldName`.
+ - **`post-call`** actions fire after a call ends (e.g. to update a CRM record).
+
+ Parameters
+ ----------
+ agent_id : str
+ Agent this action belongs to (ObjectId)
+
+ category : CreateCallActionRequestCategory
+ When the action fires
+
+ provider : str
+ Integration provider (e.g. `hubspot`, `salesforce`)
+
+ config : CreateCallActionRequestConfig
+
+ action_type : typing.Optional[CreateCallActionRequestActionType]
+ The operation to perform on the provider object
+
+ object : typing.Optional[str]
+ Provider object type to act on (e.g. `contact`, `deal`)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CreateCallActionResponse
+ Call action created
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+ from smallestai.atoms.call_actions import CreateCallActionRequestConfig
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.call_actions.create_call_action(
+ agent_id="agentId",
+ category="trigger",
+ provider="provider",
+ config=CreateCallActionRequestConfig(),
+ )
+ """
+ _response = self._raw_client.create_call_action(
+ agent_id=agent_id,
+ category=category,
+ provider=provider,
+ config=config,
+ action_type=action_type,
+ object=object,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetCallActionResponse:
+ """
+ Returns a single call action by ID. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallActionResponse
+ Call action
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.call_actions.get_call_action(
+ id="id",
+ )
+ """
+ _response = self._raw_client.get_call_action(id, request_options=request_options)
+ return _response.data
+
+ def update_call_action(
+ self,
+ id: str,
+ *,
+ category: typing.Optional[UpdateCallActionRequestCategory] = OMIT,
+ provider: typing.Optional[str] = OMIT,
+ action_type: typing.Optional[UpdateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ config: typing.Optional[UpdateCallActionRequestConfig] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UpdateCallActionResponse:
+ """
+ Updates an existing call action. All body fields are optional โ only provided fields are updated.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ category : typing.Optional[UpdateCallActionRequestCategory]
+ Change when the action fires
+
+ provider : typing.Optional[str]
+ Change the integration provider
+
+ action_type : typing.Optional[UpdateCallActionRequestActionType]
+ Change the operation type
+
+ object : typing.Optional[str]
+ Change the provider object type
+
+ config : typing.Optional[UpdateCallActionRequestConfig]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UpdateCallActionResponse
+ Updated call action
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.call_actions.update_call_action(
+ id="id",
+ )
+ """
+ _response = self._raw_client.update_call_action(
+ id,
+ category=category,
+ provider=provider,
+ action_type=action_type,
+ object=object,
+ config=config,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def delete_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> DeleteCallActionResponse:
+ """
+ Permanently deletes a call action. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DeleteCallActionResponse
+ Call action deleted
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.call_actions.delete_call_action(
+ id="id",
+ )
+ """
+ _response = self._raw_client.delete_call_action(id, request_options=request_options)
+ return _response.data
+
+
+class AsyncCallActionsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawCallActionsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawCallActionsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawCallActionsClient
+ """
+ return self._raw_client
+
+ async def list_call_actions(
+ self,
+ *,
+ agent_id: str,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ category: typing.Optional[ListCallActionsRequestCategory] = None,
+ provider: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ListCallActionsResponse:
+ """
+ Returns a paginated list of call actions for the organization, filtered by agent. Optionally filter by category or provider.
+
+ Parameters
+ ----------
+ agent_id : str
+ Filter by agent (ObjectId)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ category : typing.Optional[ListCallActionsRequestCategory]
+ Filter by category
+
+ provider : typing.Optional[str]
+ Filter by provider name
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListCallActionsResponse
+ Paginated list of call actions
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.call_actions.list_call_actions(
+ agent_id="agentId",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.list_call_actions(
+ agent_id=agent_id,
+ page=page,
+ limit=limit,
+ category=category,
+ provider=provider,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def create_call_action(
+ self,
+ *,
+ agent_id: str,
+ category: CreateCallActionRequestCategory,
+ provider: str,
+ config: CreateCallActionRequestConfig,
+ action_type: typing.Optional[CreateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> CreateCallActionResponse:
+ """
+ Creates a new call action for an agent. Call actions define automated behaviors that fire at specific points in a call lifecycle.
+
+ - **`trigger`** actions fire to initiate an outbound call and require `config.phoneNumberFieldName`.
+ - **`post-call`** actions fire after a call ends (e.g. to update a CRM record).
+
+ Parameters
+ ----------
+ agent_id : str
+ Agent this action belongs to (ObjectId)
+
+ category : CreateCallActionRequestCategory
+ When the action fires
+
+ provider : str
+ Integration provider (e.g. `hubspot`, `salesforce`)
+
+ config : CreateCallActionRequestConfig
+
+ action_type : typing.Optional[CreateCallActionRequestActionType]
+ The operation to perform on the provider object
+
+ object : typing.Optional[str]
+ Provider object type to act on (e.g. `contact`, `deal`)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CreateCallActionResponse
+ Call action created
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+ from smallestai.atoms.call_actions import CreateCallActionRequestConfig
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.call_actions.create_call_action(
+ agent_id="agentId",
+ category="trigger",
+ provider="provider",
+ config=CreateCallActionRequestConfig(),
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.create_call_action(
+ agent_id=agent_id,
+ category=category,
+ provider=provider,
+ config=config,
+ action_type=action_type,
+ object=object,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetCallActionResponse:
+ """
+ Returns a single call action by ID. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetCallActionResponse
+ Call action
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.call_actions.get_call_action(
+ id="id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_call_action(id, request_options=request_options)
+ return _response.data
+
+ async def update_call_action(
+ self,
+ id: str,
+ *,
+ category: typing.Optional[UpdateCallActionRequestCategory] = OMIT,
+ provider: typing.Optional[str] = OMIT,
+ action_type: typing.Optional[UpdateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ config: typing.Optional[UpdateCallActionRequestConfig] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UpdateCallActionResponse:
+ """
+ Updates an existing call action. All body fields are optional โ only provided fields are updated.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ category : typing.Optional[UpdateCallActionRequestCategory]
+ Change when the action fires
+
+ provider : typing.Optional[str]
+ Change the integration provider
+
+ action_type : typing.Optional[UpdateCallActionRequestActionType]
+ Change the operation type
+
+ object : typing.Optional[str]
+ Change the provider object type
+
+ config : typing.Optional[UpdateCallActionRequestConfig]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UpdateCallActionResponse
+ Updated call action
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.call_actions.update_call_action(
+ id="id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.update_call_action(
+ id,
+ category=category,
+ provider=provider,
+ action_type=action_type,
+ object=object,
+ config=config,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def delete_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> DeleteCallActionResponse:
+ """
+ Permanently deletes a call action. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ DeleteCallActionResponse
+ Call action deleted
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.call_actions.delete_call_action(
+ id="id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.delete_call_action(id, request_options=request_options)
+ return _response.data
diff --git a/src/smallestai/atoms/call_actions/raw_client.py b/src/smallestai/atoms/call_actions/raw_client.py
new file mode 100644
index 00000000..ddaab081
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/raw_client.py
@@ -0,0 +1,1079 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.jsonable_encoder import encode_path_param
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.serialization import convert_and_respect_annotation_metadata
+from ...core.unchecked_base_model import construct_type
+from ..errors.bad_request_error import BadRequestError
+from ..errors.internal_server_error import InternalServerError
+from ..errors.not_found_error import NotFoundError
+from ..errors.unauthorized_error import UnauthorizedError
+from .types.create_call_action_request_action_type import CreateCallActionRequestActionType
+from .types.create_call_action_request_category import CreateCallActionRequestCategory
+from .types.create_call_action_request_config import CreateCallActionRequestConfig
+from .types.create_call_action_response import CreateCallActionResponse
+from .types.delete_call_action_response import DeleteCallActionResponse
+from .types.get_call_action_response import GetCallActionResponse
+from .types.list_call_actions_request_category import ListCallActionsRequestCategory
+from .types.list_call_actions_response import ListCallActionsResponse
+from .types.update_call_action_request_action_type import UpdateCallActionRequestActionType
+from .types.update_call_action_request_category import UpdateCallActionRequestCategory
+from .types.update_call_action_request_config import UpdateCallActionRequestConfig
+from .types.update_call_action_response import UpdateCallActionResponse
+from pydantic import ValidationError
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class RawCallActionsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def list_call_actions(
+ self,
+ *,
+ agent_id: str,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ category: typing.Optional[ListCallActionsRequestCategory] = None,
+ provider: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[ListCallActionsResponse]:
+ """
+ Returns a paginated list of call actions for the organization, filtered by agent. Optionally filter by category or provider.
+
+ Parameters
+ ----------
+ agent_id : str
+ Filter by agent (ObjectId)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ category : typing.Optional[ListCallActionsRequestCategory]
+ Filter by category
+
+ provider : typing.Optional[str]
+ Filter by provider name
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[ListCallActionsResponse]
+ Paginated list of call actions
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "call-actions",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "page": page,
+ "limit": limit,
+ "category": category,
+ "provider": provider,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListCallActionsResponse,
+ construct_type(
+ type_=ListCallActionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def create_call_action(
+ self,
+ *,
+ agent_id: str,
+ category: CreateCallActionRequestCategory,
+ provider: str,
+ config: CreateCallActionRequestConfig,
+ action_type: typing.Optional[CreateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[CreateCallActionResponse]:
+ """
+ Creates a new call action for an agent. Call actions define automated behaviors that fire at specific points in a call lifecycle.
+
+ - **`trigger`** actions fire to initiate an outbound call and require `config.phoneNumberFieldName`.
+ - **`post-call`** actions fire after a call ends (e.g. to update a CRM record).
+
+ Parameters
+ ----------
+ agent_id : str
+ Agent this action belongs to (ObjectId)
+
+ category : CreateCallActionRequestCategory
+ When the action fires
+
+ provider : str
+ Integration provider (e.g. `hubspot`, `salesforce`)
+
+ config : CreateCallActionRequestConfig
+
+ action_type : typing.Optional[CreateCallActionRequestActionType]
+ The operation to perform on the provider object
+
+ object : typing.Optional[str]
+ Provider object type to act on (e.g. `contact`, `deal`)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[CreateCallActionResponse]
+ Call action created
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "call-actions",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "agentId": agent_id,
+ "category": category,
+ "provider": provider,
+ "action_type": action_type,
+ "object": object,
+ "config": convert_and_respect_annotation_metadata(
+ object_=config, annotation=CreateCallActionRequestConfig, direction="write"
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ CreateCallActionResponse,
+ construct_type(
+ type_=CreateCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetCallActionResponse]:
+ """
+ Returns a single call action by ID. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetCallActionResponse]
+ Call action
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"call-actions/{encode_path_param(id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallActionResponse,
+ construct_type(
+ type_=GetCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def update_call_action(
+ self,
+ id: str,
+ *,
+ category: typing.Optional[UpdateCallActionRequestCategory] = OMIT,
+ provider: typing.Optional[str] = OMIT,
+ action_type: typing.Optional[UpdateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ config: typing.Optional[UpdateCallActionRequestConfig] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[UpdateCallActionResponse]:
+ """
+ Updates an existing call action. All body fields are optional โ only provided fields are updated.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ category : typing.Optional[UpdateCallActionRequestCategory]
+ Change when the action fires
+
+ provider : typing.Optional[str]
+ Change the integration provider
+
+ action_type : typing.Optional[UpdateCallActionRequestActionType]
+ Change the operation type
+
+ object : typing.Optional[str]
+ Change the provider object type
+
+ config : typing.Optional[UpdateCallActionRequestConfig]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[UpdateCallActionResponse]
+ Updated call action
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"call-actions/{encode_path_param(id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PUT",
+ json={
+ "category": category,
+ "provider": provider,
+ "action_type": action_type,
+ "object": object,
+ "config": convert_and_respect_annotation_metadata(
+ object_=config, annotation=UpdateCallActionRequestConfig, direction="write"
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ UpdateCallActionResponse,
+ construct_type(
+ type_=UpdateCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def delete_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[DeleteCallActionResponse]:
+ """
+ Permanently deletes a call action. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[DeleteCallActionResponse]
+ Call action deleted
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"call-actions/{encode_path_param(id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="DELETE",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ DeleteCallActionResponse,
+ construct_type(
+ type_=DeleteCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawCallActionsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def list_call_actions(
+ self,
+ *,
+ agent_id: str,
+ page: typing.Optional[int] = None,
+ limit: typing.Optional[int] = None,
+ category: typing.Optional[ListCallActionsRequestCategory] = None,
+ provider: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ListCallActionsResponse]:
+ """
+ Returns a paginated list of call actions for the organization, filtered by agent. Optionally filter by category or provider.
+
+ Parameters
+ ----------
+ agent_id : str
+ Filter by agent (ObjectId)
+
+ page : typing.Optional[int]
+ Page number (default 1)
+
+ limit : typing.Optional[int]
+ Records per page (default 10)
+
+ category : typing.Optional[ListCallActionsRequestCategory]
+ Filter by category
+
+ provider : typing.Optional[str]
+ Filter by provider name
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ListCallActionsResponse]
+ Paginated list of call actions
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "call-actions",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ params={
+ "agentId": agent_id,
+ "page": page,
+ "limit": limit,
+ "category": category,
+ "provider": provider,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListCallActionsResponse,
+ construct_type(
+ type_=ListCallActionsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def create_call_action(
+ self,
+ *,
+ agent_id: str,
+ category: CreateCallActionRequestCategory,
+ provider: str,
+ config: CreateCallActionRequestConfig,
+ action_type: typing.Optional[CreateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[CreateCallActionResponse]:
+ """
+ Creates a new call action for an agent. Call actions define automated behaviors that fire at specific points in a call lifecycle.
+
+ - **`trigger`** actions fire to initiate an outbound call and require `config.phoneNumberFieldName`.
+ - **`post-call`** actions fire after a call ends (e.g. to update a CRM record).
+
+ Parameters
+ ----------
+ agent_id : str
+ Agent this action belongs to (ObjectId)
+
+ category : CreateCallActionRequestCategory
+ When the action fires
+
+ provider : str
+ Integration provider (e.g. `hubspot`, `salesforce`)
+
+ config : CreateCallActionRequestConfig
+
+ action_type : typing.Optional[CreateCallActionRequestActionType]
+ The operation to perform on the provider object
+
+ object : typing.Optional[str]
+ Provider object type to act on (e.g. `contact`, `deal`)
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[CreateCallActionResponse]
+ Call action created
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "call-actions",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "agentId": agent_id,
+ "category": category,
+ "provider": provider,
+ "action_type": action_type,
+ "object": object,
+ "config": convert_and_respect_annotation_metadata(
+ object_=config, annotation=CreateCallActionRequestConfig, direction="write"
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ CreateCallActionResponse,
+ construct_type(
+ type_=CreateCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetCallActionResponse]:
+ """
+ Returns a single call action by ID. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetCallActionResponse]
+ Call action
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"call-actions/{encode_path_param(id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetCallActionResponse,
+ construct_type(
+ type_=GetCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def update_call_action(
+ self,
+ id: str,
+ *,
+ category: typing.Optional[UpdateCallActionRequestCategory] = OMIT,
+ provider: typing.Optional[str] = OMIT,
+ action_type: typing.Optional[UpdateCallActionRequestActionType] = OMIT,
+ object: typing.Optional[str] = OMIT,
+ config: typing.Optional[UpdateCallActionRequestConfig] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[UpdateCallActionResponse]:
+ """
+ Updates an existing call action. All body fields are optional โ only provided fields are updated.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ category : typing.Optional[UpdateCallActionRequestCategory]
+ Change when the action fires
+
+ provider : typing.Optional[str]
+ Change the integration provider
+
+ action_type : typing.Optional[UpdateCallActionRequestActionType]
+ Change the operation type
+
+ object : typing.Optional[str]
+ Change the provider object type
+
+ config : typing.Optional[UpdateCallActionRequestConfig]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[UpdateCallActionResponse]
+ Updated call action
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"call-actions/{encode_path_param(id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PUT",
+ json={
+ "category": category,
+ "provider": provider,
+ "action_type": action_type,
+ "object": object,
+ "config": convert_and_respect_annotation_metadata(
+ object_=config, annotation=UpdateCallActionRequestConfig, direction="write"
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ UpdateCallActionResponse,
+ construct_type(
+ type_=UpdateCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def delete_call_action(
+ self, id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[DeleteCallActionResponse]:
+ """
+ Permanently deletes a call action. Scoped to the authenticated organization.
+
+ Parameters
+ ----------
+ id : str
+ Call action ObjectId
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[DeleteCallActionResponse]
+ Call action deleted
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"call-actions/{encode_path_param(id)}",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="DELETE",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ DeleteCallActionResponse,
+ construct_type(
+ type_=DeleteCallActionResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/call_actions/types/__init__.py b/src/smallestai/atoms/call_actions/types/__init__.py
new file mode 100644
index 00000000..e25bf307
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/__init__.py
@@ -0,0 +1,77 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .create_call_action_request_action_type import CreateCallActionRequestActionType
+ from .create_call_action_request_category import CreateCallActionRequestCategory
+ from .create_call_action_request_config import CreateCallActionRequestConfig
+ from .create_call_action_response import CreateCallActionResponse
+ from .delete_call_action_response import DeleteCallActionResponse
+ from .delete_call_action_response_data import DeleteCallActionResponseData
+ from .get_call_action_response import GetCallActionResponse
+ from .list_call_actions_request_category import ListCallActionsRequestCategory
+ from .list_call_actions_response import ListCallActionsResponse
+ from .list_call_actions_response_data import ListCallActionsResponseData
+ from .update_call_action_request_action_type import UpdateCallActionRequestActionType
+ from .update_call_action_request_category import UpdateCallActionRequestCategory
+ from .update_call_action_request_config import UpdateCallActionRequestConfig
+ from .update_call_action_response import UpdateCallActionResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateCallActionRequestActionType": ".create_call_action_request_action_type",
+ "CreateCallActionRequestCategory": ".create_call_action_request_category",
+ "CreateCallActionRequestConfig": ".create_call_action_request_config",
+ "CreateCallActionResponse": ".create_call_action_response",
+ "DeleteCallActionResponse": ".delete_call_action_response",
+ "DeleteCallActionResponseData": ".delete_call_action_response_data",
+ "GetCallActionResponse": ".get_call_action_response",
+ "ListCallActionsRequestCategory": ".list_call_actions_request_category",
+ "ListCallActionsResponse": ".list_call_actions_response",
+ "ListCallActionsResponseData": ".list_call_actions_response_data",
+ "UpdateCallActionRequestActionType": ".update_call_action_request_action_type",
+ "UpdateCallActionRequestCategory": ".update_call_action_request_category",
+ "UpdateCallActionRequestConfig": ".update_call_action_request_config",
+ "UpdateCallActionResponse": ".update_call_action_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateCallActionRequestActionType",
+ "CreateCallActionRequestCategory",
+ "CreateCallActionRequestConfig",
+ "CreateCallActionResponse",
+ "DeleteCallActionResponse",
+ "DeleteCallActionResponseData",
+ "GetCallActionResponse",
+ "ListCallActionsRequestCategory",
+ "ListCallActionsResponse",
+ "ListCallActionsResponseData",
+ "UpdateCallActionRequestActionType",
+ "UpdateCallActionRequestCategory",
+ "UpdateCallActionRequestConfig",
+ "UpdateCallActionResponse",
+]
diff --git a/src/smallestai/atoms/call_actions/types/create_call_action_request_action_type.py b/src/smallestai/atoms/call_actions/types/create_call_action_request_action_type.py
new file mode 100644
index 00000000..4de4f1ff
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/create_call_action_request_action_type.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+CreateCallActionRequestActionType = typing.Union[typing.Literal["create", "update"], typing.Any]
diff --git a/src/smallestai/atoms/call_actions/types/create_call_action_request_category.py b/src/smallestai/atoms/call_actions/types/create_call_action_request_category.py
new file mode 100644
index 00000000..02e0973d
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/create_call_action_request_category.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+CreateCallActionRequestCategory = typing.Union[typing.Literal["trigger", "post-call"], typing.Any]
diff --git a/src/smallestai/atoms/call_actions/types/create_call_action_request_config.py b/src/smallestai/atoms/call_actions/types/create_call_action_request_config.py
new file mode 100644
index 00000000..7f094363
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/create_call_action_request_config.py
@@ -0,0 +1,39 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.call_action_condition import CallActionCondition
+
+
+class CreateCallActionRequestConfig(UncheckedBaseModel):
+ phone_number_field_name: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="phoneNumberFieldName"),
+ pydantic.Field(
+ alias="phoneNumberFieldName",
+ description="Provider field name that contains the phone number. Required when `category` is `trigger`.",
+ ),
+ ] = None
+ conditions: typing.Optional[typing.List[CallActionCondition]] = pydantic.Field(default=None)
+ """
+ Filter conditions โ action only fires when all conditions match
+ """
+
+ body: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Request body template or payload for the provider action
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_release_number_response_data.py b/src/smallestai/atoms/call_actions/types/create_call_action_response.py
similarity index 79%
rename from src/smallestai/atoms/phone_numbers/types/post_product_release_number_response_data.py
rename to src/smallestai/atoms/call_actions/types/create_call_action_response.py
index 4748ca9d..f63fdfaf 100644
--- a/src/smallestai/atoms/phone_numbers/types/post_product_release_number_response_data.py
+++ b/src/smallestai/atoms/call_actions/types/create_call_action_response.py
@@ -5,10 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.call_action import CallAction
-class PostProductReleaseNumberResponseData(UncheckedBaseModel):
+class CreateCallActionResponse(UncheckedBaseModel):
success: typing.Optional[bool] = None
+ data: typing.Optional[CallAction] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/call_actions/types/delete_call_action_response.py b/src/smallestai/atoms/call_actions/types/delete_call_action_response.py
new file mode 100644
index 00000000..3501a33a
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/delete_call_action_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .delete_call_action_response_data import DeleteCallActionResponseData
+
+
+class DeleteCallActionResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[DeleteCallActionResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/post_campaign_id_pause_response.py b/src/smallestai/atoms/call_actions/types/delete_call_action_response_data.py
similarity index 83%
rename from src/smallestai/atoms/campaigns/types/post_campaign_id_pause_response.py
rename to src/smallestai/atoms/call_actions/types/delete_call_action_response_data.py
index 77d803b6..468b4872 100644
--- a/src/smallestai/atoms/campaigns/types/post_campaign_id_pause_response.py
+++ b/src/smallestai/atoms/call_actions/types/delete_call_action_response_data.py
@@ -7,8 +7,8 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class PostCampaignIdPauseResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
+class DeleteCallActionResponseData(UncheckedBaseModel):
+ message: typing.Optional[str] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/organization/types/update_name_organization_response.py b/src/smallestai/atoms/call_actions/types/get_call_action_response.py
similarity index 79%
rename from src/smallestai/atoms/organization/types/update_name_organization_response.py
rename to src/smallestai/atoms/call_actions/types/get_call_action_response.py
index f1d270e1..1c6f1df0 100644
--- a/src/smallestai/atoms/organization/types/update_name_organization_response.py
+++ b/src/smallestai/atoms/call_actions/types/get_call_action_response.py
@@ -5,11 +5,12 @@
import pydantic
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.call_action import CallAction
-class UpdateNameOrganizationResponse(UncheckedBaseModel):
+class GetCallActionResponse(UncheckedBaseModel):
success: typing.Optional[bool] = None
- name: typing.Optional[str] = None
+ data: typing.Optional[CallAction] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/call_actions/types/list_call_actions_request_category.py b/src/smallestai/atoms/call_actions/types/list_call_actions_request_category.py
new file mode 100644
index 00000000..931d7104
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/list_call_actions_request_category.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+ListCallActionsRequestCategory = typing.Union[typing.Literal["trigger", "post-call"], typing.Any]
diff --git a/src/smallestai/atoms/call_actions/types/list_call_actions_response.py b/src/smallestai/atoms/call_actions/types/list_call_actions_response.py
new file mode 100644
index 00000000..b86061da
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/list_call_actions_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .list_call_actions_response_data import ListCallActionsResponseData
+
+
+class ListCallActionsResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[ListCallActionsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/agents/types/list_call_logs_agents_response_data_pagination.py b/src/smallestai/atoms/call_actions/types/list_call_actions_response_data.py
similarity index 55%
rename from src/smallestai/atoms/agents/types/list_call_logs_agents_response_data_pagination.py
rename to src/smallestai/atoms/call_actions/types/list_call_actions_response_data.py
index f5da0328..c2e548ef 100644
--- a/src/smallestai/atoms/agents/types/list_call_logs_agents_response_data_pagination.py
+++ b/src/smallestai/atoms/call_actions/types/list_call_actions_response_data.py
@@ -7,19 +7,20 @@
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.serialization import FieldMetadata
from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.call_action import CallAction
-class ListCallLogsAgentsResponseDataPagination(UncheckedBaseModel):
- current_page: typing_extensions.Annotated[
- typing.Optional[int], FieldMetadata(alias="currentPage"), pydantic.Field(alias="currentPage")
- ] = None
- total_pages: typing_extensions.Annotated[
- typing.Optional[int], FieldMetadata(alias="totalPages"), pydantic.Field(alias="totalPages")
+class ListCallActionsResponseData(UncheckedBaseModel):
+ call_actions: typing_extensions.Annotated[
+ typing.Optional[typing.List[CallAction]],
+ FieldMetadata(alias="callActions"),
+ pydantic.Field(alias="callActions"),
] = None
total_count: typing_extensions.Annotated[
- typing.Optional[int], FieldMetadata(alias="totalCount"), pydantic.Field(alias="totalCount")
+ typing.Optional[float], FieldMetadata(alias="totalCount"), pydantic.Field(alias="totalCount")
] = None
- offset: typing.Optional[int] = None
+ page: typing.Optional[float] = None
+ limit: typing.Optional[float] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/call_actions/types/update_call_action_request_action_type.py b/src/smallestai/atoms/call_actions/types/update_call_action_request_action_type.py
new file mode 100644
index 00000000..c94eb9e6
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/update_call_action_request_action_type.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateCallActionRequestActionType = typing.Union[typing.Literal["create", "update"], typing.Any]
diff --git a/src/smallestai/atoms/call_actions/types/update_call_action_request_category.py b/src/smallestai/atoms/call_actions/types/update_call_action_request_category.py
new file mode 100644
index 00000000..28afd050
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/update_call_action_request_category.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+UpdateCallActionRequestCategory = typing.Union[typing.Literal["trigger", "post-call"], typing.Any]
diff --git a/src/smallestai/atoms/call_actions/types/update_call_action_request_config.py b/src/smallestai/atoms/call_actions/types/update_call_action_request_config.py
new file mode 100644
index 00000000..c5c362ca
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/update_call_action_request_config.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.call_action_condition import CallActionCondition
+
+
+class UpdateCallActionRequestConfig(UncheckedBaseModel):
+ phone_number_field_name: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="phoneNumberFieldName"),
+ pydantic.Field(alias="phoneNumberFieldName", description="Update the phone number field mapping"),
+ ] = None
+ conditions: typing.Optional[typing.List[CallActionCondition]] = pydantic.Field(default=None)
+ """
+ Replace the full conditions array
+ """
+
+ body: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Update the payload body
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/call_actions/types/update_call_action_response.py b/src/smallestai/atoms/call_actions/types/update_call_action_response.py
new file mode 100644
index 00000000..4a73f990
--- /dev/null
+++ b/src/smallestai/atoms/call_actions/types/update_call_action_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ...types.call_action import CallAction
+
+
+class UpdateCallActionResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[CallAction] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/calls/__init__.py b/src/smallestai/atoms/calls/__init__.py
index 5cde0202..334efeb5 100644
--- a/src/smallestai/atoms/calls/__init__.py
+++ b/src/smallestai/atoms/calls/__init__.py
@@ -2,3 +2,153 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ GetCallsResponse,
+ GetCallsResponseData,
+ GetCallsResponseDataPostCallAnalytics,
+ GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem,
+ GetCallsResponseDataStatus,
+ GetCallsResponseDataTranscriptItem,
+ GetCallsResponseDataTranscriptItemRole,
+ GetCallsResponseDataTurnLatencyMetrics,
+ GetCallsResponseDataTurnLatencyMetricsTransitionsItem,
+ GetCallsResponseDataType,
+ GetCallsResponseDataVoiceConfigUsed,
+ ListCallsRequestCallTypes,
+ ListCallsRequestSortBy,
+ ListCallsRequestSortOrder,
+ ListCallsResponse,
+ ListCallsResponseData,
+ ListCallsResponseDataDispositionMetricsConfigItem,
+ ListCallsResponseDataLogsItem,
+ ListCallsResponseDataLogsItemAgentDispositionConfigItem,
+ ListCallsResponseDataLogsItemPostCallAnalytics,
+ ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem,
+ ListCallsResponseDataLogsItemStatus,
+ ListCallsResponseDataLogsItemTurnLatencyMetrics,
+ ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem,
+ ListCallsResponseDataLogsItemType,
+ ListCallsResponseDataPagination,
+ SearchCallsResponse,
+ SearchCallsResponseData,
+ SearchCallsResponseDataLogsItem,
+ SearchCallsResponseDataLogsItemPostCallAnalytics,
+ SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem,
+ SearchCallsResponseDataLogsItemStatus,
+ SearchCallsResponseDataLogsItemTurnLatencyMetrics,
+ SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem,
+ SearchCallsResponseDataLogsItemType,
+ StartOutboundCallCallsRequestVariablesValue,
+ StartOutboundCallCallsRequestXTestCall,
+ StartOutboundCallCallsResponse,
+ StartOutboundCallCallsResponseData,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetCallsResponse": ".types",
+ "GetCallsResponseData": ".types",
+ "GetCallsResponseDataPostCallAnalytics": ".types",
+ "GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem": ".types",
+ "GetCallsResponseDataStatus": ".types",
+ "GetCallsResponseDataTranscriptItem": ".types",
+ "GetCallsResponseDataTranscriptItemRole": ".types",
+ "GetCallsResponseDataTurnLatencyMetrics": ".types",
+ "GetCallsResponseDataTurnLatencyMetricsTransitionsItem": ".types",
+ "GetCallsResponseDataType": ".types",
+ "GetCallsResponseDataVoiceConfigUsed": ".types",
+ "ListCallsRequestCallTypes": ".types",
+ "ListCallsRequestSortBy": ".types",
+ "ListCallsRequestSortOrder": ".types",
+ "ListCallsResponse": ".types",
+ "ListCallsResponseData": ".types",
+ "ListCallsResponseDataDispositionMetricsConfigItem": ".types",
+ "ListCallsResponseDataLogsItem": ".types",
+ "ListCallsResponseDataLogsItemAgentDispositionConfigItem": ".types",
+ "ListCallsResponseDataLogsItemPostCallAnalytics": ".types",
+ "ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem": ".types",
+ "ListCallsResponseDataLogsItemStatus": ".types",
+ "ListCallsResponseDataLogsItemTurnLatencyMetrics": ".types",
+ "ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem": ".types",
+ "ListCallsResponseDataLogsItemType": ".types",
+ "ListCallsResponseDataPagination": ".types",
+ "SearchCallsResponse": ".types",
+ "SearchCallsResponseData": ".types",
+ "SearchCallsResponseDataLogsItem": ".types",
+ "SearchCallsResponseDataLogsItemPostCallAnalytics": ".types",
+ "SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem": ".types",
+ "SearchCallsResponseDataLogsItemStatus": ".types",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetrics": ".types",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem": ".types",
+ "SearchCallsResponseDataLogsItemType": ".types",
+ "StartOutboundCallCallsRequestVariablesValue": ".types",
+ "StartOutboundCallCallsRequestXTestCall": ".types",
+ "StartOutboundCallCallsResponse": ".types",
+ "StartOutboundCallCallsResponseData": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetCallsResponse",
+ "GetCallsResponseData",
+ "GetCallsResponseDataPostCallAnalytics",
+ "GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem",
+ "GetCallsResponseDataStatus",
+ "GetCallsResponseDataTranscriptItem",
+ "GetCallsResponseDataTranscriptItemRole",
+ "GetCallsResponseDataTurnLatencyMetrics",
+ "GetCallsResponseDataTurnLatencyMetricsTransitionsItem",
+ "GetCallsResponseDataType",
+ "GetCallsResponseDataVoiceConfigUsed",
+ "ListCallsRequestCallTypes",
+ "ListCallsRequestSortBy",
+ "ListCallsRequestSortOrder",
+ "ListCallsResponse",
+ "ListCallsResponseData",
+ "ListCallsResponseDataDispositionMetricsConfigItem",
+ "ListCallsResponseDataLogsItem",
+ "ListCallsResponseDataLogsItemAgentDispositionConfigItem",
+ "ListCallsResponseDataLogsItemPostCallAnalytics",
+ "ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem",
+ "ListCallsResponseDataLogsItemStatus",
+ "ListCallsResponseDataLogsItemTurnLatencyMetrics",
+ "ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem",
+ "ListCallsResponseDataLogsItemType",
+ "ListCallsResponseDataPagination",
+ "SearchCallsResponse",
+ "SearchCallsResponseData",
+ "SearchCallsResponseDataLogsItem",
+ "SearchCallsResponseDataLogsItemPostCallAnalytics",
+ "SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem",
+ "SearchCallsResponseDataLogsItemStatus",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetrics",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem",
+ "SearchCallsResponseDataLogsItemType",
+ "StartOutboundCallCallsRequestVariablesValue",
+ "StartOutboundCallCallsRequestXTestCall",
+ "StartOutboundCallCallsResponse",
+ "StartOutboundCallCallsResponseData",
+]
diff --git a/src/smallestai/atoms/calls/types/__init__.py b/src/smallestai/atoms/calls/types/__init__.py
index 5cde0202..695e77c4 100644
--- a/src/smallestai/atoms/calls/types/__init__.py
+++ b/src/smallestai/atoms/calls/types/__init__.py
@@ -2,3 +2,171 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_calls_response import GetCallsResponse
+ from .get_calls_response_data import GetCallsResponseData
+ from .get_calls_response_data_post_call_analytics import GetCallsResponseDataPostCallAnalytics
+ from .get_calls_response_data_post_call_analytics_disposition_metrics_item import (
+ GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem,
+ )
+ from .get_calls_response_data_status import GetCallsResponseDataStatus
+ from .get_calls_response_data_transcript_item import GetCallsResponseDataTranscriptItem
+ from .get_calls_response_data_transcript_item_role import GetCallsResponseDataTranscriptItemRole
+ from .get_calls_response_data_turn_latency_metrics import GetCallsResponseDataTurnLatencyMetrics
+ from .get_calls_response_data_turn_latency_metrics_transitions_item import (
+ GetCallsResponseDataTurnLatencyMetricsTransitionsItem,
+ )
+ from .get_calls_response_data_type import GetCallsResponseDataType
+ from .get_calls_response_data_voice_config_used import GetCallsResponseDataVoiceConfigUsed
+ from .list_calls_request_call_types import ListCallsRequestCallTypes
+ from .list_calls_request_sort_by import ListCallsRequestSortBy
+ from .list_calls_request_sort_order import ListCallsRequestSortOrder
+ from .list_calls_response import ListCallsResponse
+ from .list_calls_response_data import ListCallsResponseData
+ from .list_calls_response_data_disposition_metrics_config_item import (
+ ListCallsResponseDataDispositionMetricsConfigItem,
+ )
+ from .list_calls_response_data_logs_item import ListCallsResponseDataLogsItem
+ from .list_calls_response_data_logs_item_agent_disposition_config_item import (
+ ListCallsResponseDataLogsItemAgentDispositionConfigItem,
+ )
+ from .list_calls_response_data_logs_item_post_call_analytics import ListCallsResponseDataLogsItemPostCallAnalytics
+ from .list_calls_response_data_logs_item_post_call_analytics_disposition_metrics_item import (
+ ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem,
+ )
+ from .list_calls_response_data_logs_item_status import ListCallsResponseDataLogsItemStatus
+ from .list_calls_response_data_logs_item_turn_latency_metrics import ListCallsResponseDataLogsItemTurnLatencyMetrics
+ from .list_calls_response_data_logs_item_turn_latency_metrics_transitions_item import (
+ ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem,
+ )
+ from .list_calls_response_data_logs_item_type import ListCallsResponseDataLogsItemType
+ from .list_calls_response_data_pagination import ListCallsResponseDataPagination
+ from .search_calls_response import SearchCallsResponse
+ from .search_calls_response_data import SearchCallsResponseData
+ from .search_calls_response_data_logs_item import SearchCallsResponseDataLogsItem
+ from .search_calls_response_data_logs_item_post_call_analytics import (
+ SearchCallsResponseDataLogsItemPostCallAnalytics,
+ )
+ from .search_calls_response_data_logs_item_post_call_analytics_disposition_metrics_item import (
+ SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem,
+ )
+ from .search_calls_response_data_logs_item_status import SearchCallsResponseDataLogsItemStatus
+ from .search_calls_response_data_logs_item_turn_latency_metrics import (
+ SearchCallsResponseDataLogsItemTurnLatencyMetrics,
+ )
+ from .search_calls_response_data_logs_item_turn_latency_metrics_transitions_item import (
+ SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem,
+ )
+ from .search_calls_response_data_logs_item_type import SearchCallsResponseDataLogsItemType
+ from .start_outbound_call_calls_request_variables_value import StartOutboundCallCallsRequestVariablesValue
+ from .start_outbound_call_calls_request_x_test_call import StartOutboundCallCallsRequestXTestCall
+ from .start_outbound_call_calls_response import StartOutboundCallCallsResponse
+ from .start_outbound_call_calls_response_data import StartOutboundCallCallsResponseData
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetCallsResponse": ".get_calls_response",
+ "GetCallsResponseData": ".get_calls_response_data",
+ "GetCallsResponseDataPostCallAnalytics": ".get_calls_response_data_post_call_analytics",
+ "GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem": ".get_calls_response_data_post_call_analytics_disposition_metrics_item",
+ "GetCallsResponseDataStatus": ".get_calls_response_data_status",
+ "GetCallsResponseDataTranscriptItem": ".get_calls_response_data_transcript_item",
+ "GetCallsResponseDataTranscriptItemRole": ".get_calls_response_data_transcript_item_role",
+ "GetCallsResponseDataTurnLatencyMetrics": ".get_calls_response_data_turn_latency_metrics",
+ "GetCallsResponseDataTurnLatencyMetricsTransitionsItem": ".get_calls_response_data_turn_latency_metrics_transitions_item",
+ "GetCallsResponseDataType": ".get_calls_response_data_type",
+ "GetCallsResponseDataVoiceConfigUsed": ".get_calls_response_data_voice_config_used",
+ "ListCallsRequestCallTypes": ".list_calls_request_call_types",
+ "ListCallsRequestSortBy": ".list_calls_request_sort_by",
+ "ListCallsRequestSortOrder": ".list_calls_request_sort_order",
+ "ListCallsResponse": ".list_calls_response",
+ "ListCallsResponseData": ".list_calls_response_data",
+ "ListCallsResponseDataDispositionMetricsConfigItem": ".list_calls_response_data_disposition_metrics_config_item",
+ "ListCallsResponseDataLogsItem": ".list_calls_response_data_logs_item",
+ "ListCallsResponseDataLogsItemAgentDispositionConfigItem": ".list_calls_response_data_logs_item_agent_disposition_config_item",
+ "ListCallsResponseDataLogsItemPostCallAnalytics": ".list_calls_response_data_logs_item_post_call_analytics",
+ "ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem": ".list_calls_response_data_logs_item_post_call_analytics_disposition_metrics_item",
+ "ListCallsResponseDataLogsItemStatus": ".list_calls_response_data_logs_item_status",
+ "ListCallsResponseDataLogsItemTurnLatencyMetrics": ".list_calls_response_data_logs_item_turn_latency_metrics",
+ "ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem": ".list_calls_response_data_logs_item_turn_latency_metrics_transitions_item",
+ "ListCallsResponseDataLogsItemType": ".list_calls_response_data_logs_item_type",
+ "ListCallsResponseDataPagination": ".list_calls_response_data_pagination",
+ "SearchCallsResponse": ".search_calls_response",
+ "SearchCallsResponseData": ".search_calls_response_data",
+ "SearchCallsResponseDataLogsItem": ".search_calls_response_data_logs_item",
+ "SearchCallsResponseDataLogsItemPostCallAnalytics": ".search_calls_response_data_logs_item_post_call_analytics",
+ "SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem": ".search_calls_response_data_logs_item_post_call_analytics_disposition_metrics_item",
+ "SearchCallsResponseDataLogsItemStatus": ".search_calls_response_data_logs_item_status",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetrics": ".search_calls_response_data_logs_item_turn_latency_metrics",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem": ".search_calls_response_data_logs_item_turn_latency_metrics_transitions_item",
+ "SearchCallsResponseDataLogsItemType": ".search_calls_response_data_logs_item_type",
+ "StartOutboundCallCallsRequestVariablesValue": ".start_outbound_call_calls_request_variables_value",
+ "StartOutboundCallCallsRequestXTestCall": ".start_outbound_call_calls_request_x_test_call",
+ "StartOutboundCallCallsResponse": ".start_outbound_call_calls_response",
+ "StartOutboundCallCallsResponseData": ".start_outbound_call_calls_response_data",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetCallsResponse",
+ "GetCallsResponseData",
+ "GetCallsResponseDataPostCallAnalytics",
+ "GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem",
+ "GetCallsResponseDataStatus",
+ "GetCallsResponseDataTranscriptItem",
+ "GetCallsResponseDataTranscriptItemRole",
+ "GetCallsResponseDataTurnLatencyMetrics",
+ "GetCallsResponseDataTurnLatencyMetricsTransitionsItem",
+ "GetCallsResponseDataType",
+ "GetCallsResponseDataVoiceConfigUsed",
+ "ListCallsRequestCallTypes",
+ "ListCallsRequestSortBy",
+ "ListCallsRequestSortOrder",
+ "ListCallsResponse",
+ "ListCallsResponseData",
+ "ListCallsResponseDataDispositionMetricsConfigItem",
+ "ListCallsResponseDataLogsItem",
+ "ListCallsResponseDataLogsItemAgentDispositionConfigItem",
+ "ListCallsResponseDataLogsItemPostCallAnalytics",
+ "ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem",
+ "ListCallsResponseDataLogsItemStatus",
+ "ListCallsResponseDataLogsItemTurnLatencyMetrics",
+ "ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem",
+ "ListCallsResponseDataLogsItemType",
+ "ListCallsResponseDataPagination",
+ "SearchCallsResponse",
+ "SearchCallsResponseData",
+ "SearchCallsResponseDataLogsItem",
+ "SearchCallsResponseDataLogsItemPostCallAnalytics",
+ "SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem",
+ "SearchCallsResponseDataLogsItemStatus",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetrics",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem",
+ "SearchCallsResponseDataLogsItemType",
+ "StartOutboundCallCallsRequestVariablesValue",
+ "StartOutboundCallCallsRequestXTestCall",
+ "StartOutboundCallCallsResponse",
+ "StartOutboundCallCallsResponseData",
+]
diff --git a/src/smallestai/atoms/campaigns/__init__.py b/src/smallestai/atoms/campaigns/__init__.py
index 5cde0202..78d6a09c 100644
--- a/src/smallestai/atoms/campaigns/__init__.py
+++ b/src/smallestai/atoms/campaigns/__init__.py
@@ -2,3 +2,111 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ CreateCampaignsResponse,
+ CreateCampaignsResponseData,
+ CreateCampaignsResponseDataStatus,
+ DeleteCampaignsResponse,
+ GetCampaignsResponse,
+ GetCampaignsResponseData,
+ GetCampaignsResponseDataCampaign,
+ GetCampaignsResponseDataCampaignAgent,
+ GetCampaignsResponseDataCampaignAudience,
+ GetCampaignsResponseDataCampaignStatus,
+ GetCampaignsResponseDataEventsItem,
+ GetCampaignsResponseDataMetrics,
+ ListCampaignsRequestSortField,
+ ListCampaignsRequestSortOrder,
+ ListCampaignsRequestStatus,
+ ListCampaignsResponse,
+ ListCampaignsResponseData,
+ ListCampaignsResponseDataCampaignsItem,
+ ListCampaignsResponseDataCampaignsItemAgent,
+ ListCampaignsResponseDataCampaignsItemAudience,
+ ListCampaignsResponseDataCampaignsItemStatus,
+ ListCampaignsResponseDataPagination,
+ PauseCampaignsResponse,
+ StartOrResumeCampaignsResponse,
+ StartOrResumeCampaignsResponseData,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateCampaignsResponse": ".types",
+ "CreateCampaignsResponseData": ".types",
+ "CreateCampaignsResponseDataStatus": ".types",
+ "DeleteCampaignsResponse": ".types",
+ "GetCampaignsResponse": ".types",
+ "GetCampaignsResponseData": ".types",
+ "GetCampaignsResponseDataCampaign": ".types",
+ "GetCampaignsResponseDataCampaignAgent": ".types",
+ "GetCampaignsResponseDataCampaignAudience": ".types",
+ "GetCampaignsResponseDataCampaignStatus": ".types",
+ "GetCampaignsResponseDataEventsItem": ".types",
+ "GetCampaignsResponseDataMetrics": ".types",
+ "ListCampaignsRequestSortField": ".types",
+ "ListCampaignsRequestSortOrder": ".types",
+ "ListCampaignsRequestStatus": ".types",
+ "ListCampaignsResponse": ".types",
+ "ListCampaignsResponseData": ".types",
+ "ListCampaignsResponseDataCampaignsItem": ".types",
+ "ListCampaignsResponseDataCampaignsItemAgent": ".types",
+ "ListCampaignsResponseDataCampaignsItemAudience": ".types",
+ "ListCampaignsResponseDataCampaignsItemStatus": ".types",
+ "ListCampaignsResponseDataPagination": ".types",
+ "PauseCampaignsResponse": ".types",
+ "StartOrResumeCampaignsResponse": ".types",
+ "StartOrResumeCampaignsResponseData": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateCampaignsResponse",
+ "CreateCampaignsResponseData",
+ "CreateCampaignsResponseDataStatus",
+ "DeleteCampaignsResponse",
+ "GetCampaignsResponse",
+ "GetCampaignsResponseData",
+ "GetCampaignsResponseDataCampaign",
+ "GetCampaignsResponseDataCampaignAgent",
+ "GetCampaignsResponseDataCampaignAudience",
+ "GetCampaignsResponseDataCampaignStatus",
+ "GetCampaignsResponseDataEventsItem",
+ "GetCampaignsResponseDataMetrics",
+ "ListCampaignsRequestSortField",
+ "ListCampaignsRequestSortOrder",
+ "ListCampaignsRequestStatus",
+ "ListCampaignsResponse",
+ "ListCampaignsResponseData",
+ "ListCampaignsResponseDataCampaignsItem",
+ "ListCampaignsResponseDataCampaignsItemAgent",
+ "ListCampaignsResponseDataCampaignsItemAudience",
+ "ListCampaignsResponseDataCampaignsItemStatus",
+ "ListCampaignsResponseDataPagination",
+ "PauseCampaignsResponse",
+ "StartOrResumeCampaignsResponse",
+ "StartOrResumeCampaignsResponseData",
+]
diff --git a/src/smallestai/atoms/campaigns/client.py b/src/smallestai/atoms/campaigns/client.py
index 3c36f518..27ac5bc5 100644
--- a/src/smallestai/atoms/campaigns/client.py
+++ b/src/smallestai/atoms/campaigns/client.py
@@ -8,7 +8,6 @@
from .raw_client import AsyncRawCampaignsClient, RawCampaignsClient
from .types.create_campaigns_response import CreateCampaignsResponse
from .types.delete_campaigns_response import DeleteCampaignsResponse
-from .types.export_logs_campaigns_response_item import ExportLogsCampaignsResponseItem
from .types.get_campaigns_response import GetCampaignsResponse
from .types.list_campaigns_request_sort_field import ListCampaignsRequestSortField
from .types.list_campaigns_request_sort_order import ListCampaignsRequestSortOrder
@@ -312,44 +311,6 @@ def pause(self, id: str, *, request_options: typing.Optional[RequestOptions] = N
_response = self._raw_client.pause(id, request_options=request_options)
return _response.data
- def export_logs(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.List[ExportLogsCampaignsResponseItem]:
- """
- Streams a JSON file containing every call log for a campaign.
-
- Response is a **file download** (`Content-Disposition: attachment`), not the
- standard `{status, data}` envelope. Body is a raw JSON array of log objects.
- When the relay-service is configured and reachable, each row also includes an
- `events` array; otherwise the field is omitted.
-
- Parameters
- ----------
- id : str
- Campaign ID.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- typing.List[ExportLogsCampaignsResponseItem]
- Campaign-logs JSON file. `Content-Disposition: attachment; filename=campaign-logs--.json`.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.campaigns.export_logs(
- id="id",
- )
- """
- _response = self._raw_client.export_logs(id, request_options=request_options)
- return _response.data
-
class AsyncCampaignsClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -693,49 +654,3 @@ async def main() -> None:
"""
_response = await self._raw_client.pause(id, request_options=request_options)
return _response.data
-
- async def export_logs(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.List[ExportLogsCampaignsResponseItem]:
- """
- Streams a JSON file containing every call log for a campaign.
-
- Response is a **file download** (`Content-Disposition: attachment`), not the
- standard `{status, data}` envelope. Body is a raw JSON array of log objects.
- When the relay-service is configured and reachable, each row also includes an
- `events` array; otherwise the field is omitted.
-
- Parameters
- ----------
- id : str
- Campaign ID.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- typing.List[ExportLogsCampaignsResponseItem]
- Campaign-logs JSON file. `Content-Disposition: attachment; filename=campaign-logs--.json`.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.campaigns.export_logs(
- id="id",
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.export_logs(id, request_options=request_options)
- return _response.data
diff --git a/src/smallestai/atoms/campaigns/raw_client.py b/src/smallestai/atoms/campaigns/raw_client.py
index 7a4ecbf0..54f2c150 100644
--- a/src/smallestai/atoms/campaigns/raw_client.py
+++ b/src/smallestai/atoms/campaigns/raw_client.py
@@ -18,7 +18,6 @@
from ..errors.unauthorized_error import UnauthorizedError
from .types.create_campaigns_response import CreateCampaignsResponse
from .types.delete_campaigns_response import DeleteCampaignsResponse
-from .types.export_logs_campaigns_response_item import ExportLogsCampaignsResponseItem
from .types.get_campaigns_response import GetCampaignsResponse
from .types.list_campaigns_request_sort_field import ListCampaignsRequestSortField
from .types.list_campaigns_request_sort_order import ListCampaignsRequestSortOrder
@@ -619,99 +618,6 @@ def pause(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def export_logs(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[typing.List[ExportLogsCampaignsResponseItem]]:
- """
- Streams a JSON file containing every call log for a campaign.
-
- Response is a **file download** (`Content-Disposition: attachment`), not the
- standard `{status, data}` envelope. Body is a raw JSON array of log objects.
- When the relay-service is configured and reachable, each row also includes an
- `events` array; otherwise the field is omitted.
-
- Parameters
- ----------
- id : str
- Campaign ID.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[typing.List[ExportLogsCampaignsResponseItem]]
- Campaign-logs JSON file. `Content-Disposition: attachment; filename=campaign-logs--.json`.
- """
- _response = self._client_wrapper.httpx_client.request(
- f"campaign/{encode_path_param(id)}/logs/export",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- typing.List[ExportLogsCampaignsResponseItem],
- construct_type(
- type_=typing.List[ExportLogsCampaignsResponseItem], # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
class AsyncRawCampaignsClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -1299,96 +1205,3 @@ async def pause(
status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def export_logs(
- self, id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[typing.List[ExportLogsCampaignsResponseItem]]:
- """
- Streams a JSON file containing every call log for a campaign.
-
- Response is a **file download** (`Content-Disposition: attachment`), not the
- standard `{status, data}` envelope. Body is a raw JSON array of log objects.
- When the relay-service is configured and reachable, each row also includes an
- `events` array; otherwise the field is omitted.
-
- Parameters
- ----------
- id : str
- Campaign ID.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[typing.List[ExportLogsCampaignsResponseItem]]
- Campaign-logs JSON file. `Content-Disposition: attachment; filename=campaign-logs--.json`.
- """
- _response = await self._client_wrapper.httpx_client.request(
- f"campaign/{encode_path_param(id)}/logs/export",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- typing.List[ExportLogsCampaignsResponseItem],
- construct_type(
- type_=typing.List[ExportLogsCampaignsResponseItem], # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/campaigns/types/__init__.py b/src/smallestai/atoms/campaigns/types/__init__.py
index 5cde0202..a2b1c1cc 100644
--- a/src/smallestai/atoms/campaigns/types/__init__.py
+++ b/src/smallestai/atoms/campaigns/types/__init__.py
@@ -2,3 +2,109 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .create_campaigns_response import CreateCampaignsResponse
+ from .create_campaigns_response_data import CreateCampaignsResponseData
+ from .create_campaigns_response_data_status import CreateCampaignsResponseDataStatus
+ from .delete_campaigns_response import DeleteCampaignsResponse
+ from .get_campaigns_response import GetCampaignsResponse
+ from .get_campaigns_response_data import GetCampaignsResponseData
+ from .get_campaigns_response_data_campaign import GetCampaignsResponseDataCampaign
+ from .get_campaigns_response_data_campaign_agent import GetCampaignsResponseDataCampaignAgent
+ from .get_campaigns_response_data_campaign_audience import GetCampaignsResponseDataCampaignAudience
+ from .get_campaigns_response_data_campaign_status import GetCampaignsResponseDataCampaignStatus
+ from .get_campaigns_response_data_events_item import GetCampaignsResponseDataEventsItem
+ from .get_campaigns_response_data_metrics import GetCampaignsResponseDataMetrics
+ from .list_campaigns_request_sort_field import ListCampaignsRequestSortField
+ from .list_campaigns_request_sort_order import ListCampaignsRequestSortOrder
+ from .list_campaigns_request_status import ListCampaignsRequestStatus
+ from .list_campaigns_response import ListCampaignsResponse
+ from .list_campaigns_response_data import ListCampaignsResponseData
+ from .list_campaigns_response_data_campaigns_item import ListCampaignsResponseDataCampaignsItem
+ from .list_campaigns_response_data_campaigns_item_agent import ListCampaignsResponseDataCampaignsItemAgent
+ from .list_campaigns_response_data_campaigns_item_audience import ListCampaignsResponseDataCampaignsItemAudience
+ from .list_campaigns_response_data_campaigns_item_status import ListCampaignsResponseDataCampaignsItemStatus
+ from .list_campaigns_response_data_pagination import ListCampaignsResponseDataPagination
+ from .pause_campaigns_response import PauseCampaignsResponse
+ from .start_or_resume_campaigns_response import StartOrResumeCampaignsResponse
+ from .start_or_resume_campaigns_response_data import StartOrResumeCampaignsResponseData
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateCampaignsResponse": ".create_campaigns_response",
+ "CreateCampaignsResponseData": ".create_campaigns_response_data",
+ "CreateCampaignsResponseDataStatus": ".create_campaigns_response_data_status",
+ "DeleteCampaignsResponse": ".delete_campaigns_response",
+ "GetCampaignsResponse": ".get_campaigns_response",
+ "GetCampaignsResponseData": ".get_campaigns_response_data",
+ "GetCampaignsResponseDataCampaign": ".get_campaigns_response_data_campaign",
+ "GetCampaignsResponseDataCampaignAgent": ".get_campaigns_response_data_campaign_agent",
+ "GetCampaignsResponseDataCampaignAudience": ".get_campaigns_response_data_campaign_audience",
+ "GetCampaignsResponseDataCampaignStatus": ".get_campaigns_response_data_campaign_status",
+ "GetCampaignsResponseDataEventsItem": ".get_campaigns_response_data_events_item",
+ "GetCampaignsResponseDataMetrics": ".get_campaigns_response_data_metrics",
+ "ListCampaignsRequestSortField": ".list_campaigns_request_sort_field",
+ "ListCampaignsRequestSortOrder": ".list_campaigns_request_sort_order",
+ "ListCampaignsRequestStatus": ".list_campaigns_request_status",
+ "ListCampaignsResponse": ".list_campaigns_response",
+ "ListCampaignsResponseData": ".list_campaigns_response_data",
+ "ListCampaignsResponseDataCampaignsItem": ".list_campaigns_response_data_campaigns_item",
+ "ListCampaignsResponseDataCampaignsItemAgent": ".list_campaigns_response_data_campaigns_item_agent",
+ "ListCampaignsResponseDataCampaignsItemAudience": ".list_campaigns_response_data_campaigns_item_audience",
+ "ListCampaignsResponseDataCampaignsItemStatus": ".list_campaigns_response_data_campaigns_item_status",
+ "ListCampaignsResponseDataPagination": ".list_campaigns_response_data_pagination",
+ "PauseCampaignsResponse": ".pause_campaigns_response",
+ "StartOrResumeCampaignsResponse": ".start_or_resume_campaigns_response",
+ "StartOrResumeCampaignsResponseData": ".start_or_resume_campaigns_response_data",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateCampaignsResponse",
+ "CreateCampaignsResponseData",
+ "CreateCampaignsResponseDataStatus",
+ "DeleteCampaignsResponse",
+ "GetCampaignsResponse",
+ "GetCampaignsResponseData",
+ "GetCampaignsResponseDataCampaign",
+ "GetCampaignsResponseDataCampaignAgent",
+ "GetCampaignsResponseDataCampaignAudience",
+ "GetCampaignsResponseDataCampaignStatus",
+ "GetCampaignsResponseDataEventsItem",
+ "GetCampaignsResponseDataMetrics",
+ "ListCampaignsRequestSortField",
+ "ListCampaignsRequestSortOrder",
+ "ListCampaignsRequestStatus",
+ "ListCampaignsResponse",
+ "ListCampaignsResponseData",
+ "ListCampaignsResponseDataCampaignsItem",
+ "ListCampaignsResponseDataCampaignsItemAgent",
+ "ListCampaignsResponseDataCampaignsItemAudience",
+ "ListCampaignsResponseDataCampaignsItemStatus",
+ "ListCampaignsResponseDataPagination",
+ "PauseCampaignsResponse",
+ "StartOrResumeCampaignsResponse",
+ "StartOrResumeCampaignsResponseData",
+]
diff --git a/src/smallestai/atoms/campaigns/types/export_logs_campaigns_response_item.py b/src/smallestai/atoms/campaigns/types/export_logs_campaigns_response_item.py
deleted file mode 100644
index 74ef35ac..00000000
--- a/src/smallestai/atoms/campaigns/types/export_logs_campaigns_response_item.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class ExportLogsCampaignsResponseItem(UncheckedBaseModel):
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- call_id: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="callId"), pydantic.Field(alias="callId")
- ] = None
- status: typing.Optional[str] = None
- duration: typing.Optional[float] = None
- recording_url: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="recordingUrl"), pydantic.Field(alias="recordingUrl")
- ] = None
- from_: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="from"), pydantic.Field(alias="from")
- ] = None
- to: typing.Optional[str] = None
- agent_id: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="agentId"), pydantic.Field(alias="agentId")
- ] = None
- status_details: typing_extensions.Annotated[
- typing.Optional[typing.List[str]], FieldMetadata(alias="statusDetails"), pydantic.Field(alias="statusDetails")
- ] = None
- events: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = pydantic.Field(default=None)
- """
- Relay-service event stream. Omitted when relay isn't configured.
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response.py b/src/smallestai/atoms/campaigns/types/get_campaign_id_response.py
deleted file mode 100644
index 79ebf58a..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_campaign_id_response_data import GetCampaignIdResponseData
-
-
-class GetCampaignIdResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[GetCampaignIdResponseData] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data.py b/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data.py
deleted file mode 100644
index b8804860..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_campaign_id_response_data_campaign import GetCampaignIdResponseDataCampaign
-from .get_campaign_id_response_data_events_item import GetCampaignIdResponseDataEventsItem
-from .get_campaign_id_response_data_metrics import GetCampaignIdResponseDataMetrics
-
-
-class GetCampaignIdResponseData(UncheckedBaseModel):
- campaign: typing.Optional[GetCampaignIdResponseDataCampaign] = None
- events: typing.Optional[typing.List[GetCampaignIdResponseDataEventsItem]] = pydantic.Field(default=None)
- """
- Campaign events history
- """
-
- metrics: typing.Optional[GetCampaignIdResponseDataMetrics] = pydantic.Field(default=None)
- """
- Campaign performance metrics
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign.py b/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign.py
deleted file mode 100644
index 66213880..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_campaign_id_response_data_campaign_agent import GetCampaignIdResponseDataCampaignAgent
-from .get_campaign_id_response_data_campaign_audience import GetCampaignIdResponseDataCampaignAudience
-from .get_campaign_id_response_data_campaign_status import GetCampaignIdResponseDataCampaignStatus
-
-
-class GetCampaignIdResponseDataCampaign(UncheckedBaseModel):
- id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="_id"),
- pydantic.Field(alias="_id", description="The unique identifier for the campaign"),
- ] = None
- name: typing.Optional[str] = pydantic.Field(default=None)
- """
- The name of the campaign
- """
-
- description: typing.Optional[str] = pydantic.Field(default=None)
- """
- The description of the campaign
- """
-
- organization: typing.Optional[str] = pydantic.Field(default=None)
- """
- The ID of the organization
- """
-
- agent: typing.Optional[GetCampaignIdResponseDataCampaignAgent] = pydantic.Field(default=None)
- """
- The agent assigned to this campaign
- """
-
- audience: typing.Optional[GetCampaignIdResponseDataCampaignAudience] = pydantic.Field(default=None)
- """
- The audience assigned to this campaign
- """
-
- created_by: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="createdBy"),
- pydantic.Field(alias="createdBy", description="The ID of the user who created the campaign"),
- ] = None
- participants_count: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="participantsCount"),
- pydantic.Field(alias="participantsCount", description="The number of participants in the campaign"),
- ] = None
- status: typing.Optional[GetCampaignIdResponseDataCampaignStatus] = pydantic.Field(default=None)
- """
- The current status of the campaign
- """
-
- max_retries: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="maxRetries"),
- pydantic.Field(alias="maxRetries", description="Maximum number of retry attempts"),
- ] = None
- retry_delay: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="retryDelay"),
- pydantic.Field(alias="retryDelay", description="Delay between retries in minutes"),
- ] = None
- retry_attempts: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="retryAttempts"),
- pydantic.Field(alias="retryAttempts", description="Total retry attempts made so far"),
- ] = None
- scheduled_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="scheduledAt"),
- pydantic.Field(alias="scheduledAt", description="Scheduled start time for the campaign"),
- ] = None
- paused_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="pausedAt"),
- pydantic.Field(alias="pausedAt", description="When the campaign was paused"),
- ] = None
- cancelled_calls_count: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="cancelledCallsCount"),
- pydantic.Field(
- alias="cancelledCallsCount", description="Number of calls cancelled (e.g. when campaign was paused)"
- ),
- ] = None
- error: typing.Optional[str] = pydantic.Field(default=None)
- """
- Error message if the campaign failed
- """
-
- failed_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="failedAt"),
- pydantic.Field(alias="failedAt", description="When the campaign entered failed state"),
- ] = None
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="The date and time when the campaign was created"),
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="updatedAt"),
- pydantic.Field(alias="updatedAt", description="The date and time when the campaign was last updated"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_status.py b/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_status.py
deleted file mode 100644
index c7d0273e..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_campaign_status.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetCampaignIdResponseDataCampaignStatus = typing.Union[
- typing.Literal["draft", "scheduled", "processing", "running", "paused", "completed", "failed"], typing.Any
-]
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_events_item.py b/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_events_item.py
deleted file mode 100644
index 77798db0..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_events_item.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class GetCampaignIdResponseDataEventsItem(UncheckedBaseModel):
- id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
- None
- )
- trigger_source: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="triggerSource"), pydantic.Field(alias="triggerSource")
- ] = None
- event_action: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="eventAction"), pydantic.Field(alias="eventAction")
- ] = None
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime], FieldMetadata(alias="createdAt"), pydantic.Field(alias="createdAt")
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime], FieldMetadata(alias="updatedAt"), pydantic.Field(alias="updatedAt")
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_metrics.py b/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_metrics.py
deleted file mode 100644
index aba6a45a..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_id_response_data_metrics.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class GetCampaignIdResponseDataMetrics(UncheckedBaseModel):
- """
- Campaign performance metrics
- """
-
- total_participants: typing.Optional[int] = pydantic.Field(default=None)
- """
- Total number of contacts in the campaign audience
- """
-
- contacts_called: typing.Optional[int] = pydantic.Field(default=None)
- """
- Number of unique contacts where a call was attempted (statuses ACTIVE, COMPLETED, FAILED, NO_ANSWER)
- """
-
- contacts_connected: typing.Optional[int] = pydantic.Field(default=None)
- """
- Number of unique contacts who answered and had a conversation (status COMPLETED)
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_request_sort_field.py b/src/smallestai/atoms/campaigns/types/get_campaign_request_sort_field.py
deleted file mode 100644
index dac17fbf..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_request_sort_field.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetCampaignRequestSortField = typing.Union[typing.Literal["createdAt", "updatedAt"], typing.Any]
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_request_sort_order.py b/src/smallestai/atoms/campaigns/types/get_campaign_request_sort_order.py
deleted file mode 100644
index e6c4cb30..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_request_sort_order.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetCampaignRequestSortOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any]
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_request_status.py b/src/smallestai/atoms/campaigns/types/get_campaign_request_status.py
deleted file mode 100644
index ae6366b5..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_request_status.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetCampaignRequestStatus = typing.Union[
- typing.Literal["draft", "scheduled", "processing", "running", "paused", "completed", "failed"], typing.Any
-]
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_response.py b/src/smallestai/atoms/campaigns/types/get_campaign_response.py
deleted file mode 100644
index a3429c3b..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_campaign_response_data import GetCampaignResponseData
-
-
-class GetCampaignResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[GetCampaignResponseData] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_response_data.py b/src/smallestai/atoms/campaigns/types/get_campaign_response_data.py
deleted file mode 100644
index 24ef6808..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_response_data.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_campaign_response_data_campaigns_item import GetCampaignResponseDataCampaignsItem
-from .get_campaign_response_data_pagination import GetCampaignResponseDataPagination
-
-
-class GetCampaignResponseData(UncheckedBaseModel):
- campaigns: typing.Optional[typing.List[GetCampaignResponseDataCampaignsItem]] = None
- pagination: typing.Optional[GetCampaignResponseDataPagination] = None
- total_campaign_count: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="totalCampaignCount"),
- pydantic.Field(
- alias="totalCampaignCount", description="Total number of campaigns in the organization (unfiltered)"
- ),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item.py b/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item.py
deleted file mode 100644
index a8a6d822..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_campaign_response_data_campaigns_item_agent import GetCampaignResponseDataCampaignsItemAgent
-from .get_campaign_response_data_campaigns_item_audience import GetCampaignResponseDataCampaignsItemAudience
-from .get_campaign_response_data_campaigns_item_status import GetCampaignResponseDataCampaignsItemStatus
-
-
-class GetCampaignResponseDataCampaignsItem(UncheckedBaseModel):
- id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="_id"),
- pydantic.Field(alias="_id", description="The unique identifier for the campaign"),
- ] = None
- name: typing.Optional[str] = pydantic.Field(default=None)
- """
- The name of the campaign
- """
-
- description: typing.Optional[str] = pydantic.Field(default=None)
- """
- The description of the campaign
- """
-
- organization: typing.Optional[str] = pydantic.Field(default=None)
- """
- The ID of the organization
- """
-
- agent: typing.Optional[GetCampaignResponseDataCampaignsItemAgent] = pydantic.Field(default=None)
- """
- The agent assigned to this campaign
- """
-
- audience: typing.Optional[GetCampaignResponseDataCampaignsItemAudience] = pydantic.Field(default=None)
- """
- The audience assigned to this campaign
- """
-
- created_by: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="createdBy"),
- pydantic.Field(alias="createdBy", description="The ID of the user who created the campaign"),
- ] = None
- participants_count: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="participantsCount"),
- pydantic.Field(alias="participantsCount", description="The number of participants in the campaign"),
- ] = None
- status: typing.Optional[GetCampaignResponseDataCampaignsItemStatus] = pydantic.Field(default=None)
- """
- Current status of the campaign
- """
-
- max_retries: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="maxRetries"),
- pydantic.Field(alias="maxRetries", description="Maximum retry attempts per failed call"),
- ] = None
- retry_delay: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="retryDelay"),
- pydantic.Field(alias="retryDelay", description="Delay in minutes between retry attempts"),
- ] = None
- retry_attempts: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="retryAttempts"),
- pydantic.Field(alias="retryAttempts", description="Total retry attempts made so far"),
- ] = None
- scheduled_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime], FieldMetadata(alias="scheduledAt"), pydantic.Field(alias="scheduledAt")
- ] = None
- paused_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime], FieldMetadata(alias="pausedAt"), pydantic.Field(alias="pausedAt")
- ] = None
- cancelled_calls_count: typing_extensions.Annotated[
- typing.Optional[int], FieldMetadata(alias="cancelledCallsCount"), pydantic.Field(alias="cancelledCallsCount")
- ] = None
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="The date and time when the campaign was created"),
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="updatedAt"),
- pydantic.Field(alias="updatedAt", description="The date and time when the campaign was last updated"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_status.py b/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_status.py
deleted file mode 100644
index 7dd53c7c..00000000
--- a/src/smallestai/atoms/campaigns/types/get_campaign_response_data_campaigns_item_status.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetCampaignResponseDataCampaignsItemStatus = typing.Union[
- typing.Literal["draft", "scheduled", "processing", "running", "paused", "completed", "failed"], typing.Any
-]
diff --git a/src/smallestai/atoms/campaigns/types/post_campaign_response_data.py b/src/smallestai/atoms/campaigns/types/post_campaign_response_data.py
deleted file mode 100644
index e9afb422..00000000
--- a/src/smallestai/atoms/campaigns/types/post_campaign_response_data.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_campaign_response_data_status import PostCampaignResponseDataStatus
-
-
-class PostCampaignResponseData(UncheckedBaseModel):
- id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="_id"),
- pydantic.Field(alias="_id", description="The unique identifier for the campaign"),
- ] = None
- name: typing.Optional[str] = pydantic.Field(default=None)
- """
- The name of the campaign
- """
-
- description: typing.Optional[str] = pydantic.Field(default=None)
- """
- The description of the campaign
- """
-
- organization: typing.Optional[str] = pydantic.Field(default=None)
- """
- The ID of the organization
- """
-
- agent_id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="agentId"),
- pydantic.Field(alias="agentId", description="Raw ObjectId of the agent (not a nested object)"),
- ] = None
- created_by: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="createdBy"),
- pydantic.Field(alias="createdBy", description="The ID of the user who created the campaign"),
- ] = None
- audience_id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="audienceId"),
- pydantic.Field(alias="audienceId", description="Raw ObjectId of the audience (not a nested object)"),
- ] = None
- participants_count: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="participantsCount"),
- pydantic.Field(alias="participantsCount", description="The number of participants in the campaign"),
- ] = None
- scheduled_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="scheduledAt"),
- pydantic.Field(alias="scheduledAt", description="The scheduled start time, if provided at creation."),
- ] = None
- max_retries: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="maxRetries"),
- pydantic.Field(alias="maxRetries", description="Maximum retries per failed call (echoes request)."),
- ] = None
- retry_delay: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="retryDelay"),
- pydantic.Field(alias="retryDelay", description="Delay in minutes between retry attempts (echoes request)."),
- ] = None
- retry_attempts: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="retryAttempts"),
- pydantic.Field(alias="retryAttempts", description="Number of retries attempted so far across the campaign."),
- ] = None
- status: typing.Optional[PostCampaignResponseDataStatus] = pydantic.Field(default=None)
- """
- Current campaign status.
- """
-
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="The date and time when the campaign was created"),
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="updatedAt"),
- pydantic.Field(alias="updatedAt", description="The date and time when the campaign was last updated"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/campaigns/types/post_campaign_response_data_status.py b/src/smallestai/atoms/campaigns/types/post_campaign_response_data_status.py
deleted file mode 100644
index 69ecdab7..00000000
--- a/src/smallestai/atoms/campaigns/types/post_campaign_response_data_status.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-PostCampaignResponseDataStatus = typing.Union[
- typing.Literal["draft", "scheduled", "processing", "running", "paused", "completed", "failed"], typing.Any
-]
diff --git a/src/smallestai/atoms/client.py b/src/smallestai/atoms/client.py
index a11520e1..ffe5c889 100644
--- a/src/smallestai/atoms/client.py
+++ b/src/smallestai/atoms/client.py
@@ -9,20 +9,31 @@
if typing.TYPE_CHECKING:
from .agent_templates.client import AgentTemplatesClient, AsyncAgentTemplatesClient
+ from .agent_versioning_branches.client import AgentVersioningBranchesClient, AsyncAgentVersioningBranchesClient
from .agent_versioning_drafts.client import AgentVersioningDraftsClient, AsyncAgentVersioningDraftsClient
+ from .agent_versioning_revisions.client import AgentVersioningRevisionsClient, AsyncAgentVersioningRevisionsClient
from .agent_versioning_versions.client import AgentVersioningVersionsClient, AsyncAgentVersioningVersionsClient
from .agents.client import AgentsClient, AsyncAgentsClient
+ from .analytics.client import AnalyticsClient, AsyncAnalyticsClient
from .audience.client import AsyncAudienceClient, AudienceClient
+ from .call_actions.client import AsyncCallActionsClient, CallActionsClient
from .calls.client import AsyncCallsClient, CallsClient
from .campaigns.client import AsyncCampaignsClient, CampaignsClient
from .compliance.client import AsyncComplianceClient, ComplianceClient
+ from .concurrency.client import AsyncConcurrencyClient, ConcurrencyClient
from .conversations.client import AsyncConversationsClient, ConversationsClient
+ from .disposition_metric_templates.client import (
+ AsyncDispositionMetricTemplatesClient,
+ DispositionMetricTemplatesClient,
+ )
from .dnc.client import AsyncDncClient, DncClient
+ from .integrations.client import AsyncIntegrationsClient, IntegrationsClient
from .knowledge_base.client import AsyncKnowledgeBaseClient, KnowledgeBaseClient
from .live_transcripts.client import AsyncLiveTranscriptsClient, LiveTranscriptsClient
from .organization.client import AsyncOrganizationClient, OrganizationClient
from .phone_numbers.client import AsyncPhoneNumbersClient, PhoneNumbersClient
from .prompt_scoring.client import AsyncPromptScoringClient, PromptScoringClient
+ from .realtime.client import AsyncRealtimeClient, RealtimeClient
from .user.client import AsyncUserClient, UserClient
from .webhooks.client import AsyncWebhooksClient, WebhooksClient
@@ -35,6 +46,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper):
self._organization: typing.Optional[OrganizationClient] = None
self._agent_templates: typing.Optional[AgentTemplatesClient] = None
self._agents: typing.Optional[AgentsClient] = None
+ self._realtime: typing.Optional[RealtimeClient] = None
self._calls: typing.Optional[CallsClient] = None
self._conversations: typing.Optional[ConversationsClient] = None
self._live_transcripts: typing.Optional[LiveTranscriptsClient] = None
@@ -46,7 +58,14 @@ def __init__(self, *, client_wrapper: SyncClientWrapper):
self._audience: typing.Optional[AudienceClient] = None
self._agent_versioning_drafts: typing.Optional[AgentVersioningDraftsClient] = None
self._agent_versioning_versions: typing.Optional[AgentVersioningVersionsClient] = None
+ self._agent_versioning_branches: typing.Optional[AgentVersioningBranchesClient] = None
+ self._agent_versioning_revisions: typing.Optional[AgentVersioningRevisionsClient] = None
self._prompt_scoring: typing.Optional[PromptScoringClient] = None
+ self._analytics: typing.Optional[AnalyticsClient] = None
+ self._call_actions: typing.Optional[CallActionsClient] = None
+ self._integrations: typing.Optional[IntegrationsClient] = None
+ self._concurrency: typing.Optional[ConcurrencyClient] = None
+ self._disposition_metric_templates: typing.Optional[DispositionMetricTemplatesClient] = None
self._dnc: typing.Optional[DncClient] = None
@property
@@ -92,6 +111,14 @@ def agents(self):
self._agents = AgentsClient(client_wrapper=self._client_wrapper)
return self._agents
+ @property
+ def realtime(self):
+ if self._realtime is None:
+ from .realtime.client import RealtimeClient # noqa: E402
+
+ self._realtime = RealtimeClient(client_wrapper=self._client_wrapper)
+ return self._realtime
+
@property
def calls(self):
if self._calls is None:
@@ -180,6 +207,22 @@ def agent_versioning_versions(self):
self._agent_versioning_versions = AgentVersioningVersionsClient(client_wrapper=self._client_wrapper)
return self._agent_versioning_versions
+ @property
+ def agent_versioning_branches(self):
+ if self._agent_versioning_branches is None:
+ from .agent_versioning_branches.client import AgentVersioningBranchesClient # noqa: E402
+
+ self._agent_versioning_branches = AgentVersioningBranchesClient(client_wrapper=self._client_wrapper)
+ return self._agent_versioning_branches
+
+ @property
+ def agent_versioning_revisions(self):
+ if self._agent_versioning_revisions is None:
+ from .agent_versioning_revisions.client import AgentVersioningRevisionsClient # noqa: E402
+
+ self._agent_versioning_revisions = AgentVersioningRevisionsClient(client_wrapper=self._client_wrapper)
+ return self._agent_versioning_revisions
+
@property
def prompt_scoring(self):
if self._prompt_scoring is None:
@@ -188,6 +231,46 @@ def prompt_scoring(self):
self._prompt_scoring = PromptScoringClient(client_wrapper=self._client_wrapper)
return self._prompt_scoring
+ @property
+ def analytics(self):
+ if self._analytics is None:
+ from .analytics.client import AnalyticsClient # noqa: E402
+
+ self._analytics = AnalyticsClient(client_wrapper=self._client_wrapper)
+ return self._analytics
+
+ @property
+ def call_actions(self):
+ if self._call_actions is None:
+ from .call_actions.client import CallActionsClient # noqa: E402
+
+ self._call_actions = CallActionsClient(client_wrapper=self._client_wrapper)
+ return self._call_actions
+
+ @property
+ def integrations(self):
+ if self._integrations is None:
+ from .integrations.client import IntegrationsClient # noqa: E402
+
+ self._integrations = IntegrationsClient(client_wrapper=self._client_wrapper)
+ return self._integrations
+
+ @property
+ def concurrency(self):
+ if self._concurrency is None:
+ from .concurrency.client import ConcurrencyClient # noqa: E402
+
+ self._concurrency = ConcurrencyClient(client_wrapper=self._client_wrapper)
+ return self._concurrency
+
+ @property
+ def disposition_metric_templates(self):
+ if self._disposition_metric_templates is None:
+ from .disposition_metric_templates.client import DispositionMetricTemplatesClient # noqa: E402
+
+ self._disposition_metric_templates = DispositionMetricTemplatesClient(client_wrapper=self._client_wrapper)
+ return self._disposition_metric_templates
+
@property
def dnc(self):
if self._dnc is None:
@@ -205,6 +288,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper):
self._organization: typing.Optional[AsyncOrganizationClient] = None
self._agent_templates: typing.Optional[AsyncAgentTemplatesClient] = None
self._agents: typing.Optional[AsyncAgentsClient] = None
+ self._realtime: typing.Optional[AsyncRealtimeClient] = None
self._calls: typing.Optional[AsyncCallsClient] = None
self._conversations: typing.Optional[AsyncConversationsClient] = None
self._live_transcripts: typing.Optional[AsyncLiveTranscriptsClient] = None
@@ -216,7 +300,14 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper):
self._audience: typing.Optional[AsyncAudienceClient] = None
self._agent_versioning_drafts: typing.Optional[AsyncAgentVersioningDraftsClient] = None
self._agent_versioning_versions: typing.Optional[AsyncAgentVersioningVersionsClient] = None
+ self._agent_versioning_branches: typing.Optional[AsyncAgentVersioningBranchesClient] = None
+ self._agent_versioning_revisions: typing.Optional[AsyncAgentVersioningRevisionsClient] = None
self._prompt_scoring: typing.Optional[AsyncPromptScoringClient] = None
+ self._analytics: typing.Optional[AsyncAnalyticsClient] = None
+ self._call_actions: typing.Optional[AsyncCallActionsClient] = None
+ self._integrations: typing.Optional[AsyncIntegrationsClient] = None
+ self._concurrency: typing.Optional[AsyncConcurrencyClient] = None
+ self._disposition_metric_templates: typing.Optional[AsyncDispositionMetricTemplatesClient] = None
self._dnc: typing.Optional[AsyncDncClient] = None
@property
@@ -262,6 +353,14 @@ def agents(self):
self._agents = AsyncAgentsClient(client_wrapper=self._client_wrapper)
return self._agents
+ @property
+ def realtime(self):
+ if self._realtime is None:
+ from .realtime.client import AsyncRealtimeClient # noqa: E402
+
+ self._realtime = AsyncRealtimeClient(client_wrapper=self._client_wrapper)
+ return self._realtime
+
@property
def calls(self):
if self._calls is None:
@@ -350,6 +449,22 @@ def agent_versioning_versions(self):
self._agent_versioning_versions = AsyncAgentVersioningVersionsClient(client_wrapper=self._client_wrapper)
return self._agent_versioning_versions
+ @property
+ def agent_versioning_branches(self):
+ if self._agent_versioning_branches is None:
+ from .agent_versioning_branches.client import AsyncAgentVersioningBranchesClient # noqa: E402
+
+ self._agent_versioning_branches = AsyncAgentVersioningBranchesClient(client_wrapper=self._client_wrapper)
+ return self._agent_versioning_branches
+
+ @property
+ def agent_versioning_revisions(self):
+ if self._agent_versioning_revisions is None:
+ from .agent_versioning_revisions.client import AsyncAgentVersioningRevisionsClient # noqa: E402
+
+ self._agent_versioning_revisions = AsyncAgentVersioningRevisionsClient(client_wrapper=self._client_wrapper)
+ return self._agent_versioning_revisions
+
@property
def prompt_scoring(self):
if self._prompt_scoring is None:
@@ -358,6 +473,48 @@ def prompt_scoring(self):
self._prompt_scoring = AsyncPromptScoringClient(client_wrapper=self._client_wrapper)
return self._prompt_scoring
+ @property
+ def analytics(self):
+ if self._analytics is None:
+ from .analytics.client import AsyncAnalyticsClient # noqa: E402
+
+ self._analytics = AsyncAnalyticsClient(client_wrapper=self._client_wrapper)
+ return self._analytics
+
+ @property
+ def call_actions(self):
+ if self._call_actions is None:
+ from .call_actions.client import AsyncCallActionsClient # noqa: E402
+
+ self._call_actions = AsyncCallActionsClient(client_wrapper=self._client_wrapper)
+ return self._call_actions
+
+ @property
+ def integrations(self):
+ if self._integrations is None:
+ from .integrations.client import AsyncIntegrationsClient # noqa: E402
+
+ self._integrations = AsyncIntegrationsClient(client_wrapper=self._client_wrapper)
+ return self._integrations
+
+ @property
+ def concurrency(self):
+ if self._concurrency is None:
+ from .concurrency.client import AsyncConcurrencyClient # noqa: E402
+
+ self._concurrency = AsyncConcurrencyClient(client_wrapper=self._client_wrapper)
+ return self._concurrency
+
+ @property
+ def disposition_metric_templates(self):
+ if self._disposition_metric_templates is None:
+ from .disposition_metric_templates.client import AsyncDispositionMetricTemplatesClient # noqa: E402
+
+ self._disposition_metric_templates = AsyncDispositionMetricTemplatesClient(
+ client_wrapper=self._client_wrapper
+ )
+ return self._disposition_metric_templates
+
@property
def dnc(self):
if self._dnc is None:
diff --git a/src/smallestai/atoms/compliance/__init__.py b/src/smallestai/atoms/compliance/__init__.py
index 5cde0202..1dc1b2f3 100644
--- a/src/smallestai/atoms/compliance/__init__.py
+++ b/src/smallestai/atoms/compliance/__init__.py
@@ -2,3 +2,75 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ GetComplianceRequirementsRequestNumberType,
+ GetComplianceRequirementsRequestUserType,
+ GetComplianceRequirementsResponse,
+ GetComplianceStatusRequestNumberType,
+ GetComplianceStatusRequestUserType,
+ GetComplianceStatusResponse,
+ GetComplianceStatusResponseData,
+ GetComplianceStatusResponseDataStep,
+ PostComplianceApplicationsIdRefreshResponse,
+ ResubmitComplianceResponse,
+ SubmitComplianceRequestNumberType,
+ SubmitComplianceRequestUserType,
+ SubmitComplianceResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetComplianceRequirementsRequestNumberType": ".types",
+ "GetComplianceRequirementsRequestUserType": ".types",
+ "GetComplianceRequirementsResponse": ".types",
+ "GetComplianceStatusRequestNumberType": ".types",
+ "GetComplianceStatusRequestUserType": ".types",
+ "GetComplianceStatusResponse": ".types",
+ "GetComplianceStatusResponseData": ".types",
+ "GetComplianceStatusResponseDataStep": ".types",
+ "PostComplianceApplicationsIdRefreshResponse": ".types",
+ "ResubmitComplianceResponse": ".types",
+ "SubmitComplianceRequestNumberType": ".types",
+ "SubmitComplianceRequestUserType": ".types",
+ "SubmitComplianceResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetComplianceRequirementsRequestNumberType",
+ "GetComplianceRequirementsRequestUserType",
+ "GetComplianceRequirementsResponse",
+ "GetComplianceStatusRequestNumberType",
+ "GetComplianceStatusRequestUserType",
+ "GetComplianceStatusResponse",
+ "GetComplianceStatusResponseData",
+ "GetComplianceStatusResponseDataStep",
+ "PostComplianceApplicationsIdRefreshResponse",
+ "ResubmitComplianceResponse",
+ "SubmitComplianceRequestNumberType",
+ "SubmitComplianceRequestUserType",
+ "SubmitComplianceResponse",
+]
diff --git a/src/smallestai/atoms/compliance/types/__init__.py b/src/smallestai/atoms/compliance/types/__init__.py
index 5cde0202..3fde8c2b 100644
--- a/src/smallestai/atoms/compliance/types/__init__.py
+++ b/src/smallestai/atoms/compliance/types/__init__.py
@@ -2,3 +2,73 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_compliance_requirements_request_number_type import GetComplianceRequirementsRequestNumberType
+ from .get_compliance_requirements_request_user_type import GetComplianceRequirementsRequestUserType
+ from .get_compliance_requirements_response import GetComplianceRequirementsResponse
+ from .get_compliance_status_request_number_type import GetComplianceStatusRequestNumberType
+ from .get_compliance_status_request_user_type import GetComplianceStatusRequestUserType
+ from .get_compliance_status_response import GetComplianceStatusResponse
+ from .get_compliance_status_response_data import GetComplianceStatusResponseData
+ from .get_compliance_status_response_data_step import GetComplianceStatusResponseDataStep
+ from .post_compliance_applications_id_refresh_response import PostComplianceApplicationsIdRefreshResponse
+ from .resubmit_compliance_response import ResubmitComplianceResponse
+ from .submit_compliance_request_number_type import SubmitComplianceRequestNumberType
+ from .submit_compliance_request_user_type import SubmitComplianceRequestUserType
+ from .submit_compliance_response import SubmitComplianceResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetComplianceRequirementsRequestNumberType": ".get_compliance_requirements_request_number_type",
+ "GetComplianceRequirementsRequestUserType": ".get_compliance_requirements_request_user_type",
+ "GetComplianceRequirementsResponse": ".get_compliance_requirements_response",
+ "GetComplianceStatusRequestNumberType": ".get_compliance_status_request_number_type",
+ "GetComplianceStatusRequestUserType": ".get_compliance_status_request_user_type",
+ "GetComplianceStatusResponse": ".get_compliance_status_response",
+ "GetComplianceStatusResponseData": ".get_compliance_status_response_data",
+ "GetComplianceStatusResponseDataStep": ".get_compliance_status_response_data_step",
+ "PostComplianceApplicationsIdRefreshResponse": ".post_compliance_applications_id_refresh_response",
+ "ResubmitComplianceResponse": ".resubmit_compliance_response",
+ "SubmitComplianceRequestNumberType": ".submit_compliance_request_number_type",
+ "SubmitComplianceRequestUserType": ".submit_compliance_request_user_type",
+ "SubmitComplianceResponse": ".submit_compliance_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetComplianceRequirementsRequestNumberType",
+ "GetComplianceRequirementsRequestUserType",
+ "GetComplianceRequirementsResponse",
+ "GetComplianceStatusRequestNumberType",
+ "GetComplianceStatusRequestUserType",
+ "GetComplianceStatusResponse",
+ "GetComplianceStatusResponseData",
+ "GetComplianceStatusResponseDataStep",
+ "PostComplianceApplicationsIdRefreshResponse",
+ "ResubmitComplianceResponse",
+ "SubmitComplianceRequestNumberType",
+ "SubmitComplianceRequestUserType",
+ "SubmitComplianceResponse",
+]
diff --git a/src/smallestai/atoms/compliance/types/post_compliance_applications_request_number_type.py b/src/smallestai/atoms/compliance/types/post_compliance_applications_request_number_type.py
deleted file mode 100644
index cfbe154d..00000000
--- a/src/smallestai/atoms/compliance/types/post_compliance_applications_request_number_type.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-PostComplianceApplicationsRequestNumberType = typing.Union[typing.Literal["local", "mobile", "tollfree"], typing.Any]
diff --git a/src/smallestai/atoms/compliance/types/post_compliance_applications_request_user_type.py b/src/smallestai/atoms/compliance/types/post_compliance_applications_request_user_type.py
deleted file mode 100644
index 57d901d0..00000000
--- a/src/smallestai/atoms/compliance/types/post_compliance_applications_request_user_type.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-PostComplianceApplicationsRequestUserType = typing.Union[typing.Literal["individual", "business"], typing.Any]
diff --git a/src/smallestai/atoms/compliance/types/post_compliance_applications_response.py b/src/smallestai/atoms/compliance/types/post_compliance_applications_response.py
deleted file mode 100644
index 04a0ece2..00000000
--- a/src/smallestai/atoms/compliance/types/post_compliance_applications_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.compliance_application import ComplianceApplication
-
-
-class PostComplianceApplicationsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[ComplianceApplication] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/concurrency/__init__.py b/src/smallestai/atoms/concurrency/__init__.py
new file mode 100644
index 00000000..c287d454
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/__init__.py
@@ -0,0 +1,55 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ GetConcurrencyResponse,
+ GetConcurrencyResponseData,
+ GetConcurrencyResponseDataAgentsItem,
+ UpdateConcurrencyReservationsRequestReservationsItem,
+ UpdateConcurrencyReservationsResponse,
+ UpdateConcurrencyReservationsResponseData,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetConcurrencyResponse": ".types",
+ "GetConcurrencyResponseData": ".types",
+ "GetConcurrencyResponseDataAgentsItem": ".types",
+ "UpdateConcurrencyReservationsRequestReservationsItem": ".types",
+ "UpdateConcurrencyReservationsResponse": ".types",
+ "UpdateConcurrencyReservationsResponseData": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetConcurrencyResponse",
+ "GetConcurrencyResponseData",
+ "GetConcurrencyResponseDataAgentsItem",
+ "UpdateConcurrencyReservationsRequestReservationsItem",
+ "UpdateConcurrencyReservationsResponse",
+ "UpdateConcurrencyReservationsResponseData",
+]
diff --git a/src/smallestai/atoms/concurrency/client.py b/src/smallestai/atoms/concurrency/client.py
new file mode 100644
index 00000000..2bf92744
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/client.py
@@ -0,0 +1,215 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from .raw_client import AsyncRawConcurrencyClient, RawConcurrencyClient
+from .types.get_concurrency_response import GetConcurrencyResponse
+from .types.update_concurrency_reservations_request_reservations_item import (
+ UpdateConcurrencyReservationsRequestReservationsItem,
+)
+from .types.update_concurrency_reservations_response import UpdateConcurrencyReservationsResponse
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class ConcurrencyClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawConcurrencyClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawConcurrencyClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawConcurrencyClient
+ """
+ return self._raw_client
+
+ def get_concurrency(self, *, request_options: typing.Optional[RequestOptions] = None) -> GetConcurrencyResponse:
+ """
+ Returns the organization's overall concurrency limit, how much is reserved across all agents, the remaining unreserved pool, and the per-agent reservation breakdown per call channel.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetConcurrencyResponse
+ Concurrency overview
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.concurrency.get_concurrency()
+ """
+ _response = self._raw_client.get_concurrency(request_options=request_options)
+ return _response.data
+
+ def update_concurrency_reservations(
+ self,
+ *,
+ reservations: typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UpdateConcurrencyReservationsResponse:
+ """
+ Updates concurrency reservations for one or more agents in a single request. Replaces the existing reservation values for each specified agent. **Admin role required.**
+
+ Parameters
+ ----------
+ reservations : typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem]
+ Array of agent reservations to update
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UpdateConcurrencyReservationsResponse
+ Reservations updated
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+ from smallestai.atoms.concurrency import (
+ UpdateConcurrencyReservationsRequestReservationsItem,
+ )
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.concurrency.update_concurrency_reservations(
+ reservations=[
+ UpdateConcurrencyReservationsRequestReservationsItem(
+ agent_id="agentId",
+ webcall=1,
+ outbound=1,
+ inbound=1,
+ chat=1,
+ )
+ ],
+ )
+ """
+ _response = self._raw_client.update_concurrency_reservations(
+ reservations=reservations, request_options=request_options
+ )
+ return _response.data
+
+
+class AsyncConcurrencyClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawConcurrencyClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawConcurrencyClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawConcurrencyClient
+ """
+ return self._raw_client
+
+ async def get_concurrency(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetConcurrencyResponse:
+ """
+ Returns the organization's overall concurrency limit, how much is reserved across all agents, the remaining unreserved pool, and the per-agent reservation breakdown per call channel.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetConcurrencyResponse
+ Concurrency overview
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.concurrency.get_concurrency()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_concurrency(request_options=request_options)
+ return _response.data
+
+ async def update_concurrency_reservations(
+ self,
+ *,
+ reservations: typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UpdateConcurrencyReservationsResponse:
+ """
+ Updates concurrency reservations for one or more agents in a single request. Replaces the existing reservation values for each specified agent. **Admin role required.**
+
+ Parameters
+ ----------
+ reservations : typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem]
+ Array of agent reservations to update
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UpdateConcurrencyReservationsResponse
+ Reservations updated
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+ from smallestai.atoms.concurrency import (
+ UpdateConcurrencyReservationsRequestReservationsItem,
+ )
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.concurrency.update_concurrency_reservations(
+ reservations=[
+ UpdateConcurrencyReservationsRequestReservationsItem(
+ agent_id="agentId",
+ webcall=1,
+ outbound=1,
+ inbound=1,
+ chat=1,
+ )
+ ],
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.update_concurrency_reservations(
+ reservations=reservations, request_options=request_options
+ )
+ return _response.data
diff --git a/src/smallestai/atoms/concurrency/raw_client.py b/src/smallestai/atoms/concurrency/raw_client.py
new file mode 100644
index 00000000..1648ab01
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/raw_client.py
@@ -0,0 +1,365 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.serialization import convert_and_respect_annotation_metadata
+from ...core.unchecked_base_model import construct_type
+from ..errors.bad_request_error import BadRequestError
+from ..errors.forbidden_error import ForbiddenError
+from ..errors.internal_server_error import InternalServerError
+from ..errors.unauthorized_error import UnauthorizedError
+from .types.get_concurrency_response import GetConcurrencyResponse
+from .types.update_concurrency_reservations_request_reservations_item import (
+ UpdateConcurrencyReservationsRequestReservationsItem,
+)
+from .types.update_concurrency_reservations_response import UpdateConcurrencyReservationsResponse
+from pydantic import ValidationError
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class RawConcurrencyClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def get_concurrency(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetConcurrencyResponse]:
+ """
+ Returns the organization's overall concurrency limit, how much is reserved across all agents, the remaining unreserved pool, and the per-agent reservation breakdown per call channel.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetConcurrencyResponse]
+ Concurrency overview
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "concurrency",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetConcurrencyResponse,
+ construct_type(
+ type_=GetConcurrencyResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def update_concurrency_reservations(
+ self,
+ *,
+ reservations: typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[UpdateConcurrencyReservationsResponse]:
+ """
+ Updates concurrency reservations for one or more agents in a single request. Replaces the existing reservation values for each specified agent. **Admin role required.**
+
+ Parameters
+ ----------
+ reservations : typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem]
+ Array of agent reservations to update
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[UpdateConcurrencyReservationsResponse]
+ Reservations updated
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "concurrency/reservations",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PUT",
+ json={
+ "reservations": convert_and_respect_annotation_metadata(
+ object_=reservations,
+ annotation=typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem],
+ direction="write",
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ UpdateConcurrencyReservationsResponse,
+ construct_type(
+ type_=UpdateConcurrencyReservationsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawConcurrencyClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def get_concurrency(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetConcurrencyResponse]:
+ """
+ Returns the organization's overall concurrency limit, how much is reserved across all agents, the remaining unreserved pool, and the per-agent reservation breakdown per call channel.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetConcurrencyResponse]
+ Concurrency overview
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "concurrency",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetConcurrencyResponse,
+ construct_type(
+ type_=GetConcurrencyResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def update_concurrency_reservations(
+ self,
+ *,
+ reservations: typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[UpdateConcurrencyReservationsResponse]:
+ """
+ Updates concurrency reservations for one or more agents in a single request. Replaces the existing reservation values for each specified agent. **Admin role required.**
+
+ Parameters
+ ----------
+ reservations : typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem]
+ Array of agent reservations to update
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[UpdateConcurrencyReservationsResponse]
+ Reservations updated
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "concurrency/reservations",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="PUT",
+ json={
+ "reservations": convert_and_respect_annotation_metadata(
+ object_=reservations,
+ annotation=typing.Sequence[UpdateConcurrencyReservationsRequestReservationsItem],
+ direction="write",
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ UpdateConcurrencyReservationsResponse,
+ construct_type(
+ type_=UpdateConcurrencyReservationsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/concurrency/types/__init__.py b/src/smallestai/atoms/concurrency/types/__init__.py
new file mode 100644
index 00000000..49aebd61
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/types/__init__.py
@@ -0,0 +1,55 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_concurrency_response import GetConcurrencyResponse
+ from .get_concurrency_response_data import GetConcurrencyResponseData
+ from .get_concurrency_response_data_agents_item import GetConcurrencyResponseDataAgentsItem
+ from .update_concurrency_reservations_request_reservations_item import (
+ UpdateConcurrencyReservationsRequestReservationsItem,
+ )
+ from .update_concurrency_reservations_response import UpdateConcurrencyReservationsResponse
+ from .update_concurrency_reservations_response_data import UpdateConcurrencyReservationsResponseData
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetConcurrencyResponse": ".get_concurrency_response",
+ "GetConcurrencyResponseData": ".get_concurrency_response_data",
+ "GetConcurrencyResponseDataAgentsItem": ".get_concurrency_response_data_agents_item",
+ "UpdateConcurrencyReservationsRequestReservationsItem": ".update_concurrency_reservations_request_reservations_item",
+ "UpdateConcurrencyReservationsResponse": ".update_concurrency_reservations_response",
+ "UpdateConcurrencyReservationsResponseData": ".update_concurrency_reservations_response_data",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetConcurrencyResponse",
+ "GetConcurrencyResponseData",
+ "GetConcurrencyResponseDataAgentsItem",
+ "UpdateConcurrencyReservationsRequestReservationsItem",
+ "UpdateConcurrencyReservationsResponse",
+ "UpdateConcurrencyReservationsResponseData",
+]
diff --git a/src/smallestai/atoms/concurrency/types/get_concurrency_response.py b/src/smallestai/atoms/concurrency/types/get_concurrency_response.py
new file mode 100644
index 00000000..1b2d319d
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/types/get_concurrency_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_concurrency_response_data import GetConcurrencyResponseData
+
+
+class GetConcurrencyResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[GetConcurrencyResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/concurrency/types/get_concurrency_response_data.py b/src/smallestai/atoms/concurrency/types/get_concurrency_response_data.py
new file mode 100644
index 00000000..55360806
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/types/get_concurrency_response_data.py
@@ -0,0 +1,40 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .get_concurrency_response_data_agents_item import GetConcurrencyResponseDataAgentsItem
+
+
+class GetConcurrencyResponseData(UncheckedBaseModel):
+ org_limit: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="orgLimit"),
+ pydantic.Field(alias="orgLimit", description="Total concurrent calls allowed for this organization"),
+ ] = None
+ total_reserved: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="totalReserved"),
+ pydantic.Field(alias="totalReserved", description="Sum of all agent reservations across all channels"),
+ ] = None
+ unreserved_pool: typing_extensions.Annotated[
+ typing.Optional[float],
+ FieldMetadata(alias="unreservedPool"),
+ pydantic.Field(
+ alias="unreservedPool", description="orgLimit minus totalReserved โ capacity available to any agent"
+ ),
+ ] = None
+ agents: typing.Optional[typing.List[GetConcurrencyResponseDataAgentsItem]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/concurrency/types/get_concurrency_response_data_agents_item.py b/src/smallestai/atoms/concurrency/types/get_concurrency_response_data_agents_item.py
new file mode 100644
index 00000000..8203923e
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/types/get_concurrency_response_data_agents_item.py
@@ -0,0 +1,51 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class GetConcurrencyResponseDataAgentsItem(UncheckedBaseModel):
+ agent_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="agentId"),
+ pydantic.Field(alias="agentId", description="Agent ObjectId"),
+ ] = None
+ agent_name: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="agentName"), pydantic.Field(alias="agentName")
+ ] = None
+ avatar_url: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="avatarUrl"), pydantic.Field(alias="avatarUrl")
+ ] = None
+ webcall: typing.Optional[float] = pydantic.Field(default=None)
+ """
+ Reserved webcall slots
+ """
+
+ outbound: typing.Optional[float] = pydantic.Field(default=None)
+ """
+ Reserved outbound call slots
+ """
+
+ inbound: typing.Optional[float] = pydantic.Field(default=None)
+ """
+ Reserved inbound call slots
+ """
+
+ chat: typing.Optional[float] = pydantic.Field(default=None)
+ """
+ Reserved chat slots
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/webhooks/types/post_webhook_request_events_item.py b/src/smallestai/atoms/concurrency/types/update_concurrency_reservations_request_reservations_item.py
similarity index 60%
rename from src/smallestai/atoms/webhooks/types/post_webhook_request_events_item.py
rename to src/smallestai/atoms/concurrency/types/update_concurrency_reservations_request_reservations_item.py
index c4e423d2..196543c4 100644
--- a/src/smallestai/atoms/webhooks/types/post_webhook_request_events_item.py
+++ b/src/smallestai/atoms/concurrency/types/update_concurrency_reservations_request_reservations_item.py
@@ -7,18 +7,31 @@
from ....core.pydantic_utilities import IS_PYDANTIC_V2
from ....core.serialization import FieldMetadata
from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_webhook_request_events_item_event_type import PostWebhookRequestEventsItemEventType
-class PostWebhookRequestEventsItem(UncheckedBaseModel):
+class UpdateConcurrencyReservationsRequestReservationsItem(UncheckedBaseModel):
agent_id: typing_extensions.Annotated[
- str, FieldMetadata(alias="agentId"), pydantic.Field(alias="agentId", description="The ID of the agent")
- ]
- event_type: typing_extensions.Annotated[
- PostWebhookRequestEventsItemEventType,
- FieldMetadata(alias="eventType"),
- pydantic.Field(alias="eventType", description="The type of event to subscribe to"),
+ str, FieldMetadata(alias="agentId"), pydantic.Field(alias="agentId", description="Agent ObjectId")
]
+ webcall: int = pydantic.Field()
+ """
+ Reserved webcall slots
+ """
+
+ outbound: int = pydantic.Field()
+ """
+ Reserved outbound call slots
+ """
+
+ inbound: int = pydantic.Field()
+ """
+ Reserved inbound call slots
+ """
+
+ chat: int = pydantic.Field()
+ """
+ Reserved chat slots
+ """
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/concurrency/types/update_concurrency_reservations_response.py b/src/smallestai/atoms/concurrency/types/update_concurrency_reservations_response.py
new file mode 100644
index 00000000..f8afc2ee
--- /dev/null
+++ b/src/smallestai/atoms/concurrency/types/update_concurrency_reservations_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .update_concurrency_reservations_response_data import UpdateConcurrencyReservationsResponseData
+
+
+class UpdateConcurrencyReservationsResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[UpdateConcurrencyReservationsResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/webhooks/types/delete_webhook_id_response.py b/src/smallestai/atoms/concurrency/types/update_concurrency_reservations_response_data.py
similarity index 74%
rename from src/smallestai/atoms/webhooks/types/delete_webhook_id_response.py
rename to src/smallestai/atoms/concurrency/types/update_concurrency_reservations_response_data.py
index 6e387c43..69856cba 100644
--- a/src/smallestai/atoms/webhooks/types/delete_webhook_id_response.py
+++ b/src/smallestai/atoms/concurrency/types/update_concurrency_reservations_response_data.py
@@ -7,11 +7,10 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class DeleteWebhookIdResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[str] = pydantic.Field(default=None)
+class UpdateConcurrencyReservationsResponseData(UncheckedBaseModel):
+ updated: typing.Optional[float] = pydantic.Field(default=None)
"""
- Success message
+ Count of agent reservations updated
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/conversations/__init__.py b/src/smallestai/atoms/conversations/__init__.py
index 5cde0202..dbd075c5 100644
--- a/src/smallestai/atoms/conversations/__init__.py
+++ b/src/smallestai/atoms/conversations/__init__.py
@@ -2,3 +2,60 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ CancelConversationsResponse,
+ CancelConversationsResponseData,
+ CancelQueuedConversationsResponse,
+ CancelQueuedConversationsResponseData,
+ GetConversationCallIdRecordingDownloadUrlResponse,
+ GetConversationCallIdRecordingDownloadUrlResponseData,
+ ListRetryAttemptsConversationsResponse,
+ ListRetryAttemptsConversationsResponseData,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "CancelConversationsResponse": ".types",
+ "CancelConversationsResponseData": ".types",
+ "CancelQueuedConversationsResponse": ".types",
+ "CancelQueuedConversationsResponseData": ".types",
+ "GetConversationCallIdRecordingDownloadUrlResponse": ".types",
+ "GetConversationCallIdRecordingDownloadUrlResponseData": ".types",
+ "ListRetryAttemptsConversationsResponse": ".types",
+ "ListRetryAttemptsConversationsResponseData": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CancelConversationsResponse",
+ "CancelConversationsResponseData",
+ "CancelQueuedConversationsResponse",
+ "CancelQueuedConversationsResponseData",
+ "GetConversationCallIdRecordingDownloadUrlResponse",
+ "GetConversationCallIdRecordingDownloadUrlResponseData",
+ "ListRetryAttemptsConversationsResponse",
+ "ListRetryAttemptsConversationsResponseData",
+]
diff --git a/src/smallestai/atoms/conversations/types/__init__.py b/src/smallestai/atoms/conversations/types/__init__.py
index 5cde0202..a546a02a 100644
--- a/src/smallestai/atoms/conversations/types/__init__.py
+++ b/src/smallestai/atoms/conversations/types/__init__.py
@@ -2,3 +2,62 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .cancel_conversations_response import CancelConversationsResponse
+ from .cancel_conversations_response_data import CancelConversationsResponseData
+ from .cancel_queued_conversations_response import CancelQueuedConversationsResponse
+ from .cancel_queued_conversations_response_data import CancelQueuedConversationsResponseData
+ from .get_conversation_call_id_recording_download_url_response import (
+ GetConversationCallIdRecordingDownloadUrlResponse,
+ )
+ from .get_conversation_call_id_recording_download_url_response_data import (
+ GetConversationCallIdRecordingDownloadUrlResponseData,
+ )
+ from .list_retry_attempts_conversations_response import ListRetryAttemptsConversationsResponse
+ from .list_retry_attempts_conversations_response_data import ListRetryAttemptsConversationsResponseData
+_dynamic_imports: typing.Dict[str, str] = {
+ "CancelConversationsResponse": ".cancel_conversations_response",
+ "CancelConversationsResponseData": ".cancel_conversations_response_data",
+ "CancelQueuedConversationsResponse": ".cancel_queued_conversations_response",
+ "CancelQueuedConversationsResponseData": ".cancel_queued_conversations_response_data",
+ "GetConversationCallIdRecordingDownloadUrlResponse": ".get_conversation_call_id_recording_download_url_response",
+ "GetConversationCallIdRecordingDownloadUrlResponseData": ".get_conversation_call_id_recording_download_url_response_data",
+ "ListRetryAttemptsConversationsResponse": ".list_retry_attempts_conversations_response",
+ "ListRetryAttemptsConversationsResponseData": ".list_retry_attempts_conversations_response_data",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CancelConversationsResponse",
+ "CancelConversationsResponseData",
+ "CancelQueuedConversationsResponse",
+ "CancelQueuedConversationsResponseData",
+ "GetConversationCallIdRecordingDownloadUrlResponse",
+ "GetConversationCallIdRecordingDownloadUrlResponseData",
+ "ListRetryAttemptsConversationsResponse",
+ "ListRetryAttemptsConversationsResponseData",
+]
diff --git a/src/smallestai/atoms/disposition_metric_templates/__init__.py b/src/smallestai/atoms/disposition_metric_templates/__init__.py
new file mode 100644
index 00000000..8d833481
--- /dev/null
+++ b/src/smallestai/atoms/disposition_metric_templates/__init__.py
@@ -0,0 +1,46 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ ListDispositionMetricTemplatesResponse,
+ ListDispositionMetricTemplatesResponseDataItem,
+ ListDispositionMetricTemplatesResponseDataItemDispositionMetricType,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ListDispositionMetricTemplatesResponse": ".types",
+ "ListDispositionMetricTemplatesResponseDataItem": ".types",
+ "ListDispositionMetricTemplatesResponseDataItemDispositionMetricType": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ListDispositionMetricTemplatesResponse",
+ "ListDispositionMetricTemplatesResponseDataItem",
+ "ListDispositionMetricTemplatesResponseDataItemDispositionMetricType",
+]
diff --git a/src/smallestai/atoms/disposition_metric_templates/client.py b/src/smallestai/atoms/disposition_metric_templates/client.py
new file mode 100644
index 00000000..994aa5bd
--- /dev/null
+++ b/src/smallestai/atoms/disposition_metric_templates/client.py
@@ -0,0 +1,104 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from .raw_client import AsyncRawDispositionMetricTemplatesClient, RawDispositionMetricTemplatesClient
+from .types.list_disposition_metric_templates_response import ListDispositionMetricTemplatesResponse
+
+
+class DispositionMetricTemplatesClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawDispositionMetricTemplatesClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawDispositionMetricTemplatesClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawDispositionMetricTemplatesClient
+ """
+ return self._raw_client
+
+ def list_disposition_metric_templates(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ListDispositionMetricTemplatesResponse:
+ """
+ Returns all available disposition metric templates. These reusable definitions are used to populate the post-call analytics metric picker when configuring an agent's `postCallAnalyticsConfig`. Only a user token is required โ no organization context needed.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListDispositionMetricTemplatesResponse
+ Array of disposition metric templates
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.disposition_metric_templates.list_disposition_metric_templates()
+ """
+ _response = self._raw_client.list_disposition_metric_templates(request_options=request_options)
+ return _response.data
+
+
+class AsyncDispositionMetricTemplatesClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawDispositionMetricTemplatesClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawDispositionMetricTemplatesClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawDispositionMetricTemplatesClient
+ """
+ return self._raw_client
+
+ async def list_disposition_metric_templates(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ListDispositionMetricTemplatesResponse:
+ """
+ Returns all available disposition metric templates. These reusable definitions are used to populate the post-call analytics metric picker when configuring an agent's `postCallAnalyticsConfig`. Only a user token is required โ no organization context needed.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListDispositionMetricTemplatesResponse
+ Array of disposition metric templates
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.disposition_metric_templates.list_disposition_metric_templates()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.list_disposition_metric_templates(request_options=request_options)
+ return _response.data
diff --git a/src/smallestai/atoms/disposition_metric_templates/raw_client.py b/src/smallestai/atoms/disposition_metric_templates/raw_client.py
new file mode 100644
index 00000000..e0dcc1f1
--- /dev/null
+++ b/src/smallestai/atoms/disposition_metric_templates/raw_client.py
@@ -0,0 +1,151 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.unchecked_base_model import construct_type
+from ..errors.internal_server_error import InternalServerError
+from ..errors.unauthorized_error import UnauthorizedError
+from .types.list_disposition_metric_templates_response import ListDispositionMetricTemplatesResponse
+from pydantic import ValidationError
+
+
+class RawDispositionMetricTemplatesClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def list_disposition_metric_templates(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[ListDispositionMetricTemplatesResponse]:
+ """
+ Returns all available disposition metric templates. These reusable definitions are used to populate the post-call analytics metric picker when configuring an agent's `postCallAnalyticsConfig`. Only a user token is required โ no organization context needed.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[ListDispositionMetricTemplatesResponse]
+ Array of disposition metric templates
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "disposition-metric-templates",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListDispositionMetricTemplatesResponse,
+ construct_type(
+ type_=ListDispositionMetricTemplatesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawDispositionMetricTemplatesClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def list_disposition_metric_templates(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[ListDispositionMetricTemplatesResponse]:
+ """
+ Returns all available disposition metric templates. These reusable definitions are used to populate the post-call analytics metric picker when configuring an agent's `postCallAnalyticsConfig`. Only a user token is required โ no organization context needed.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ListDispositionMetricTemplatesResponse]
+ Array of disposition metric templates
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "disposition-metric-templates",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListDispositionMetricTemplatesResponse,
+ construct_type(
+ type_=ListDispositionMetricTemplatesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/disposition_metric_templates/types/__init__.py b/src/smallestai/atoms/disposition_metric_templates/types/__init__.py
new file mode 100644
index 00000000..6b88b223
--- /dev/null
+++ b/src/smallestai/atoms/disposition_metric_templates/types/__init__.py
@@ -0,0 +1,46 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .list_disposition_metric_templates_response import ListDispositionMetricTemplatesResponse
+ from .list_disposition_metric_templates_response_data_item import ListDispositionMetricTemplatesResponseDataItem
+ from .list_disposition_metric_templates_response_data_item_disposition_metric_type import (
+ ListDispositionMetricTemplatesResponseDataItemDispositionMetricType,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ListDispositionMetricTemplatesResponse": ".list_disposition_metric_templates_response",
+ "ListDispositionMetricTemplatesResponseDataItem": ".list_disposition_metric_templates_response_data_item",
+ "ListDispositionMetricTemplatesResponseDataItemDispositionMetricType": ".list_disposition_metric_templates_response_data_item_disposition_metric_type",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ListDispositionMetricTemplatesResponse",
+ "ListDispositionMetricTemplatesResponseDataItem",
+ "ListDispositionMetricTemplatesResponseDataItemDispositionMetricType",
+]
diff --git a/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response.py b/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response.py
new file mode 100644
index 00000000..12938cd8
--- /dev/null
+++ b/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .list_disposition_metric_templates_response_data_item import ListDispositionMetricTemplatesResponseDataItem
+
+
+class ListDispositionMetricTemplatesResponse(UncheckedBaseModel):
+ success: typing.Optional[bool] = None
+ data: typing.Optional[typing.List[ListDispositionMetricTemplatesResponseDataItem]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item.py b/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item.py
new file mode 100644
index 00000000..7d6e9de8
--- /dev/null
+++ b/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item.py
@@ -0,0 +1,45 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.serialization import FieldMetadata
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .list_disposition_metric_templates_response_data_item_disposition_metric_type import (
+ ListDispositionMetricTemplatesResponseDataItemDispositionMetricType,
+)
+
+
+class ListDispositionMetricTemplatesResponseDataItem(UncheckedBaseModel):
+ identifier: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Unique snake_case key (lowercase letters, digits, and underscores only)
+ """
+
+ disposition_metric_prompt: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="dispositionMetricPrompt"),
+ pydantic.Field(
+ alias="dispositionMetricPrompt",
+ description="AI prompt used to extract this metric from the call transcript",
+ ),
+ ] = None
+ disposition_metric_type: typing_extensions.Annotated[
+ typing.Optional[ListDispositionMetricTemplatesResponseDataItemDispositionMetricType],
+ FieldMetadata(alias="dispositionMetricType"),
+ pydantic.Field(
+ alias="dispositionMetricType",
+ description="How the extracted value is interpreted:\n- `STRING` โ free-text output\n- `BOOLEAN` โ true/false outcome\n- `INTEGER` โ whole number value\n- `ENUM` โ one of a predefined set of choices\n- `DATETIME` โ a date/time value",
+ ),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item_disposition_metric_type.py b/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item_disposition_metric_type.py
new file mode 100644
index 00000000..464ed1b2
--- /dev/null
+++ b/src/smallestai/atoms/disposition_metric_templates/types/list_disposition_metric_templates_response_data_item_disposition_metric_type.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+ListDispositionMetricTemplatesResponseDataItemDispositionMetricType = typing.Union[
+ typing.Literal["STRING", "BOOLEAN", "INTEGER", "ENUM", "DATETIME"], typing.Any
+]
diff --git a/src/smallestai/atoms/dnc/__init__.py b/src/smallestai/atoms/dnc/__init__.py
index 5cde0202..7e2024a0 100644
--- a/src/smallestai/atoms/dnc/__init__.py
+++ b/src/smallestai/atoms/dnc/__init__.py
@@ -2,3 +2,54 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ ListDncRequestSortField,
+ ListDncRequestSortOrder,
+ ListDncResponse,
+ ListDncResponseData,
+ ListDncResponseDataEntriesItem,
+ ListDncResponseDataPagination,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ListDncRequestSortField": ".types",
+ "ListDncRequestSortOrder": ".types",
+ "ListDncResponse": ".types",
+ "ListDncResponseData": ".types",
+ "ListDncResponseDataEntriesItem": ".types",
+ "ListDncResponseDataPagination": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ListDncRequestSortField",
+ "ListDncRequestSortOrder",
+ "ListDncResponse",
+ "ListDncResponseData",
+ "ListDncResponseDataEntriesItem",
+ "ListDncResponseDataPagination",
+]
diff --git a/src/smallestai/atoms/dnc/types/__init__.py b/src/smallestai/atoms/dnc/types/__init__.py
index 5cde0202..ba2bc633 100644
--- a/src/smallestai/atoms/dnc/types/__init__.py
+++ b/src/smallestai/atoms/dnc/types/__init__.py
@@ -2,3 +2,52 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .list_dnc_request_sort_field import ListDncRequestSortField
+ from .list_dnc_request_sort_order import ListDncRequestSortOrder
+ from .list_dnc_response import ListDncResponse
+ from .list_dnc_response_data import ListDncResponseData
+ from .list_dnc_response_data_entries_item import ListDncResponseDataEntriesItem
+ from .list_dnc_response_data_pagination import ListDncResponseDataPagination
+_dynamic_imports: typing.Dict[str, str] = {
+ "ListDncRequestSortField": ".list_dnc_request_sort_field",
+ "ListDncRequestSortOrder": ".list_dnc_request_sort_order",
+ "ListDncResponse": ".list_dnc_response",
+ "ListDncResponseData": ".list_dnc_response_data",
+ "ListDncResponseDataEntriesItem": ".list_dnc_response_data_entries_item",
+ "ListDncResponseDataPagination": ".list_dnc_response_data_pagination",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ListDncRequestSortField",
+ "ListDncRequestSortOrder",
+ "ListDncResponse",
+ "ListDncResponseData",
+ "ListDncResponseDataEntriesItem",
+ "ListDncResponseDataPagination",
+]
diff --git a/src/smallestai/atoms/errors/__init__.py b/src/smallestai/atoms/errors/__init__.py
index 5cde0202..e6dc96d8 100644
--- a/src/smallestai/atoms/errors/__init__.py
+++ b/src/smallestai/atoms/errors/__init__.py
@@ -2,3 +2,64 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .bad_gateway_error import BadGatewayError
+ from .bad_request_error import BadRequestError
+ from .conflict_error import ConflictError
+ from .forbidden_error import ForbiddenError
+ from .internal_server_error import InternalServerError
+ from .locked_error import LockedError
+ from .not_found_error import NotFoundError
+ from .too_many_requests_error import TooManyRequestsError
+ from .unauthorized_error import UnauthorizedError
+ from .unprocessable_entity_error import UnprocessableEntityError
+_dynamic_imports: typing.Dict[str, str] = {
+ "BadGatewayError": ".bad_gateway_error",
+ "BadRequestError": ".bad_request_error",
+ "ConflictError": ".conflict_error",
+ "ForbiddenError": ".forbidden_error",
+ "InternalServerError": ".internal_server_error",
+ "LockedError": ".locked_error",
+ "NotFoundError": ".not_found_error",
+ "TooManyRequestsError": ".too_many_requests_error",
+ "UnauthorizedError": ".unauthorized_error",
+ "UnprocessableEntityError": ".unprocessable_entity_error",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "BadGatewayError",
+ "BadRequestError",
+ "ConflictError",
+ "ForbiddenError",
+ "InternalServerError",
+ "LockedError",
+ "NotFoundError",
+ "TooManyRequestsError",
+ "UnauthorizedError",
+ "UnprocessableEntityError",
+]
diff --git a/src/smallestai/atoms/errors/locked_error.py b/src/smallestai/atoms/errors/locked_error.py
new file mode 100644
index 00000000..212f85fc
--- /dev/null
+++ b/src/smallestai/atoms/errors/locked_error.py
@@ -0,0 +1,11 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.api_error import ApiError
+from ..types.locked_error_response import LockedErrorResponse
+
+
+class LockedError(ApiError):
+ def __init__(self, body: LockedErrorResponse, headers: typing.Optional[typing.Dict[str, str]] = None):
+ super().__init__(status_code=423, headers=headers, body=body)
diff --git a/src/smallestai/atoms/integrations/__init__.py b/src/smallestai/atoms/integrations/__init__.py
new file mode 100644
index 00000000..227cb59c
--- /dev/null
+++ b/src/smallestai/atoms/integrations/__init__.py
@@ -0,0 +1,37 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import GetWebEngageDetailsResponse, ModifyWebEngageIntegrationResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetWebEngageDetailsResponse": ".types",
+ "ModifyWebEngageIntegrationResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = ["GetWebEngageDetailsResponse", "ModifyWebEngageIntegrationResponse"]
diff --git a/src/smallestai/atoms/integrations/client.py b/src/smallestai/atoms/integrations/client.py
new file mode 100644
index 00000000..4801a9fb
--- /dev/null
+++ b/src/smallestai/atoms/integrations/client.py
@@ -0,0 +1,215 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from ..types.web_engage_integration_set import WebEngageIntegrationSet
+from .raw_client import AsyncRawIntegrationsClient, RawIntegrationsClient
+from .types.get_web_engage_details_response import GetWebEngageDetailsResponse
+from .types.modify_web_engage_integration_response import ModifyWebEngageIntegrationResponse
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class IntegrationsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawIntegrationsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawIntegrationsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawIntegrationsClient
+ """
+ return self._raw_client
+
+ def modify_web_engage_integration(
+ self,
+ *,
+ integration_sets: typing.Sequence[WebEngageIntegrationSet],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ModifyWebEngageIntegrationResponse:
+ """
+ Creates or updates the WebEngage integration for the organization. Replaces the existing integration configuration with the provided credential set(s).
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ integration_sets : typing.Sequence[WebEngageIntegrationSet]
+ One or more WebEngage credential sets
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ModifyWebEngageIntegrationResponse
+ Integration created or updated
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+ from smallestai.atoms import WebEngageIntegrationSet
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.integrations.modify_web_engage_integration(
+ integration_sets=[
+ WebEngageIntegrationSet(
+ license_code="licenseCode",
+ environment="environment",
+ api_key="apiKey",
+ )
+ ],
+ )
+ """
+ _response = self._raw_client.modify_web_engage_integration(
+ integration_sets=integration_sets, request_options=request_options
+ )
+ return _response.data
+
+ def get_web_engage_details(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetWebEngageDetailsResponse:
+ """
+ Returns the current WebEngage integration configuration for the organization.
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetWebEngageDetailsResponse
+ WebEngage integration details
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.integrations.get_web_engage_details()
+ """
+ _response = self._raw_client.get_web_engage_details(request_options=request_options)
+ return _response.data
+
+
+class AsyncIntegrationsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawIntegrationsClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawIntegrationsClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawIntegrationsClient
+ """
+ return self._raw_client
+
+ async def modify_web_engage_integration(
+ self,
+ *,
+ integration_sets: typing.Sequence[WebEngageIntegrationSet],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ModifyWebEngageIntegrationResponse:
+ """
+ Creates or updates the WebEngage integration for the organization. Replaces the existing integration configuration with the provided credential set(s).
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ integration_sets : typing.Sequence[WebEngageIntegrationSet]
+ One or more WebEngage credential sets
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ModifyWebEngageIntegrationResponse
+ Integration created or updated
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+ from smallestai.atoms import WebEngageIntegrationSet
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.integrations.modify_web_engage_integration(
+ integration_sets=[
+ WebEngageIntegrationSet(
+ license_code="licenseCode",
+ environment="environment",
+ api_key="apiKey",
+ )
+ ],
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.modify_web_engage_integration(
+ integration_sets=integration_sets, request_options=request_options
+ )
+ return _response.data
+
+ async def get_web_engage_details(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetWebEngageDetailsResponse:
+ """
+ Returns the current WebEngage integration configuration for the organization.
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetWebEngageDetailsResponse
+ WebEngage integration details
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.integrations.get_web_engage_details()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_web_engage_details(request_options=request_options)
+ return _response.data
diff --git a/src/smallestai/atoms/integrations/raw_client.py b/src/smallestai/atoms/integrations/raw_client.py
new file mode 100644
index 00000000..0abd97f6
--- /dev/null
+++ b/src/smallestai/atoms/integrations/raw_client.py
@@ -0,0 +1,389 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.serialization import convert_and_respect_annotation_metadata
+from ...core.unchecked_base_model import construct_type
+from ..errors.bad_request_error import BadRequestError
+from ..errors.internal_server_error import InternalServerError
+from ..errors.not_found_error import NotFoundError
+from ..errors.unauthorized_error import UnauthorizedError
+from ..types.web_engage_integration_set import WebEngageIntegrationSet
+from .types.get_web_engage_details_response import GetWebEngageDetailsResponse
+from .types.modify_web_engage_integration_response import ModifyWebEngageIntegrationResponse
+from pydantic import ValidationError
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class RawIntegrationsClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def modify_web_engage_integration(
+ self,
+ *,
+ integration_sets: typing.Sequence[WebEngageIntegrationSet],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[ModifyWebEngageIntegrationResponse]:
+ """
+ Creates or updates the WebEngage integration for the organization. Replaces the existing integration configuration with the provided credential set(s).
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ integration_sets : typing.Sequence[WebEngageIntegrationSet]
+ One or more WebEngage credential sets
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[ModifyWebEngageIntegrationResponse]
+ Integration created or updated
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "integration/modify-webengage-integration",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "integrationSets": convert_and_respect_annotation_metadata(
+ object_=integration_sets, annotation=typing.Sequence[WebEngageIntegrationSet], direction="write"
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ModifyWebEngageIntegrationResponse,
+ construct_type(
+ type_=ModifyWebEngageIntegrationResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def get_web_engage_details(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetWebEngageDetailsResponse]:
+ """
+ Returns the current WebEngage integration configuration for the organization.
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[GetWebEngageDetailsResponse]
+ WebEngage integration details
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "integration/get-webengage-details",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetWebEngageDetailsResponse,
+ construct_type(
+ type_=GetWebEngageDetailsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawIntegrationsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def modify_web_engage_integration(
+ self,
+ *,
+ integration_sets: typing.Sequence[WebEngageIntegrationSet],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ModifyWebEngageIntegrationResponse]:
+ """
+ Creates or updates the WebEngage integration for the organization. Replaces the existing integration configuration with the provided credential set(s).
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ integration_sets : typing.Sequence[WebEngageIntegrationSet]
+ One or more WebEngage credential sets
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ModifyWebEngageIntegrationResponse]
+ Integration created or updated
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "integration/modify-webengage-integration",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "integrationSets": convert_and_respect_annotation_metadata(
+ object_=integration_sets, annotation=typing.Sequence[WebEngageIntegrationSet], direction="write"
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ModifyWebEngageIntegrationResponse,
+ construct_type(
+ type_=ModifyWebEngageIntegrationResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_web_engage_details(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetWebEngageDetailsResponse]:
+ """
+ Returns the current WebEngage integration configuration for the organization.
+
+ **Note:** This endpoint returns a direct JSON response โ not the standard `{ success, data }` wrapper used by other endpoints.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetWebEngageDetailsResponse]
+ WebEngage integration details
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "integration/get-webengage-details",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetWebEngageDetailsResponse,
+ construct_type(
+ type_=GetWebEngageDetailsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/integrations/types/__init__.py b/src/smallestai/atoms/integrations/types/__init__.py
new file mode 100644
index 00000000..9063224f
--- /dev/null
+++ b/src/smallestai/atoms/integrations/types/__init__.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_web_engage_details_response import GetWebEngageDetailsResponse
+ from .modify_web_engage_integration_response import ModifyWebEngageIntegrationResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetWebEngageDetailsResponse": ".get_web_engage_details_response",
+ "ModifyWebEngageIntegrationResponse": ".modify_web_engage_integration_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = ["GetWebEngageDetailsResponse", "ModifyWebEngageIntegrationResponse"]
diff --git a/src/smallestai/atoms/agents/types/create_with_ai_agents_response.py b/src/smallestai/atoms/integrations/types/get_web_engage_details_response.py
similarity index 71%
rename from src/smallestai/atoms/agents/types/create_with_ai_agents_response.py
rename to src/smallestai/atoms/integrations/types/get_web_engage_details_response.py
index 2236099b..056d2484 100644
--- a/src/smallestai/atoms/agents/types/create_with_ai_agents_response.py
+++ b/src/smallestai/atoms/integrations/types/get_web_engage_details_response.py
@@ -7,11 +7,11 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class CreateWithAiAgentsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[str] = pydantic.Field(default=None)
+class GetWebEngageDetailsResponse(UncheckedBaseModel):
+ message: typing.Optional[str] = None
+ data: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None)
"""
- Newly created agent ID.
+ Integration details object
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/webhooks/types/post_webhook_response.py b/src/smallestai/atoms/integrations/types/modify_web_engage_integration_response.py
similarity index 70%
rename from src/smallestai/atoms/webhooks/types/post_webhook_response.py
rename to src/smallestai/atoms/integrations/types/modify_web_engage_integration_response.py
index 47d9c350..06ce903f 100644
--- a/src/smallestai/atoms/webhooks/types/post_webhook_response.py
+++ b/src/smallestai/atoms/integrations/types/modify_web_engage_integration_response.py
@@ -7,11 +7,11 @@
from ....core.unchecked_base_model import UncheckedBaseModel
-class PostWebhookResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[str] = pydantic.Field(default=None)
+class ModifyWebEngageIntegrationResponse(UncheckedBaseModel):
+ message: typing.Optional[str] = None
+ data: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None)
"""
- The ID of the created webhook
+ Integration details object
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/atoms/knowledge_base/__init__.py b/src/smallestai/atoms/knowledge_base/__init__.py
index 5cde0202..60211ff9 100644
--- a/src/smallestai/atoms/knowledge_base/__init__.py
+++ b/src/smallestai/atoms/knowledge_base/__init__.py
@@ -2,3 +2,87 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ CreateKnowledgeBaseResponse,
+ DeleteKnowledgeBaseResponse,
+ DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse,
+ DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse,
+ ExtractSitemapUrlsKnowledgeBaseResponse,
+ ExtractSitemapUrlsKnowledgeBaseResponseData,
+ GetKnowledgeBaseResponse,
+ GetKnowledgebaseIdItemsResponse,
+ GetKnowledgebaseIdScrapedUrlsResponse,
+ GetKnowledgebaseIdScrapedUrlsResponseDataItem,
+ ListKnowledgeBaseResponse,
+ PostKnowledgebaseCompeleteFileUploadResponse,
+ PostKnowledgebaseGetPresignedUrlResponse,
+ PostKnowledgebaseGetPresignedUrlResponseData,
+ PostKnowledgebaseIdItemsUploadMediaResponse,
+ PostKnowledgebaseIdResponse,
+ ScrapeUrlsKnowledgeBaseResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateKnowledgeBaseResponse": ".types",
+ "DeleteKnowledgeBaseResponse": ".types",
+ "DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse": ".types",
+ "DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse": ".types",
+ "ExtractSitemapUrlsKnowledgeBaseResponse": ".types",
+ "ExtractSitemapUrlsKnowledgeBaseResponseData": ".types",
+ "GetKnowledgeBaseResponse": ".types",
+ "GetKnowledgebaseIdItemsResponse": ".types",
+ "GetKnowledgebaseIdScrapedUrlsResponse": ".types",
+ "GetKnowledgebaseIdScrapedUrlsResponseDataItem": ".types",
+ "ListKnowledgeBaseResponse": ".types",
+ "PostKnowledgebaseCompeleteFileUploadResponse": ".types",
+ "PostKnowledgebaseGetPresignedUrlResponse": ".types",
+ "PostKnowledgebaseGetPresignedUrlResponseData": ".types",
+ "PostKnowledgebaseIdItemsUploadMediaResponse": ".types",
+ "PostKnowledgebaseIdResponse": ".types",
+ "ScrapeUrlsKnowledgeBaseResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateKnowledgeBaseResponse",
+ "DeleteKnowledgeBaseResponse",
+ "DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse",
+ "DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse",
+ "ExtractSitemapUrlsKnowledgeBaseResponse",
+ "ExtractSitemapUrlsKnowledgeBaseResponseData",
+ "GetKnowledgeBaseResponse",
+ "GetKnowledgebaseIdItemsResponse",
+ "GetKnowledgebaseIdScrapedUrlsResponse",
+ "GetKnowledgebaseIdScrapedUrlsResponseDataItem",
+ "ListKnowledgeBaseResponse",
+ "PostKnowledgebaseCompeleteFileUploadResponse",
+ "PostKnowledgebaseGetPresignedUrlResponse",
+ "PostKnowledgebaseGetPresignedUrlResponseData",
+ "PostKnowledgebaseIdItemsUploadMediaResponse",
+ "PostKnowledgebaseIdResponse",
+ "ScrapeUrlsKnowledgeBaseResponse",
+]
diff --git a/src/smallestai/atoms/knowledge_base/types/__init__.py b/src/smallestai/atoms/knowledge_base/types/__init__.py
index 5cde0202..b9bd44b3 100644
--- a/src/smallestai/atoms/knowledge_base/types/__init__.py
+++ b/src/smallestai/atoms/knowledge_base/types/__init__.py
@@ -2,3 +2,89 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .create_knowledge_base_response import CreateKnowledgeBaseResponse
+ from .delete_knowledge_base_response import DeleteKnowledgeBaseResponse
+ from .delete_knowledgebase_knowledge_base_id_items_knowledge_base_item_id_response import (
+ DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse,
+ )
+ from .delete_knowledgebase_knowledge_base_id_scraped_urls_knowledge_base_scraped_urls_id_response import (
+ DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse,
+ )
+ from .extract_sitemap_urls_knowledge_base_response import ExtractSitemapUrlsKnowledgeBaseResponse
+ from .extract_sitemap_urls_knowledge_base_response_data import ExtractSitemapUrlsKnowledgeBaseResponseData
+ from .get_knowledge_base_response import GetKnowledgeBaseResponse
+ from .get_knowledgebase_id_items_response import GetKnowledgebaseIdItemsResponse
+ from .get_knowledgebase_id_scraped_urls_response import GetKnowledgebaseIdScrapedUrlsResponse
+ from .get_knowledgebase_id_scraped_urls_response_data_item import GetKnowledgebaseIdScrapedUrlsResponseDataItem
+ from .list_knowledge_base_response import ListKnowledgeBaseResponse
+ from .post_knowledgebase_compelete_file_upload_response import PostKnowledgebaseCompeleteFileUploadResponse
+ from .post_knowledgebase_get_presigned_url_response import PostKnowledgebaseGetPresignedUrlResponse
+ from .post_knowledgebase_get_presigned_url_response_data import PostKnowledgebaseGetPresignedUrlResponseData
+ from .post_knowledgebase_id_items_upload_media_response import PostKnowledgebaseIdItemsUploadMediaResponse
+ from .post_knowledgebase_id_response import PostKnowledgebaseIdResponse
+ from .scrape_urls_knowledge_base_response import ScrapeUrlsKnowledgeBaseResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateKnowledgeBaseResponse": ".create_knowledge_base_response",
+ "DeleteKnowledgeBaseResponse": ".delete_knowledge_base_response",
+ "DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse": ".delete_knowledgebase_knowledge_base_id_items_knowledge_base_item_id_response",
+ "DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse": ".delete_knowledgebase_knowledge_base_id_scraped_urls_knowledge_base_scraped_urls_id_response",
+ "ExtractSitemapUrlsKnowledgeBaseResponse": ".extract_sitemap_urls_knowledge_base_response",
+ "ExtractSitemapUrlsKnowledgeBaseResponseData": ".extract_sitemap_urls_knowledge_base_response_data",
+ "GetKnowledgeBaseResponse": ".get_knowledge_base_response",
+ "GetKnowledgebaseIdItemsResponse": ".get_knowledgebase_id_items_response",
+ "GetKnowledgebaseIdScrapedUrlsResponse": ".get_knowledgebase_id_scraped_urls_response",
+ "GetKnowledgebaseIdScrapedUrlsResponseDataItem": ".get_knowledgebase_id_scraped_urls_response_data_item",
+ "ListKnowledgeBaseResponse": ".list_knowledge_base_response",
+ "PostKnowledgebaseCompeleteFileUploadResponse": ".post_knowledgebase_compelete_file_upload_response",
+ "PostKnowledgebaseGetPresignedUrlResponse": ".post_knowledgebase_get_presigned_url_response",
+ "PostKnowledgebaseGetPresignedUrlResponseData": ".post_knowledgebase_get_presigned_url_response_data",
+ "PostKnowledgebaseIdItemsUploadMediaResponse": ".post_knowledgebase_id_items_upload_media_response",
+ "PostKnowledgebaseIdResponse": ".post_knowledgebase_id_response",
+ "ScrapeUrlsKnowledgeBaseResponse": ".scrape_urls_knowledge_base_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateKnowledgeBaseResponse",
+ "DeleteKnowledgeBaseResponse",
+ "DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse",
+ "DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse",
+ "ExtractSitemapUrlsKnowledgeBaseResponse",
+ "ExtractSitemapUrlsKnowledgeBaseResponseData",
+ "GetKnowledgeBaseResponse",
+ "GetKnowledgebaseIdItemsResponse",
+ "GetKnowledgebaseIdScrapedUrlsResponse",
+ "GetKnowledgebaseIdScrapedUrlsResponseDataItem",
+ "ListKnowledgeBaseResponse",
+ "PostKnowledgebaseCompeleteFileUploadResponse",
+ "PostKnowledgebaseGetPresignedUrlResponse",
+ "PostKnowledgebaseGetPresignedUrlResponseData",
+ "PostKnowledgebaseIdItemsUploadMediaResponse",
+ "PostKnowledgebaseIdResponse",
+ "ScrapeUrlsKnowledgeBaseResponse",
+]
diff --git a/src/smallestai/atoms/knowledge_base/types/get_knowledgebase_id_response.py b/src/smallestai/atoms/knowledge_base/types/get_knowledgebase_id_response.py
deleted file mode 100644
index 4a5491b0..00000000
--- a/src/smallestai/atoms/knowledge_base/types/get_knowledgebase_id_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.knowledge_base import KnowledgeBase
-
-
-class GetKnowledgebaseIdResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[KnowledgeBase] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/knowledge_base/types/get_knowledgebase_response.py b/src/smallestai/atoms/knowledge_base/types/get_knowledgebase_response.py
deleted file mode 100644
index 0133882b..00000000
--- a/src/smallestai/atoms/knowledge_base/types/get_knowledgebase_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from ...types.knowledge_base import KnowledgeBase
-
-
-class GetKnowledgebaseResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[typing.List[KnowledgeBase]] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_get_sitemap_urls_response.py b/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_get_sitemap_urls_response.py
deleted file mode 100644
index e4e6734f..00000000
--- a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_get_sitemap_urls_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_knowledgebase_get_sitemap_urls_response_data import PostKnowledgebaseGetSitemapUrlsResponseData
-
-
-class PostKnowledgebaseGetSitemapUrlsResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[PostKnowledgebaseGetSitemapUrlsResponseData] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_response.py b/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_response.py
deleted file mode 100644
index af04aceb..00000000
--- a/src/smallestai/atoms/knowledge_base/types/post_knowledgebase_response.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class PostKnowledgebaseResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[str] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/live_transcripts/__init__.py b/src/smallestai/atoms/live_transcripts/__init__.py
index 5cde0202..fbca21e9 100644
--- a/src/smallestai/atoms/live_transcripts/__init__.py
+++ b/src/smallestai/atoms/live_transcripts/__init__.py
@@ -2,3 +2,45 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ SubscribeToLiveEventsLiveTranscriptsResponse,
+ SubscribeToLiveEventsLiveTranscriptsResponseEventType,
+ SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "SubscribeToLiveEventsLiveTranscriptsResponse": ".types",
+ "SubscribeToLiveEventsLiveTranscriptsResponseEventType": ".types",
+ "SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "SubscribeToLiveEventsLiveTranscriptsResponse",
+ "SubscribeToLiveEventsLiveTranscriptsResponseEventType",
+ "SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem",
+]
diff --git a/src/smallestai/atoms/live_transcripts/types/__init__.py b/src/smallestai/atoms/live_transcripts/types/__init__.py
index 5cde0202..ef7a7184 100644
--- a/src/smallestai/atoms/live_transcripts/types/__init__.py
+++ b/src/smallestai/atoms/live_transcripts/types/__init__.py
@@ -2,3 +2,47 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .subscribe_to_live_events_live_transcripts_response import SubscribeToLiveEventsLiveTranscriptsResponse
+ from .subscribe_to_live_events_live_transcripts_response_event_type import (
+ SubscribeToLiveEventsLiveTranscriptsResponseEventType,
+ )
+ from .subscribe_to_live_events_live_transcripts_response_metrics_item import (
+ SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "SubscribeToLiveEventsLiveTranscriptsResponse": ".subscribe_to_live_events_live_transcripts_response",
+ "SubscribeToLiveEventsLiveTranscriptsResponseEventType": ".subscribe_to_live_events_live_transcripts_response_event_type",
+ "SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem": ".subscribe_to_live_events_live_transcripts_response_metrics_item",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "SubscribeToLiveEventsLiveTranscriptsResponse",
+ "SubscribeToLiveEventsLiveTranscriptsResponseEventType",
+ "SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem",
+]
diff --git a/src/smallestai/atoms/organization/__init__.py b/src/smallestai/atoms/organization/__init__.py
index 5cde0202..43efb7f5 100644
--- a/src/smallestai/atoms/organization/__init__.py
+++ b/src/smallestai/atoms/organization/__init__.py
@@ -2,3 +2,48 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ GetOrganizationResponse,
+ GetOrganizationResponseData,
+ GetOrganizationResponseDataMembersItem,
+ GetOrganizationResponseDataSubscription,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetOrganizationResponse": ".types",
+ "GetOrganizationResponseData": ".types",
+ "GetOrganizationResponseDataMembersItem": ".types",
+ "GetOrganizationResponseDataSubscription": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetOrganizationResponse",
+ "GetOrganizationResponseData",
+ "GetOrganizationResponseDataMembersItem",
+ "GetOrganizationResponseDataSubscription",
+]
diff --git a/src/smallestai/atoms/organization/client.py b/src/smallestai/atoms/organization/client.py
index 03de2d98..026cdc80 100644
--- a/src/smallestai/atoms/organization/client.py
+++ b/src/smallestai/atoms/organization/client.py
@@ -5,12 +5,7 @@
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ...core.request_options import RequestOptions
from .raw_client import AsyncRawOrganizationClient, RawOrganizationClient
-from .types.get_account_details_organization_response import GetAccountDetailsOrganizationResponse
from .types.get_organization_response import GetOrganizationResponse
-from .types.update_name_organization_response import UpdateNameOrganizationResponse
-
-# this is used as the default value for optional parameters
-OMIT = typing.cast(typing.Any, ...)
class OrganizationClient:
@@ -54,73 +49,6 @@ def get_organization_details(
_response = self._raw_client.get_organization_details(request_options=request_options)
return _response.data
- def get_account_details(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetAccountDetailsOrganizationResponse:
- """
- Returns the authenticated user's profile and the organizations they belong to.
-
- **Response envelope is non-standard** โ this endpoint returns the account object
- directly (no `{status, data}` wrapper).
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetAccountDetailsOrganizationResponse
- Authenticated user + organizations. Raw object, no envelope.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.organization.get_account_details()
- """
- _response = self._raw_client.get_account_details(request_options=request_options)
- return _response.data
-
- def update_name(
- self, *, name: str, request_options: typing.Optional[RequestOptions] = None
- ) -> UpdateNameOrganizationResponse:
- """
- Update the display name of the caller's current organization. Requires admin role.
-
- **Response envelope is non-standard** โ returns `{success: true, name}` instead
- of the usual `{status, data}` wrapper.
-
- Parameters
- ----------
- name : str
- New organization name (trimmed).
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- UpdateNameOrganizationResponse
- Organization renamed.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.organization.update_name(
- name="name",
- )
- """
- _response = self._raw_client.update_name(name=name, request_options=request_options)
- return _response.data
-
class AsyncOrganizationClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -170,86 +98,3 @@ async def main() -> None:
"""
_response = await self._raw_client.get_organization_details(request_options=request_options)
return _response.data
-
- async def get_account_details(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetAccountDetailsOrganizationResponse:
- """
- Returns the authenticated user's profile and the organizations they belong to.
-
- **Response envelope is non-standard** โ this endpoint returns the account object
- directly (no `{status, data}` wrapper).
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetAccountDetailsOrganizationResponse
- Authenticated user + organizations. Raw object, no envelope.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.organization.get_account_details()
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.get_account_details(request_options=request_options)
- return _response.data
-
- async def update_name(
- self, *, name: str, request_options: typing.Optional[RequestOptions] = None
- ) -> UpdateNameOrganizationResponse:
- """
- Update the display name of the caller's current organization. Requires admin role.
-
- **Response envelope is non-standard** โ returns `{success: true, name}` instead
- of the usual `{status, data}` wrapper.
-
- Parameters
- ----------
- name : str
- New organization name (trimmed).
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- UpdateNameOrganizationResponse
- Organization renamed.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.organization.update_name(
- name="name",
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.update_name(name=name, request_options=request_options)
- return _response.data
diff --git a/src/smallestai/atoms/organization/raw_client.py b/src/smallestai/atoms/organization/raw_client.py
index 20ce9878..90a30d7c 100644
--- a/src/smallestai/atoms/organization/raw_client.py
+++ b/src/smallestai/atoms/organization/raw_client.py
@@ -9,17 +9,11 @@
from ...core.parse_error import ParsingError
from ...core.request_options import RequestOptions
from ...core.unchecked_base_model import construct_type
-from ..errors.bad_request_error import BadRequestError
from ..errors.internal_server_error import InternalServerError
from ..errors.unauthorized_error import UnauthorizedError
-from .types.get_account_details_organization_response import GetAccountDetailsOrganizationResponse
from .types.get_organization_response import GetOrganizationResponse
-from .types.update_name_organization_response import UpdateNameOrganizationResponse
from pydantic import ValidationError
-# this is used as the default value for optional parameters
-OMIT = typing.cast(typing.Any, ...)
-
class RawOrganizationClient:
def __init__(self, *, client_wrapper: SyncClientWrapper):
@@ -86,159 +80,6 @@ def get_organization_details(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def get_account_details(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetAccountDetailsOrganizationResponse]:
- """
- Returns the authenticated user's profile and the organizations they belong to.
-
- **Response envelope is non-standard** โ this endpoint returns the account object
- directly (no `{status, data}` wrapper).
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[GetAccountDetailsOrganizationResponse]
- Authenticated user + organizations. Raw object, no envelope.
- """
- _response = self._client_wrapper.httpx_client.request(
- "account/get-account-details",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetAccountDetailsOrganizationResponse,
- construct_type(
- type_=GetAccountDetailsOrganizationResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- def update_name(
- self, *, name: str, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[UpdateNameOrganizationResponse]:
- """
- Update the display name of the caller's current organization. Requires admin role.
-
- **Response envelope is non-standard** โ returns `{success: true, name}` instead
- of the usual `{status, data}` wrapper.
-
- Parameters
- ----------
- name : str
- New organization name (trimmed).
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[UpdateNameOrganizationResponse]
- Organization renamed.
- """
- _response = self._client_wrapper.httpx_client.request(
- "account/update-org-name",
- base_url=self._client_wrapper.get_environment().atoms,
- method="PUT",
- json={
- "name": name,
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- UpdateNameOrganizationResponse,
- construct_type(
- type_=UpdateNameOrganizationResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
class AsyncRawOrganizationClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -304,156 +145,3 @@ async def get_organization_details(
status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def get_account_details(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetAccountDetailsOrganizationResponse]:
- """
- Returns the authenticated user's profile and the organizations they belong to.
-
- **Response envelope is non-standard** โ this endpoint returns the account object
- directly (no `{status, data}` wrapper).
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[GetAccountDetailsOrganizationResponse]
- Authenticated user + organizations. Raw object, no envelope.
- """
- _response = await self._client_wrapper.httpx_client.request(
- "account/get-account-details",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetAccountDetailsOrganizationResponse,
- construct_type(
- type_=GetAccountDetailsOrganizationResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def update_name(
- self, *, name: str, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[UpdateNameOrganizationResponse]:
- """
- Update the display name of the caller's current organization. Requires admin role.
-
- **Response envelope is non-standard** โ returns `{success: true, name}` instead
- of the usual `{status, data}` wrapper.
-
- Parameters
- ----------
- name : str
- New organization name (trimmed).
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[UpdateNameOrganizationResponse]
- Organization renamed.
- """
- _response = await self._client_wrapper.httpx_client.request(
- "account/update-org-name",
- base_url=self._client_wrapper.get_environment().atoms,
- method="PUT",
- json={
- "name": name,
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- UpdateNameOrganizationResponse,
- construct_type(
- type_=UpdateNameOrganizationResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/organization/types/__init__.py b/src/smallestai/atoms/organization/types/__init__.py
index 5cde0202..7d08cf6f 100644
--- a/src/smallestai/atoms/organization/types/__init__.py
+++ b/src/smallestai/atoms/organization/types/__init__.py
@@ -2,3 +2,46 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_organization_response import GetOrganizationResponse
+ from .get_organization_response_data import GetOrganizationResponseData
+ from .get_organization_response_data_members_item import GetOrganizationResponseDataMembersItem
+ from .get_organization_response_data_subscription import GetOrganizationResponseDataSubscription
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetOrganizationResponse": ".get_organization_response",
+ "GetOrganizationResponseData": ".get_organization_response_data",
+ "GetOrganizationResponseDataMembersItem": ".get_organization_response_data_members_item",
+ "GetOrganizationResponseDataSubscription": ".get_organization_response_data_subscription",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetOrganizationResponse",
+ "GetOrganizationResponseData",
+ "GetOrganizationResponseDataMembersItem",
+ "GetOrganizationResponseDataSubscription",
+]
diff --git a/src/smallestai/atoms/organization/types/get_account_details_organization_response.py b/src/smallestai/atoms/organization/types/get_account_details_organization_response.py
deleted file mode 100644
index 7afd8a48..00000000
--- a/src/smallestai/atoms/organization/types/get_account_details_organization_response.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_account_details_organization_response_organizations_item import (
- GetAccountDetailsOrganizationResponseOrganizationsItem,
-)
-
-
-class GetAccountDetailsOrganizationResponse(UncheckedBaseModel):
- email: typing.Optional[str] = None
- phone: typing.Optional[str] = None
- first_name: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="firstName"), pydantic.Field(alias="firstName")
- ] = None
- last_name: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="lastName"), pydantic.Field(alias="lastName")
- ] = None
- user_id: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="userId"), pydantic.Field(alias="userId")
- ] = None
- picture: typing.Optional[str] = None
- organizations: typing.Optional[typing.List[GetAccountDetailsOrganizationResponseOrganizationsItem]] = None
- has_onboarded: typing_extensions.Annotated[
- typing.Optional[bool], FieldMetadata(alias="hasOnboarded"), pydantic.Field(alias="hasOnboarded")
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/__init__.py b/src/smallestai/atoms/phone_numbers/__init__.py
index 5cde0202..1996bf80 100644
--- a/src/smallestai/atoms/phone_numbers/__init__.py
+++ b/src/smallestai/atoms/phone_numbers/__init__.py
@@ -2,3 +2,114 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ GetProductAllNumbersResponse,
+ GetProductAllNumbersResponseData,
+ GetProductManageSubscriptionResponse,
+ GetProductManageSubscriptionResponseData,
+ GetProductProrationAmountResponse,
+ GetProductProrationAmountResponseData,
+ GetProductUnpaidInvoicesResponse,
+ GetProductUnpaidInvoicesResponseData,
+ ImportSipPhoneNumbersResponse,
+ ImportSipPhoneNumbersResponseData,
+ ImportSipPhoneNumbersResponseDataAttributes,
+ ImportSipPhoneNumbersResponseDataProductType,
+ ListPhoneNumbersResponse,
+ ListPhoneNumbersResponseDataItem,
+ ListPhoneNumbersResponseDataItemAttributes,
+ ListPhoneNumbersResponseDataItemAttributesProvider,
+ ListPhoneNumbersResponseDataItemProductType,
+ ReleasePhoneNumbersResponse,
+ ReleasePhoneNumbersResponseData,
+ RentPhoneNumbersRequestProvider,
+ RentPhoneNumbersResponse,
+ RentPhoneNumbersResponseData,
+ SearchRentablePhoneNumbersRequestProvider,
+ SearchRentablePhoneNumbersResponse,
+ SearchRentablePhoneNumbersResponseDataItem,
+ SearchRentablePhoneNumbersResponseDataItemProvider,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetProductAllNumbersResponse": ".types",
+ "GetProductAllNumbersResponseData": ".types",
+ "GetProductManageSubscriptionResponse": ".types",
+ "GetProductManageSubscriptionResponseData": ".types",
+ "GetProductProrationAmountResponse": ".types",
+ "GetProductProrationAmountResponseData": ".types",
+ "GetProductUnpaidInvoicesResponse": ".types",
+ "GetProductUnpaidInvoicesResponseData": ".types",
+ "ImportSipPhoneNumbersResponse": ".types",
+ "ImportSipPhoneNumbersResponseData": ".types",
+ "ImportSipPhoneNumbersResponseDataAttributes": ".types",
+ "ImportSipPhoneNumbersResponseDataProductType": ".types",
+ "ListPhoneNumbersResponse": ".types",
+ "ListPhoneNumbersResponseDataItem": ".types",
+ "ListPhoneNumbersResponseDataItemAttributes": ".types",
+ "ListPhoneNumbersResponseDataItemAttributesProvider": ".types",
+ "ListPhoneNumbersResponseDataItemProductType": ".types",
+ "ReleasePhoneNumbersResponse": ".types",
+ "ReleasePhoneNumbersResponseData": ".types",
+ "RentPhoneNumbersRequestProvider": ".types",
+ "RentPhoneNumbersResponse": ".types",
+ "RentPhoneNumbersResponseData": ".types",
+ "SearchRentablePhoneNumbersRequestProvider": ".types",
+ "SearchRentablePhoneNumbersResponse": ".types",
+ "SearchRentablePhoneNumbersResponseDataItem": ".types",
+ "SearchRentablePhoneNumbersResponseDataItemProvider": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetProductAllNumbersResponse",
+ "GetProductAllNumbersResponseData",
+ "GetProductManageSubscriptionResponse",
+ "GetProductManageSubscriptionResponseData",
+ "GetProductProrationAmountResponse",
+ "GetProductProrationAmountResponseData",
+ "GetProductUnpaidInvoicesResponse",
+ "GetProductUnpaidInvoicesResponseData",
+ "ImportSipPhoneNumbersResponse",
+ "ImportSipPhoneNumbersResponseData",
+ "ImportSipPhoneNumbersResponseDataAttributes",
+ "ImportSipPhoneNumbersResponseDataProductType",
+ "ListPhoneNumbersResponse",
+ "ListPhoneNumbersResponseDataItem",
+ "ListPhoneNumbersResponseDataItemAttributes",
+ "ListPhoneNumbersResponseDataItemAttributesProvider",
+ "ListPhoneNumbersResponseDataItemProductType",
+ "ReleasePhoneNumbersResponse",
+ "ReleasePhoneNumbersResponseData",
+ "RentPhoneNumbersRequestProvider",
+ "RentPhoneNumbersResponse",
+ "RentPhoneNumbersResponseData",
+ "SearchRentablePhoneNumbersRequestProvider",
+ "SearchRentablePhoneNumbersResponse",
+ "SearchRentablePhoneNumbersResponseDataItem",
+ "SearchRentablePhoneNumbersResponseDataItemProvider",
+]
diff --git a/src/smallestai/atoms/phone_numbers/client.py b/src/smallestai/atoms/phone_numbers/client.py
index 133bd4b5..19d27183 100644
--- a/src/smallestai/atoms/phone_numbers/client.py
+++ b/src/smallestai/atoms/phone_numbers/client.py
@@ -336,7 +336,7 @@ def import_sip(
Your existing phone number. E.164 format is recommended but not enforced server-side โ any non-empty string is accepted.
sip_termination_url : str
- The SIP URI where calls should be routed to your infrastructure
+ Your SIP provider's termination host โ a hostname or IP address, optionally with a port (e.g. "sip.your-provider.com:5060"). Full SIP URIs ("sip:" / "sips:") are also accepted and automatically normalized to the bare host.
name : typing.Optional[str]
A friendly display name for this number
@@ -364,7 +364,7 @@ def import_sip(
)
client.atoms.phone_numbers.import_sip(
phone_number="+14155551234",
- sip_termination_url="sip:trunk.your-provider.com",
+ sip_termination_url="trunk.your-provider.com",
name="Main Support Line",
sip_username="",
sip_password="",
@@ -764,7 +764,7 @@ async def import_sip(
Your existing phone number. E.164 format is recommended but not enforced server-side โ any non-empty string is accepted.
sip_termination_url : str
- The SIP URI where calls should be routed to your infrastructure
+ Your SIP provider's termination host โ a hostname or IP address, optionally with a port (e.g. "sip.your-provider.com:5060"). Full SIP URIs ("sip:" / "sips:") are also accepted and automatically normalized to the bare host.
name : typing.Optional[str]
A friendly display name for this number
@@ -797,7 +797,7 @@ async def import_sip(
async def main() -> None:
await client.atoms.phone_numbers.import_sip(
phone_number="+14155551234",
- sip_termination_url="sip:trunk.your-provider.com",
+ sip_termination_url="trunk.your-provider.com",
name="Main Support Line",
sip_username="",
sip_password="",
diff --git a/src/smallestai/atoms/phone_numbers/raw_client.py b/src/smallestai/atoms/phone_numbers/raw_client.py
index 314a7aa5..32dd329e 100644
--- a/src/smallestai/atoms/phone_numbers/raw_client.py
+++ b/src/smallestai/atoms/phone_numbers/raw_client.py
@@ -667,7 +667,7 @@ def import_sip(
Your existing phone number. E.164 format is recommended but not enforced server-side โ any non-empty string is accepted.
sip_termination_url : str
- The SIP URI where calls should be routed to your infrastructure
+ Your SIP provider's termination host โ a hostname or IP address, optionally with a port (e.g. "sip.your-provider.com:5060"). Full SIP URIs ("sip:" / "sips:") are also accepted and automatically normalized to the bare host.
name : typing.Optional[str]
A friendly display name for this number
@@ -1394,7 +1394,7 @@ async def import_sip(
Your existing phone number. E.164 format is recommended but not enforced server-side โ any non-empty string is accepted.
sip_termination_url : str
- The SIP URI where calls should be routed to your infrastructure
+ Your SIP provider's termination host โ a hostname or IP address, optionally with a port (e.g. "sip.your-provider.com:5060"). Full SIP URIs ("sip:" / "sips:") are also accepted and automatically normalized to the bare host.
name : typing.Optional[str]
A friendly display name for this number
diff --git a/src/smallestai/atoms/phone_numbers/types/__init__.py b/src/smallestai/atoms/phone_numbers/types/__init__.py
index 5cde0202..81634bbe 100644
--- a/src/smallestai/atoms/phone_numbers/types/__init__.py
+++ b/src/smallestai/atoms/phone_numbers/types/__init__.py
@@ -2,3 +2,116 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_product_all_numbers_response import GetProductAllNumbersResponse
+ from .get_product_all_numbers_response_data import GetProductAllNumbersResponseData
+ from .get_product_manage_subscription_response import GetProductManageSubscriptionResponse
+ from .get_product_manage_subscription_response_data import GetProductManageSubscriptionResponseData
+ from .get_product_proration_amount_response import GetProductProrationAmountResponse
+ from .get_product_proration_amount_response_data import GetProductProrationAmountResponseData
+ from .get_product_unpaid_invoices_response import GetProductUnpaidInvoicesResponse
+ from .get_product_unpaid_invoices_response_data import GetProductUnpaidInvoicesResponseData
+ from .import_sip_phone_numbers_response import ImportSipPhoneNumbersResponse
+ from .import_sip_phone_numbers_response_data import ImportSipPhoneNumbersResponseData
+ from .import_sip_phone_numbers_response_data_attributes import ImportSipPhoneNumbersResponseDataAttributes
+ from .import_sip_phone_numbers_response_data_product_type import ImportSipPhoneNumbersResponseDataProductType
+ from .list_phone_numbers_response import ListPhoneNumbersResponse
+ from .list_phone_numbers_response_data_item import ListPhoneNumbersResponseDataItem
+ from .list_phone_numbers_response_data_item_attributes import ListPhoneNumbersResponseDataItemAttributes
+ from .list_phone_numbers_response_data_item_attributes_provider import (
+ ListPhoneNumbersResponseDataItemAttributesProvider,
+ )
+ from .list_phone_numbers_response_data_item_product_type import ListPhoneNumbersResponseDataItemProductType
+ from .release_phone_numbers_response import ReleasePhoneNumbersResponse
+ from .release_phone_numbers_response_data import ReleasePhoneNumbersResponseData
+ from .rent_phone_numbers_request_provider import RentPhoneNumbersRequestProvider
+ from .rent_phone_numbers_response import RentPhoneNumbersResponse
+ from .rent_phone_numbers_response_data import RentPhoneNumbersResponseData
+ from .search_rentable_phone_numbers_request_provider import SearchRentablePhoneNumbersRequestProvider
+ from .search_rentable_phone_numbers_response import SearchRentablePhoneNumbersResponse
+ from .search_rentable_phone_numbers_response_data_item import SearchRentablePhoneNumbersResponseDataItem
+ from .search_rentable_phone_numbers_response_data_item_provider import (
+ SearchRentablePhoneNumbersResponseDataItemProvider,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetProductAllNumbersResponse": ".get_product_all_numbers_response",
+ "GetProductAllNumbersResponseData": ".get_product_all_numbers_response_data",
+ "GetProductManageSubscriptionResponse": ".get_product_manage_subscription_response",
+ "GetProductManageSubscriptionResponseData": ".get_product_manage_subscription_response_data",
+ "GetProductProrationAmountResponse": ".get_product_proration_amount_response",
+ "GetProductProrationAmountResponseData": ".get_product_proration_amount_response_data",
+ "GetProductUnpaidInvoicesResponse": ".get_product_unpaid_invoices_response",
+ "GetProductUnpaidInvoicesResponseData": ".get_product_unpaid_invoices_response_data",
+ "ImportSipPhoneNumbersResponse": ".import_sip_phone_numbers_response",
+ "ImportSipPhoneNumbersResponseData": ".import_sip_phone_numbers_response_data",
+ "ImportSipPhoneNumbersResponseDataAttributes": ".import_sip_phone_numbers_response_data_attributes",
+ "ImportSipPhoneNumbersResponseDataProductType": ".import_sip_phone_numbers_response_data_product_type",
+ "ListPhoneNumbersResponse": ".list_phone_numbers_response",
+ "ListPhoneNumbersResponseDataItem": ".list_phone_numbers_response_data_item",
+ "ListPhoneNumbersResponseDataItemAttributes": ".list_phone_numbers_response_data_item_attributes",
+ "ListPhoneNumbersResponseDataItemAttributesProvider": ".list_phone_numbers_response_data_item_attributes_provider",
+ "ListPhoneNumbersResponseDataItemProductType": ".list_phone_numbers_response_data_item_product_type",
+ "ReleasePhoneNumbersResponse": ".release_phone_numbers_response",
+ "ReleasePhoneNumbersResponseData": ".release_phone_numbers_response_data",
+ "RentPhoneNumbersRequestProvider": ".rent_phone_numbers_request_provider",
+ "RentPhoneNumbersResponse": ".rent_phone_numbers_response",
+ "RentPhoneNumbersResponseData": ".rent_phone_numbers_response_data",
+ "SearchRentablePhoneNumbersRequestProvider": ".search_rentable_phone_numbers_request_provider",
+ "SearchRentablePhoneNumbersResponse": ".search_rentable_phone_numbers_response",
+ "SearchRentablePhoneNumbersResponseDataItem": ".search_rentable_phone_numbers_response_data_item",
+ "SearchRentablePhoneNumbersResponseDataItemProvider": ".search_rentable_phone_numbers_response_data_item_provider",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "GetProductAllNumbersResponse",
+ "GetProductAllNumbersResponseData",
+ "GetProductManageSubscriptionResponse",
+ "GetProductManageSubscriptionResponseData",
+ "GetProductProrationAmountResponse",
+ "GetProductProrationAmountResponseData",
+ "GetProductUnpaidInvoicesResponse",
+ "GetProductUnpaidInvoicesResponseData",
+ "ImportSipPhoneNumbersResponse",
+ "ImportSipPhoneNumbersResponseData",
+ "ImportSipPhoneNumbersResponseDataAttributes",
+ "ImportSipPhoneNumbersResponseDataProductType",
+ "ListPhoneNumbersResponse",
+ "ListPhoneNumbersResponseDataItem",
+ "ListPhoneNumbersResponseDataItemAttributes",
+ "ListPhoneNumbersResponseDataItemAttributesProvider",
+ "ListPhoneNumbersResponseDataItemProductType",
+ "ReleasePhoneNumbersResponse",
+ "ReleasePhoneNumbersResponseData",
+ "RentPhoneNumbersRequestProvider",
+ "RentPhoneNumbersResponse",
+ "RentPhoneNumbersResponseData",
+ "SearchRentablePhoneNumbersRequestProvider",
+ "SearchRentablePhoneNumbersResponse",
+ "SearchRentablePhoneNumbersResponseDataItem",
+ "SearchRentablePhoneNumbersResponseDataItemProvider",
+]
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_request_provider.py b/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_request_provider.py
deleted file mode 100644
index a9ebd5cf..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_request_provider.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetProductGetAvailableNumbersRequestProvider = typing.Union[typing.Literal["plivo", "twilio"], typing.Any]
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response.py b/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response.py
deleted file mode 100644
index 5eaada68..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_product_get_available_numbers_response_data_item import GetProductGetAvailableNumbersResponseDataItem
-
-
-class GetProductGetAvailableNumbersResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[typing.List[GetProductGetAvailableNumbersResponseDataItem]] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item.py b/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item.py
deleted file mode 100644
index 647a14f6..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_product_get_available_numbers_response_data_item_provider import (
- GetProductGetAvailableNumbersResponseDataItemProvider,
-)
-
-
-class GetProductGetAvailableNumbersResponseDataItem(UncheckedBaseModel):
- phone_number: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="phoneNumber"),
- pydantic.Field(
- alias="phoneNumber",
- description="E.164-style number without the leading `+`. Pass exactly this value to `POST /product/rent-number`.",
- ),
- ] = None
- country_code: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="countryCode"), pydantic.Field(alias="countryCode")
- ] = None
- provider: typing.Optional[GetProductGetAvailableNumbersResponseDataItemProvider] = None
- area_code: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="areaCode"),
- pydantic.Field(alias="areaCode", description="Region / state / area-code label returned by the provider."),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item_provider.py b/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item_provider.py
deleted file mode 100644
index 5afc0e3c..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_get_available_numbers_response_data_item_provider.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetProductGetAvailableNumbersResponseDataItemProvider = typing.Union[typing.Literal["plivo", "twilio"], typing.Any]
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response.py b/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response.py
deleted file mode 100644
index 33b94886..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_product_phone_numbers_response_data_item import GetProductPhoneNumbersResponseDataItem
-
-
-class GetProductPhoneNumbersResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[typing.List[GetProductPhoneNumbersResponseDataItem]] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item.py b/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item.py
deleted file mode 100644
index 29bd0986..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_product_phone_numbers_response_data_item_attributes import GetProductPhoneNumbersResponseDataItemAttributes
-from .get_product_phone_numbers_response_data_item_product_type import GetProductPhoneNumbersResponseDataItemProductType
-
-
-class GetProductPhoneNumbersResponseDataItem(UncheckedBaseModel):
- id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="_id"),
- pydantic.Field(alias="_id", description="The unique identifier for the phone number"),
- ] = None
- product_type: typing_extensions.Annotated[
- typing.Optional[GetProductPhoneNumbersResponseDataItemProductType],
- FieldMetadata(alias="productType"),
- pydantic.Field(
- alias="productType",
- description="The product type โ always `telephony` for numbers returned by this endpoint",
- ),
- ] = None
- is_active: typing_extensions.Annotated[
- typing.Optional[bool],
- FieldMetadata(alias="isActive"),
- pydantic.Field(alias="isActive", description="Whether the phone number is active"),
- ] = None
- agent_id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="agentId"),
- pydantic.Field(
- alias="agentId", description="ID of the agent currently assigned to this number, or null if unassigned"
- ),
- ] = None
- attributes: typing.Optional[GetProductPhoneNumbersResponseDataItemAttributes] = pydantic.Field(default=None)
- """
- Telephony provider attributes for the phone number
- """
-
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="The date and time when the phone number was created"),
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="updatedAt"),
- pydantic.Field(alias="updatedAt", description="The date and time when the phone number was last updated"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes.py b/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes.py
deleted file mode 100644
index c766f56b..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_product_phone_numbers_response_data_item_attributes_provider import (
- GetProductPhoneNumbersResponseDataItemAttributesProvider,
-)
-
-
-class GetProductPhoneNumbersResponseDataItemAttributes(UncheckedBaseModel):
- """
- Telephony provider attributes for the phone number
- """
-
- provider: typing.Optional[GetProductPhoneNumbersResponseDataItemAttributesProvider] = pydantic.Field(default=None)
- """
- The telephony provider
- """
-
- phone_number: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="phoneNumber"),
- pydantic.Field(alias="phoneNumber", description="The actual phone number in E.164 format"),
- ] = None
- country_code: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="countryCode"),
- pydantic.Field(alias="countryCode", description="ISO 3166-1 alpha-2 country code of the number"),
- ] = None
- area_code: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="areaCode"),
- pydantic.Field(alias="areaCode", description="Area code of the number (if applicable)"),
- ] = None
- compliance_application_id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="complianceApplicationId"),
- pydantic.Field(
- alias="complianceApplicationId",
- description="Compliance application ID associated with the number (if applicable)",
- ),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes_provider.py b/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes_provider.py
deleted file mode 100644
index f55aac75..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_attributes_provider.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetProductPhoneNumbersResponseDataItemAttributesProvider = typing.Union[typing.Literal["twilio", "plivo"], typing.Any]
diff --git a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_product_type.py b/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_product_type.py
deleted file mode 100644
index ba982000..00000000
--- a/src/smallestai/atoms/phone_numbers/types/get_product_phone_numbers_response_data_item_product_type.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-GetProductPhoneNumbersResponseDataItemProductType = typing.Union[typing.Literal["telephony"], typing.Any]
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data.py b/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data.py
deleted file mode 100644
index f5aaa4ac..00000000
--- a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_product_import_phone_number_response_data_attributes import (
- PostProductImportPhoneNumberResponseDataAttributes,
-)
-from .post_product_import_phone_number_response_data_product_type import (
- PostProductImportPhoneNumberResponseDataProductType,
-)
-
-
-class PostProductImportPhoneNumberResponseData(UncheckedBaseModel):
- id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="_id"),
- pydantic.Field(alias="_id", description="Unique identifier of the created product"),
- ] = None
- product_type: typing_extensions.Annotated[
- typing.Optional[PostProductImportPhoneNumberResponseDataProductType],
- FieldMetadata(alias="productType"),
- pydantic.Field(
- alias="productType", description="The type of product created โ always `custom` for imported SIP numbers"
- ),
- ] = None
- is_active: typing_extensions.Annotated[
- typing.Optional[bool],
- FieldMetadata(alias="isActive"),
- pydantic.Field(alias="isActive", description="Whether the number is active and ready to use"),
- ] = None
- attributes: typing.Optional[PostProductImportPhoneNumberResponseDataAttributes] = None
- agent_id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="agentId"),
- pydantic.Field(alias="agentId", description="ID of the agent assigned to this number (null if unassigned)"),
- ] = None
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="Timestamp when the product was created"),
- ] = None
- updated_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="updatedAt"),
- pydantic.Field(alias="updatedAt", description="Timestamp when the product was last updated"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_attributes.py b/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_attributes.py
deleted file mode 100644
index 4a2d14aa..00000000
--- a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_attributes.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class PostProductImportPhoneNumberResponseDataAttributes(UncheckedBaseModel):
- name: typing.Optional[str] = pydantic.Field(default=None)
- """
- Display name for the number
- """
-
- phone_number: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="phoneNumber"),
- pydantic.Field(alias="phoneNumber", description="The imported phone number"),
- ] = None
- outbound_sip_trunk_id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="outboundSipTrunkId"),
- pydantic.Field(alias="outboundSipTrunkId", description="Identifier for the outbound SIP trunk created"),
- ] = None
- inbound_sip_trunk_id: typing_extensions.Annotated[
- typing.Optional[str],
- FieldMetadata(alias="inboundSipTrunkId"),
- pydantic.Field(alias="inboundSipTrunkId", description="Identifier for the inbound SIP trunk created"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_product_type.py b/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_product_type.py
deleted file mode 100644
index 3de76102..00000000
--- a/src/smallestai/atoms/phone_numbers/types/post_product_import_phone_number_response_data_product_type.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-PostProductImportPhoneNumberResponseDataProductType = typing.Union[typing.Literal["custom"], typing.Any]
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_request_provider.py b/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_request_provider.py
deleted file mode 100644
index 28e68b68..00000000
--- a/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_request_provider.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-PostProductRentNumberRequestProvider = typing.Union[typing.Literal["plivo", "twilio"], typing.Any]
diff --git a/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_response.py b/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_response.py
deleted file mode 100644
index 9b038b04..00000000
--- a/src/smallestai/atoms/phone_numbers/types/post_product_rent_number_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .post_product_rent_number_response_data import PostProductRentNumberResponseData
-
-
-class PostProductRentNumberResponse(UncheckedBaseModel):
- status: typing.Optional[bool] = None
- data: typing.Optional[PostProductRentNumberResponseData] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/prompt_scoring/types/__init__.py b/src/smallestai/atoms/prompt_scoring/types/__init__.py
index 5cde0202..cb1b5bcb 100644
--- a/src/smallestai/atoms/prompt_scoring/types/__init__.py
+++ b/src/smallestai/atoms/prompt_scoring/types/__init__.py
@@ -2,3 +2,65 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .post_prompt_scoring_score_request import PostPromptScoringScoreRequest
+ from .post_prompt_scoring_score_request_draft_id import PostPromptScoringScoreRequestDraftId
+ from .post_prompt_scoring_score_request_version_id import PostPromptScoringScoreRequestVersionId
+ from .post_prompt_scoring_score_response import PostPromptScoringScoreResponse
+ from .post_prompt_scoring_score_response_data import PostPromptScoringScoreResponseData
+ from .post_prompt_scoring_score_response_data_band import PostPromptScoringScoreResponseDataBand
+ from .post_prompt_scoring_score_response_data_dimensions_item import (
+ PostPromptScoringScoreResponseDataDimensionsItem,
+ )
+ from .post_prompt_scoring_score_response_data_dimensions_item_level import (
+ PostPromptScoringScoreResponseDataDimensionsItemLevel,
+ )
+ from .post_prompt_scoring_score_response_data_overall_grade import PostPromptScoringScoreResponseDataOverallGrade
+_dynamic_imports: typing.Dict[str, str] = {
+ "PostPromptScoringScoreRequest": ".post_prompt_scoring_score_request",
+ "PostPromptScoringScoreRequestDraftId": ".post_prompt_scoring_score_request_draft_id",
+ "PostPromptScoringScoreRequestVersionId": ".post_prompt_scoring_score_request_version_id",
+ "PostPromptScoringScoreResponse": ".post_prompt_scoring_score_response",
+ "PostPromptScoringScoreResponseData": ".post_prompt_scoring_score_response_data",
+ "PostPromptScoringScoreResponseDataBand": ".post_prompt_scoring_score_response_data_band",
+ "PostPromptScoringScoreResponseDataDimensionsItem": ".post_prompt_scoring_score_response_data_dimensions_item",
+ "PostPromptScoringScoreResponseDataDimensionsItemLevel": ".post_prompt_scoring_score_response_data_dimensions_item_level",
+ "PostPromptScoringScoreResponseDataOverallGrade": ".post_prompt_scoring_score_response_data_overall_grade",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "PostPromptScoringScoreRequest",
+ "PostPromptScoringScoreRequestDraftId",
+ "PostPromptScoringScoreRequestVersionId",
+ "PostPromptScoringScoreResponse",
+ "PostPromptScoringScoreResponseData",
+ "PostPromptScoringScoreResponseDataBand",
+ "PostPromptScoringScoreResponseDataDimensionsItem",
+ "PostPromptScoringScoreResponseDataDimensionsItemLevel",
+ "PostPromptScoringScoreResponseDataOverallGrade",
+]
diff --git a/src/smallestai/atoms/realtime/__init__.py b/src/smallestai/atoms/realtime/__init__.py
new file mode 100644
index 00000000..b5242629
--- /dev/null
+++ b/src/smallestai/atoms/realtime/__init__.py
@@ -0,0 +1,49 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ RegisterCallRealtimeRequestMode,
+ RegisterCallRealtimeRequestVariablesValue,
+ RegisterCallRealtimeResponse,
+ RegisterCallRealtimeResponseData,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "RegisterCallRealtimeRequestMode": ".types",
+ "RegisterCallRealtimeRequestVariablesValue": ".types",
+ "RegisterCallRealtimeResponse": ".types",
+ "RegisterCallRealtimeResponseData": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "RegisterCallRealtimeRequestMode",
+ "RegisterCallRealtimeRequestVariablesValue",
+ "RegisterCallRealtimeResponse",
+ "RegisterCallRealtimeResponseData",
+]
diff --git a/src/smallestai/atoms/realtime/client.py b/src/smallestai/atoms/realtime/client.py
new file mode 100644
index 00000000..91b00724
--- /dev/null
+++ b/src/smallestai/atoms/realtime/client.py
@@ -0,0 +1,195 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.request_options import RequestOptions
+from .raw_client import AsyncRawRealtimeClient, RawRealtimeClient
+from .types.register_call_realtime_request_mode import RegisterCallRealtimeRequestMode
+from .types.register_call_realtime_request_variables_value import RegisterCallRealtimeRequestVariablesValue
+from .types.register_call_realtime_response import RegisterCallRealtimeResponse
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class RealtimeClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawRealtimeClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawRealtimeClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawRealtimeClient
+ """
+ return self._raw_client
+
+ def register_call(
+ self,
+ *,
+ agent_id: str,
+ mode: typing.Optional[RegisterCallRealtimeRequestMode] = OMIT,
+ variables: typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> RegisterCallRealtimeResponse:
+ """
+ Mint a **short-lived, single-use access token** for opening a realtime
+ [Agent WebSocket](/atoms/api-reference/realtime-agent/realtime-agent)
+ connection. This is the **recommended** way to start a session from a
+ browser or other client-side app: your API key stays server-side, and
+ the browser only ever sees the short-lived token. (Server-side or
+ trusted clients may instead connect to the WebSocket with a raw API key
+ directly.)
+
+ Flow:
+ 1. Call this endpoint with your API key and the `agent_id` (plus optional
+ `mode` and per-call `variables`). All session configuration is fixed
+ here โ it is baked into the returned token.
+ 2. Open a WebSocket to `wss://api.smallest.ai/atoms/v1/agent/connect?token=`.
+ No `agent_id`, `mode`, or `variables` query params are needed on the
+ WebSocket โ they come from the token.
+
+ The token is valid for `expires_in` seconds (30) and can be used for a
+ single connection. Request a fresh token for each connection.
+
+ Parameters
+ ----------
+ agent_id : str
+ The Atoms agent to connect to.
+
+ mode : typing.Optional[RegisterCallRealtimeRequestMode]
+ Session mode. `webcall` = full voice pipeline (audio in +
+ audio out). `chat` = text-only pipeline. Defaults to `webcall`.
+
+ variables : typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]]
+ Per-call prompt variables that override the agent's
+ `defaultVariables` for this session only. Values must be
+ `string`, `number`, or `boolean`. Reserved system-variable
+ keys (`call_id`, `conversation_type`, `agent_number`,
+ `user_number`, `current_date`, `current_time`, `current_day`,
+ `agent_gender`, `default_language`, `supported_languages`,
+ `timezone`) are populated by the server and stripped if
+ supplied.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ RegisterCallRealtimeResponse
+ Access token created.
+
+ Examples
+ --------
+ from smallestai import SmallestAI
+
+ client = SmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+ client.atoms.realtime.register_call(
+ agent_id="69da0b4c20c0e03cfa4ee258",
+ )
+ """
+ _response = self._raw_client.register_call(
+ agent_id=agent_id, mode=mode, variables=variables, request_options=request_options
+ )
+ return _response.data
+
+
+class AsyncRealtimeClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawRealtimeClient(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawRealtimeClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawRealtimeClient
+ """
+ return self._raw_client
+
+ async def register_call(
+ self,
+ *,
+ agent_id: str,
+ mode: typing.Optional[RegisterCallRealtimeRequestMode] = OMIT,
+ variables: typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> RegisterCallRealtimeResponse:
+ """
+ Mint a **short-lived, single-use access token** for opening a realtime
+ [Agent WebSocket](/atoms/api-reference/realtime-agent/realtime-agent)
+ connection. This is the **recommended** way to start a session from a
+ browser or other client-side app: your API key stays server-side, and
+ the browser only ever sees the short-lived token. (Server-side or
+ trusted clients may instead connect to the WebSocket with a raw API key
+ directly.)
+
+ Flow:
+ 1. Call this endpoint with your API key and the `agent_id` (plus optional
+ `mode` and per-call `variables`). All session configuration is fixed
+ here โ it is baked into the returned token.
+ 2. Open a WebSocket to `wss://api.smallest.ai/atoms/v1/agent/connect?token=`.
+ No `agent_id`, `mode`, or `variables` query params are needed on the
+ WebSocket โ they come from the token.
+
+ The token is valid for `expires_in` seconds (30) and can be used for a
+ single connection. Request a fresh token for each connection.
+
+ Parameters
+ ----------
+ agent_id : str
+ The Atoms agent to connect to.
+
+ mode : typing.Optional[RegisterCallRealtimeRequestMode]
+ Session mode. `webcall` = full voice pipeline (audio in +
+ audio out). `chat` = text-only pipeline. Defaults to `webcall`.
+
+ variables : typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]]
+ Per-call prompt variables that override the agent's
+ `defaultVariables` for this session only. Values must be
+ `string`, `number`, or `boolean`. Reserved system-variable
+ keys (`call_id`, `conversation_type`, `agent_number`,
+ `user_number`, `current_date`, `current_time`, `current_day`,
+ `agent_gender`, `default_language`, `supported_languages`,
+ `timezone`) are populated by the server and stripped if
+ supplied.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ RegisterCallRealtimeResponse
+ Access token created.
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.atoms.realtime.register_call(
+ agent_id="69da0b4c20c0e03cfa4ee258",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.register_call(
+ agent_id=agent_id, mode=mode, variables=variables, request_options=request_options
+ )
+ return _response.data
diff --git a/src/smallestai/atoms/realtime/raw_client.py b/src/smallestai/atoms/realtime/raw_client.py
new file mode 100644
index 00000000..61f9b9b7
--- /dev/null
+++ b/src/smallestai/atoms/realtime/raw_client.py
@@ -0,0 +1,307 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.http_response import AsyncHttpResponse, HttpResponse
+from ...core.parse_error import ParsingError
+from ...core.request_options import RequestOptions
+from ...core.serialization import convert_and_respect_annotation_metadata
+from ...core.unchecked_base_model import construct_type
+from ..errors.bad_request_error import BadRequestError
+from ..errors.internal_server_error import InternalServerError
+from ..errors.not_found_error import NotFoundError
+from ..errors.unauthorized_error import UnauthorizedError
+from .types.register_call_realtime_request_mode import RegisterCallRealtimeRequestMode
+from .types.register_call_realtime_request_variables_value import RegisterCallRealtimeRequestVariablesValue
+from .types.register_call_realtime_response import RegisterCallRealtimeResponse
+from pydantic import ValidationError
+
+# this is used as the default value for optional parameters
+OMIT = typing.cast(typing.Any, ...)
+
+
+class RawRealtimeClient:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def register_call(
+ self,
+ *,
+ agent_id: str,
+ mode: typing.Optional[RegisterCallRealtimeRequestMode] = OMIT,
+ variables: typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[RegisterCallRealtimeResponse]:
+ """
+ Mint a **short-lived, single-use access token** for opening a realtime
+ [Agent WebSocket](/atoms/api-reference/realtime-agent/realtime-agent)
+ connection. This is the **recommended** way to start a session from a
+ browser or other client-side app: your API key stays server-side, and
+ the browser only ever sees the short-lived token. (Server-side or
+ trusted clients may instead connect to the WebSocket with a raw API key
+ directly.)
+
+ Flow:
+ 1. Call this endpoint with your API key and the `agent_id` (plus optional
+ `mode` and per-call `variables`). All session configuration is fixed
+ here โ it is baked into the returned token.
+ 2. Open a WebSocket to `wss://api.smallest.ai/atoms/v1/agent/connect?token=`.
+ No `agent_id`, `mode`, or `variables` query params are needed on the
+ WebSocket โ they come from the token.
+
+ The token is valid for `expires_in` seconds (30) and can be used for a
+ single connection. Request a fresh token for each connection.
+
+ Parameters
+ ----------
+ agent_id : str
+ The Atoms agent to connect to.
+
+ mode : typing.Optional[RegisterCallRealtimeRequestMode]
+ Session mode. `webcall` = full voice pipeline (audio in +
+ audio out). `chat` = text-only pipeline. Defaults to `webcall`.
+
+ variables : typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]]
+ Per-call prompt variables that override the agent's
+ `defaultVariables` for this session only. Values must be
+ `string`, `number`, or `boolean`. Reserved system-variable
+ keys (`call_id`, `conversation_type`, `agent_number`,
+ `user_number`, `current_date`, `current_time`, `current_day`,
+ `agent_gender`, `default_language`, `supported_languages`,
+ `timezone`) are populated by the server and stripped if
+ supplied.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[RegisterCallRealtimeResponse]
+ Access token created.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "conversation/register-call",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "agent_id": agent_id,
+ "mode": mode,
+ "variables": convert_and_respect_annotation_metadata(
+ object_=variables,
+ annotation=typing.Dict[str, RegisterCallRealtimeRequestVariablesValue],
+ direction="write",
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ RegisterCallRealtimeResponse,
+ construct_type(
+ type_=RegisterCallRealtimeResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+
+class AsyncRawRealtimeClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def register_call(
+ self,
+ *,
+ agent_id: str,
+ mode: typing.Optional[RegisterCallRealtimeRequestMode] = OMIT,
+ variables: typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[RegisterCallRealtimeResponse]:
+ """
+ Mint a **short-lived, single-use access token** for opening a realtime
+ [Agent WebSocket](/atoms/api-reference/realtime-agent/realtime-agent)
+ connection. This is the **recommended** way to start a session from a
+ browser or other client-side app: your API key stays server-side, and
+ the browser only ever sees the short-lived token. (Server-side or
+ trusted clients may instead connect to the WebSocket with a raw API key
+ directly.)
+
+ Flow:
+ 1. Call this endpoint with your API key and the `agent_id` (plus optional
+ `mode` and per-call `variables`). All session configuration is fixed
+ here โ it is baked into the returned token.
+ 2. Open a WebSocket to `wss://api.smallest.ai/atoms/v1/agent/connect?token=`.
+ No `agent_id`, `mode`, or `variables` query params are needed on the
+ WebSocket โ they come from the token.
+
+ The token is valid for `expires_in` seconds (30) and can be used for a
+ single connection. Request a fresh token for each connection.
+
+ Parameters
+ ----------
+ agent_id : str
+ The Atoms agent to connect to.
+
+ mode : typing.Optional[RegisterCallRealtimeRequestMode]
+ Session mode. `webcall` = full voice pipeline (audio in +
+ audio out). `chat` = text-only pipeline. Defaults to `webcall`.
+
+ variables : typing.Optional[typing.Dict[str, RegisterCallRealtimeRequestVariablesValue]]
+ Per-call prompt variables that override the agent's
+ `defaultVariables` for this session only. Values must be
+ `string`, `number`, or `boolean`. Reserved system-variable
+ keys (`call_id`, `conversation_type`, `agent_number`,
+ `user_number`, `current_date`, `current_time`, `current_day`,
+ `agent_gender`, `default_language`, `supported_languages`,
+ `timezone`) are populated by the server and stripped if
+ supplied.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[RegisterCallRealtimeResponse]
+ Access token created.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "conversation/register-call",
+ base_url=self._client_wrapper.get_environment().atoms,
+ method="POST",
+ json={
+ "agent_id": agent_id,
+ "mode": mode,
+ "variables": convert_and_respect_annotation_metadata(
+ object_=variables,
+ annotation=typing.Dict[str, RegisterCallRealtimeRequestVariablesValue],
+ direction="write",
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ RegisterCallRealtimeResponse,
+ construct_type(
+ type_=RegisterCallRealtimeResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/realtime/types/__init__.py b/src/smallestai/atoms/realtime/types/__init__.py
new file mode 100644
index 00000000..a2ebb2e7
--- /dev/null
+++ b/src/smallestai/atoms/realtime/types/__init__.py
@@ -0,0 +1,47 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .register_call_realtime_request_mode import RegisterCallRealtimeRequestMode
+ from .register_call_realtime_request_variables_value import RegisterCallRealtimeRequestVariablesValue
+ from .register_call_realtime_response import RegisterCallRealtimeResponse
+ from .register_call_realtime_response_data import RegisterCallRealtimeResponseData
+_dynamic_imports: typing.Dict[str, str] = {
+ "RegisterCallRealtimeRequestMode": ".register_call_realtime_request_mode",
+ "RegisterCallRealtimeRequestVariablesValue": ".register_call_realtime_request_variables_value",
+ "RegisterCallRealtimeResponse": ".register_call_realtime_response",
+ "RegisterCallRealtimeResponseData": ".register_call_realtime_response_data",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "RegisterCallRealtimeRequestMode",
+ "RegisterCallRealtimeRequestVariablesValue",
+ "RegisterCallRealtimeResponse",
+ "RegisterCallRealtimeResponseData",
+]
diff --git a/src/smallestai/atoms/realtime/types/register_call_realtime_request_mode.py b/src/smallestai/atoms/realtime/types/register_call_realtime_request_mode.py
new file mode 100644
index 00000000..c75f847d
--- /dev/null
+++ b/src/smallestai/atoms/realtime/types/register_call_realtime_request_mode.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+RegisterCallRealtimeRequestMode = typing.Union[typing.Literal["webcall", "chat"], typing.Any]
diff --git a/src/smallestai/atoms/realtime/types/register_call_realtime_request_variables_value.py b/src/smallestai/atoms/realtime/types/register_call_realtime_request_variables_value.py
new file mode 100644
index 00000000..969f59cb
--- /dev/null
+++ b/src/smallestai/atoms/realtime/types/register_call_realtime_request_variables_value.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+RegisterCallRealtimeRequestVariablesValue = typing.Union[str, float, bool]
diff --git a/src/smallestai/atoms/realtime/types/register_call_realtime_response.py b/src/smallestai/atoms/realtime/types/register_call_realtime_response.py
new file mode 100644
index 00000000..e9f85b8a
--- /dev/null
+++ b/src/smallestai/atoms/realtime/types/register_call_realtime_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .register_call_realtime_response_data import RegisterCallRealtimeResponseData
+
+
+class RegisterCallRealtimeResponse(UncheckedBaseModel):
+ status: typing.Optional[bool] = None
+ data: typing.Optional[RegisterCallRealtimeResponseData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/realtime/types/register_call_realtime_response_data.py b/src/smallestai/atoms/realtime/types/register_call_realtime_response_data.py
new file mode 100644
index 00000000..2f1bfc0c
--- /dev/null
+++ b/src/smallestai/atoms/realtime/types/register_call_realtime_response_data.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class RegisterCallRealtimeResponseData(UncheckedBaseModel):
+ access_token: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Short-lived, single-use token (prefixed `wct_`). Pass it
+ as the `token` query param when opening the Agent
+ WebSocket.
+ """
+
+ expires_in: typing.Optional[int] = pydantic.Field(default=None)
+ """
+ Token lifetime in seconds.
+ """
+
+ sample_rate: typing.Optional[int] = pydantic.Field(default=None)
+ """
+ Negotiated audio sample rate (Hz) for the session. Echoed
+ back in the WebSocket `session.created` event.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/__init__.py b/src/smallestai/atoms/types/__init__.py
index 5cde0202..1d14373f 100644
--- a/src/smallestai/atoms/types/__init__.py
+++ b/src/smallestai/atoms/types/__init__.py
@@ -2,3 +2,543 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .agent_dto import AgentDto
+ from .agent_dto_background_sound import AgentDtoBackgroundSound
+ from .agent_dto_config_source import AgentDtoConfigSource
+ from .agent_dto_denoising_config import AgentDtoDenoisingConfig
+ from .agent_dto_language import AgentDtoLanguage
+ from .agent_dto_language_default import AgentDtoLanguageDefault
+ from .agent_dto_language_switching import AgentDtoLanguageSwitching
+ from .agent_dto_llm_idle_timeout_config import AgentDtoLlmIdleTimeoutConfig
+ from .agent_dto_pre_call_api import AgentDtoPreCallApi
+ from .agent_dto_pre_call_api_method import AgentDtoPreCallApiMethod
+ from .agent_dto_pre_call_api_response_variables_item import AgentDtoPreCallApiResponseVariablesItem
+ from .agent_dto_pronunciation_dicts_item import AgentDtoPronunciationDictsItem
+ from .agent_dto_redaction_config import AgentDtoRedactionConfig
+ from .agent_dto_resolved_config import AgentDtoResolvedConfig
+ from .agent_dto_resolved_config_default_language import AgentDtoResolvedConfigDefaultLanguage
+ from .agent_dto_session_timeout_config import AgentDtoSessionTimeoutConfig
+ from .agent_dto_slm_model import AgentDtoSlmModel
+ from .agent_dto_smart_turn_config import AgentDtoSmartTurnConfig
+ from .agent_dto_synthesizer import AgentDtoSynthesizer
+ from .agent_dto_synthesizer_voice_config import AgentDtoSynthesizerVoiceConfig
+ from .agent_dto_synthesizer_voice_config_gender import AgentDtoSynthesizerVoiceConfigGender
+ from .agent_dto_synthesizer_voice_config_model import AgentDtoSynthesizerVoiceConfigModel
+ from .agent_dto_timezone import AgentDtoTimezone
+ from .agent_dto_versioned_workflow import AgentDtoVersionedWorkflow
+ from .agent_dto_voice_detection_config import AgentDtoVoiceDetectionConfig
+ from .agent_dto_voice_mail_detection_config import AgentDtoVoiceMailDetectionConfig
+ from .agent_dto_widget_config import AgentDtoWidgetConfig
+ from .agent_dto_widget_config_mode import AgentDtoWidgetConfigMode
+ from .agent_dto_widget_config_position import AgentDtoWidgetConfigPosition
+ from .agent_dto_widget_config_size import AgentDtoWidgetConfigSize
+ from .agent_dto_widget_config_theme import AgentDtoWidgetConfigTheme
+ from .agent_version import AgentVersion
+ from .agent_version_blocks import AgentVersionBlocks
+ from .agent_version_diff import AgentVersionDiff
+ from .agent_version_diff_diffs_item import AgentVersionDiffDiffsItem
+ from .agent_version_diff_diffs_item_changes_item import AgentVersionDiffDiffsItemChangesItem
+ from .agent_version_diff_diffs_item_changes_item_new_value import AgentVersionDiffDiffsItemChangesItemNewValue
+ from .agent_version_diff_diffs_item_changes_item_old_value import AgentVersionDiffDiffsItemChangesItemOldValue
+ from .agent_version_metrics import AgentVersionMetrics
+ from .agent_version_metrics_comparison import AgentVersionMetricsComparison
+ from .agent_version_metrics_comparison_metrics import AgentVersionMetricsComparisonMetrics
+ from .agent_version_metrics_comparison_metrics_deltas import AgentVersionMetricsComparisonMetricsDeltas
+ from .agent_version_status import AgentVersionStatus
+ from .analytics_date_range import AnalyticsDateRange
+ from .analytics_trend_metric import AnalyticsTrendMetric
+ from .api_response import ApiResponse
+ from .bad_request_error_body import BadRequestErrorBody
+ from .bad_request_error_response import BadRequestErrorResponse
+ from .branch import Branch
+ from .branch_status import BranchStatus
+ from .branch_summary import BranchSummary
+ from .call_action import CallAction
+ from .call_action_action_type import CallActionActionType
+ from .call_action_category import CallActionCategory
+ from .call_action_condition import CallActionCondition
+ from .call_action_condition_operator import CallActionConditionOperator
+ from .call_action_config import CallActionConfig
+ from .compliance_application import ComplianceApplication
+ from .compliance_application_number_type import ComplianceApplicationNumberType
+ from .compliance_application_status import ComplianceApplicationStatus
+ from .compliance_application_user_type import ComplianceApplicationUserType
+ from .compliance_requirement import ComplianceRequirement
+ from .conflict_error_response import ConflictErrorResponse
+ from .diff_change import DiffChange
+ from .diff_result import DiffResult
+ from .diff_section import DiffSection
+ from .disposition_metric import DispositionMetric
+ from .disposition_metric_disposition_metric_type import DispositionMetricDispositionMetricType
+ from .draft_conflict_error import DraftConflictError
+ from .draft_conflict_error_data import DraftConflictErrorData
+ from .draft_conflict_error_data_conflict import DraftConflictErrorDataConflict
+ from .draft_detail import DraftDetail
+ from .draft_detail_edit_history_item import DraftDetailEditHistoryItem
+ from .draft_edit_history_entry import DraftEditHistoryEntry
+ from .forbidden_error_body import ForbiddenErrorBody
+ from .internal_server_error_body import InternalServerErrorBody
+ from .internal_server_error_response import InternalServerErrorResponse
+ from .knowledge_base import KnowledgeBase
+ from .knowledge_base_item import KnowledgeBaseItem
+ from .knowledge_base_item_item_type import KnowledgeBaseItemItemType
+ from .knowledge_base_item_processing_status import KnowledgeBaseItemProcessingStatus
+ from .knowledge_base_processing_status import KnowledgeBaseProcessingStatus
+ from .locked_error_response import LockedErrorResponse
+ from .locked_error_response_error_type import LockedErrorResponseErrorType
+ from .not_found_error_body import NotFoundErrorBody
+ from .post_call_analytics_config import PostCallAnalyticsConfig
+ from .post_call_analytics_config_success_metrics_item import PostCallAnalyticsConfigSuccessMetricsItem
+ from .post_call_analytics_config_success_metrics_item_success_metric_type import (
+ PostCallAnalyticsConfigSuccessMetricsItemSuccessMetricType,
+ )
+ from .product import Product
+ from .product_attributes import ProductAttributes
+ from .product_attributes_provider import ProductAttributesProvider
+ from .product_product_type import ProductProductType
+ from .prompt_score import PromptScore
+ from .prompt_score_dimensions_item import PromptScoreDimensionsItem
+ from .publish_result import PublishResult
+ from .publish_result_state import PublishResultState
+ from .required_document_type import RequiredDocumentType
+ from .required_document_type_required_fields_item import RequiredDocumentTypeRequiredFieldsItem
+ from .revision import Revision
+ from .revision_pending_publish import RevisionPendingPublish
+ from .revision_pending_publish_state import RevisionPendingPublishState
+ from .revision_security_check import RevisionSecurityCheck
+ from .revision_status import RevisionStatus
+ from .single_prompt_config import SinglePromptConfig
+ from .single_prompt_data import SinglePromptData
+ from .test_call_request import TestCallRequest
+ from .test_call_request_mode import TestCallRequestMode
+ from .test_call_result import TestCallResult
+ from .too_many_requests_error_body import TooManyRequestsErrorBody
+ from .too_many_requests_error_body_rate_limit import TooManyRequestsErrorBodyRateLimit
+ from .tool import Tool
+ from .tool_headers_array_item import ToolHeadersArrayItem
+ from .tool_llm_parameters_item import ToolLlmParametersItem
+ from .tool_llm_parameters_item_type import ToolLlmParametersItemType
+ from .tool_method import ToolMethod
+ from .tool_on_hold_music import ToolOnHoldMusic
+ from .tool_query_params_item import ToolQueryParamsItem
+ from .tool_response_variables_item import ToolResponseVariablesItem
+ from .tool_transfer_option import ToolTransferOption
+ from .tool_transfer_option_private_handoff_option import ToolTransferOptionPrivateHandoffOption
+ from .tool_transfer_option_private_handoff_option_type import ToolTransferOptionPrivateHandoffOptionType
+ from .tool_transfer_option_public_handoff_option import ToolTransferOptionPublicHandoffOption
+ from .tool_transfer_option_public_handoff_option_type import ToolTransferOptionPublicHandoffOptionType
+ from .tool_transfer_option_type import ToolTransferOptionType
+ from .tool_type import ToolType
+ from .tool_variables_extraction_schema_item import ToolVariablesExtractionSchemaItem
+ from .tool_variables_extraction_schema_item_type import ToolVariablesExtractionSchemaItemType
+ from .unauthorized_error_response import UnauthorizedErrorResponse
+ from .version_blocks import VersionBlocks
+ from .versioning_v2migration_required_response import VersioningV2MigrationRequiredResponse
+ from .versioning_v2migration_required_response_error_type import VersioningV2MigrationRequiredResponseErrorType
+ from .web_engage_integration_set import WebEngageIntegrationSet
+ from .webhook import Webhook
+ from .webhook_agent import WebhookAgent
+ from .webhook_event import WebhookEvent
+ from .webhook_event_analytics_completed import WebhookEventAnalyticsCompleted
+ from .webhook_event_analytics_completed_metadata import WebhookEventAnalyticsCompletedMetadata
+ from .webhook_event_analytics_completed_metadata_analytics import WebhookEventAnalyticsCompletedMetadataAnalytics
+ from .webhook_event_analytics_completed_metadata_event_type import WebhookEventAnalyticsCompletedMetadataEventType
+ from .webhook_event_analytics_metric import WebhookEventAnalyticsMetric
+ from .webhook_event_analytics_metric_disposition_metric_type import WebhookEventAnalyticsMetricDispositionMetricType
+ from .webhook_event_analytics_metric_value import WebhookEventAnalyticsMetricValue
+ from .webhook_event_call_data import WebhookEventCallData
+ from .webhook_event_call_data_call_direction import WebhookEventCallDataCallDirection
+ from .webhook_event_call_data_call_status import WebhookEventCallDataCallStatus
+ from .webhook_event_envelope import WebhookEventEnvelope
+ from .webhook_event_post_conversation import WebhookEventPostConversation
+ from .webhook_event_post_conversation_metadata import WebhookEventPostConversationMetadata
+ from .webhook_event_post_conversation_metadata_event_type import WebhookEventPostConversationMetadataEventType
+ from .webhook_event_pre_conversation import WebhookEventPreConversation
+ from .webhook_event_pre_conversation_metadata import WebhookEventPreConversationMetadata
+ from .webhook_event_pre_conversation_metadata_event_type import WebhookEventPreConversationMetadataEventType
+ from .webhook_event_transcript_turn import WebhookEventTranscriptTurn
+ from .webhook_event_transcript_turn_role import WebhookEventTranscriptTurnRole
+ from .webhook_status import WebhookStatus
+ from .webhook_subscription import WebhookSubscription
+ from .webhook_subscription_event_type import WebhookSubscriptionEventType
+ from .webhook_subscription_populated import WebhookSubscriptionPopulated
+ from .webhook_subscription_populated_event_type import WebhookSubscriptionPopulatedEventType
+ from .widget_config import WidgetConfig
+ from .widget_config_mode import WidgetConfigMode
+ from .widget_config_position import WidgetConfigPosition
+ from .widget_config_size import WidgetConfigSize
+ from .widget_config_theme import WidgetConfigTheme
+ from .workflow_graph_data import WorkflowGraphData
+ from .workflow_graph_data_edges_item import WorkflowGraphDataEdgesItem
+ from .workflow_graph_data_nodes_item import WorkflowGraphDataNodesItem
+ from .workflow_graph_data_nodes_item_position import WorkflowGraphDataNodesItemPosition
+ from .workflow_type import WorkflowType
+_dynamic_imports: typing.Dict[str, str] = {
+ "AgentDto": ".agent_dto",
+ "AgentDtoBackgroundSound": ".agent_dto_background_sound",
+ "AgentDtoConfigSource": ".agent_dto_config_source",
+ "AgentDtoDenoisingConfig": ".agent_dto_denoising_config",
+ "AgentDtoLanguage": ".agent_dto_language",
+ "AgentDtoLanguageDefault": ".agent_dto_language_default",
+ "AgentDtoLanguageSwitching": ".agent_dto_language_switching",
+ "AgentDtoLlmIdleTimeoutConfig": ".agent_dto_llm_idle_timeout_config",
+ "AgentDtoPreCallApi": ".agent_dto_pre_call_api",
+ "AgentDtoPreCallApiMethod": ".agent_dto_pre_call_api_method",
+ "AgentDtoPreCallApiResponseVariablesItem": ".agent_dto_pre_call_api_response_variables_item",
+ "AgentDtoPronunciationDictsItem": ".agent_dto_pronunciation_dicts_item",
+ "AgentDtoRedactionConfig": ".agent_dto_redaction_config",
+ "AgentDtoResolvedConfig": ".agent_dto_resolved_config",
+ "AgentDtoResolvedConfigDefaultLanguage": ".agent_dto_resolved_config_default_language",
+ "AgentDtoSessionTimeoutConfig": ".agent_dto_session_timeout_config",
+ "AgentDtoSlmModel": ".agent_dto_slm_model",
+ "AgentDtoSmartTurnConfig": ".agent_dto_smart_turn_config",
+ "AgentDtoSynthesizer": ".agent_dto_synthesizer",
+ "AgentDtoSynthesizerVoiceConfig": ".agent_dto_synthesizer_voice_config",
+ "AgentDtoSynthesizerVoiceConfigGender": ".agent_dto_synthesizer_voice_config_gender",
+ "AgentDtoSynthesizerVoiceConfigModel": ".agent_dto_synthesizer_voice_config_model",
+ "AgentDtoTimezone": ".agent_dto_timezone",
+ "AgentDtoVersionedWorkflow": ".agent_dto_versioned_workflow",
+ "AgentDtoVoiceDetectionConfig": ".agent_dto_voice_detection_config",
+ "AgentDtoVoiceMailDetectionConfig": ".agent_dto_voice_mail_detection_config",
+ "AgentDtoWidgetConfig": ".agent_dto_widget_config",
+ "AgentDtoWidgetConfigMode": ".agent_dto_widget_config_mode",
+ "AgentDtoWidgetConfigPosition": ".agent_dto_widget_config_position",
+ "AgentDtoWidgetConfigSize": ".agent_dto_widget_config_size",
+ "AgentDtoWidgetConfigTheme": ".agent_dto_widget_config_theme",
+ "AgentVersion": ".agent_version",
+ "AgentVersionBlocks": ".agent_version_blocks",
+ "AgentVersionDiff": ".agent_version_diff",
+ "AgentVersionDiffDiffsItem": ".agent_version_diff_diffs_item",
+ "AgentVersionDiffDiffsItemChangesItem": ".agent_version_diff_diffs_item_changes_item",
+ "AgentVersionDiffDiffsItemChangesItemNewValue": ".agent_version_diff_diffs_item_changes_item_new_value",
+ "AgentVersionDiffDiffsItemChangesItemOldValue": ".agent_version_diff_diffs_item_changes_item_old_value",
+ "AgentVersionMetrics": ".agent_version_metrics",
+ "AgentVersionMetricsComparison": ".agent_version_metrics_comparison",
+ "AgentVersionMetricsComparisonMetrics": ".agent_version_metrics_comparison_metrics",
+ "AgentVersionMetricsComparisonMetricsDeltas": ".agent_version_metrics_comparison_metrics_deltas",
+ "AgentVersionStatus": ".agent_version_status",
+ "AnalyticsDateRange": ".analytics_date_range",
+ "AnalyticsTrendMetric": ".analytics_trend_metric",
+ "ApiResponse": ".api_response",
+ "BadRequestErrorBody": ".bad_request_error_body",
+ "BadRequestErrorResponse": ".bad_request_error_response",
+ "Branch": ".branch",
+ "BranchStatus": ".branch_status",
+ "BranchSummary": ".branch_summary",
+ "CallAction": ".call_action",
+ "CallActionActionType": ".call_action_action_type",
+ "CallActionCategory": ".call_action_category",
+ "CallActionCondition": ".call_action_condition",
+ "CallActionConditionOperator": ".call_action_condition_operator",
+ "CallActionConfig": ".call_action_config",
+ "ComplianceApplication": ".compliance_application",
+ "ComplianceApplicationNumberType": ".compliance_application_number_type",
+ "ComplianceApplicationStatus": ".compliance_application_status",
+ "ComplianceApplicationUserType": ".compliance_application_user_type",
+ "ComplianceRequirement": ".compliance_requirement",
+ "ConflictErrorResponse": ".conflict_error_response",
+ "DiffChange": ".diff_change",
+ "DiffResult": ".diff_result",
+ "DiffSection": ".diff_section",
+ "DispositionMetric": ".disposition_metric",
+ "DispositionMetricDispositionMetricType": ".disposition_metric_disposition_metric_type",
+ "DraftConflictError": ".draft_conflict_error",
+ "DraftConflictErrorData": ".draft_conflict_error_data",
+ "DraftConflictErrorDataConflict": ".draft_conflict_error_data_conflict",
+ "DraftDetail": ".draft_detail",
+ "DraftDetailEditHistoryItem": ".draft_detail_edit_history_item",
+ "DraftEditHistoryEntry": ".draft_edit_history_entry",
+ "ForbiddenErrorBody": ".forbidden_error_body",
+ "InternalServerErrorBody": ".internal_server_error_body",
+ "InternalServerErrorResponse": ".internal_server_error_response",
+ "KnowledgeBase": ".knowledge_base",
+ "KnowledgeBaseItem": ".knowledge_base_item",
+ "KnowledgeBaseItemItemType": ".knowledge_base_item_item_type",
+ "KnowledgeBaseItemProcessingStatus": ".knowledge_base_item_processing_status",
+ "KnowledgeBaseProcessingStatus": ".knowledge_base_processing_status",
+ "LockedErrorResponse": ".locked_error_response",
+ "LockedErrorResponseErrorType": ".locked_error_response_error_type",
+ "NotFoundErrorBody": ".not_found_error_body",
+ "PostCallAnalyticsConfig": ".post_call_analytics_config",
+ "PostCallAnalyticsConfigSuccessMetricsItem": ".post_call_analytics_config_success_metrics_item",
+ "PostCallAnalyticsConfigSuccessMetricsItemSuccessMetricType": ".post_call_analytics_config_success_metrics_item_success_metric_type",
+ "Product": ".product",
+ "ProductAttributes": ".product_attributes",
+ "ProductAttributesProvider": ".product_attributes_provider",
+ "ProductProductType": ".product_product_type",
+ "PromptScore": ".prompt_score",
+ "PromptScoreDimensionsItem": ".prompt_score_dimensions_item",
+ "PublishResult": ".publish_result",
+ "PublishResultState": ".publish_result_state",
+ "RequiredDocumentType": ".required_document_type",
+ "RequiredDocumentTypeRequiredFieldsItem": ".required_document_type_required_fields_item",
+ "Revision": ".revision",
+ "RevisionPendingPublish": ".revision_pending_publish",
+ "RevisionPendingPublishState": ".revision_pending_publish_state",
+ "RevisionSecurityCheck": ".revision_security_check",
+ "RevisionStatus": ".revision_status",
+ "SinglePromptConfig": ".single_prompt_config",
+ "SinglePromptData": ".single_prompt_data",
+ "TestCallRequest": ".test_call_request",
+ "TestCallRequestMode": ".test_call_request_mode",
+ "TestCallResult": ".test_call_result",
+ "TooManyRequestsErrorBody": ".too_many_requests_error_body",
+ "TooManyRequestsErrorBodyRateLimit": ".too_many_requests_error_body_rate_limit",
+ "Tool": ".tool",
+ "ToolHeadersArrayItem": ".tool_headers_array_item",
+ "ToolLlmParametersItem": ".tool_llm_parameters_item",
+ "ToolLlmParametersItemType": ".tool_llm_parameters_item_type",
+ "ToolMethod": ".tool_method",
+ "ToolOnHoldMusic": ".tool_on_hold_music",
+ "ToolQueryParamsItem": ".tool_query_params_item",
+ "ToolResponseVariablesItem": ".tool_response_variables_item",
+ "ToolTransferOption": ".tool_transfer_option",
+ "ToolTransferOptionPrivateHandoffOption": ".tool_transfer_option_private_handoff_option",
+ "ToolTransferOptionPrivateHandoffOptionType": ".tool_transfer_option_private_handoff_option_type",
+ "ToolTransferOptionPublicHandoffOption": ".tool_transfer_option_public_handoff_option",
+ "ToolTransferOptionPublicHandoffOptionType": ".tool_transfer_option_public_handoff_option_type",
+ "ToolTransferOptionType": ".tool_transfer_option_type",
+ "ToolType": ".tool_type",
+ "ToolVariablesExtractionSchemaItem": ".tool_variables_extraction_schema_item",
+ "ToolVariablesExtractionSchemaItemType": ".tool_variables_extraction_schema_item_type",
+ "UnauthorizedErrorResponse": ".unauthorized_error_response",
+ "VersionBlocks": ".version_blocks",
+ "VersioningV2MigrationRequiredResponse": ".versioning_v2migration_required_response",
+ "VersioningV2MigrationRequiredResponseErrorType": ".versioning_v2migration_required_response_error_type",
+ "WebEngageIntegrationSet": ".web_engage_integration_set",
+ "Webhook": ".webhook",
+ "WebhookAgent": ".webhook_agent",
+ "WebhookEvent": ".webhook_event",
+ "WebhookEventAnalyticsCompleted": ".webhook_event_analytics_completed",
+ "WebhookEventAnalyticsCompletedMetadata": ".webhook_event_analytics_completed_metadata",
+ "WebhookEventAnalyticsCompletedMetadataAnalytics": ".webhook_event_analytics_completed_metadata_analytics",
+ "WebhookEventAnalyticsCompletedMetadataEventType": ".webhook_event_analytics_completed_metadata_event_type",
+ "WebhookEventAnalyticsMetric": ".webhook_event_analytics_metric",
+ "WebhookEventAnalyticsMetricDispositionMetricType": ".webhook_event_analytics_metric_disposition_metric_type",
+ "WebhookEventAnalyticsMetricValue": ".webhook_event_analytics_metric_value",
+ "WebhookEventCallData": ".webhook_event_call_data",
+ "WebhookEventCallDataCallDirection": ".webhook_event_call_data_call_direction",
+ "WebhookEventCallDataCallStatus": ".webhook_event_call_data_call_status",
+ "WebhookEventEnvelope": ".webhook_event_envelope",
+ "WebhookEventPostConversation": ".webhook_event_post_conversation",
+ "WebhookEventPostConversationMetadata": ".webhook_event_post_conversation_metadata",
+ "WebhookEventPostConversationMetadataEventType": ".webhook_event_post_conversation_metadata_event_type",
+ "WebhookEventPreConversation": ".webhook_event_pre_conversation",
+ "WebhookEventPreConversationMetadata": ".webhook_event_pre_conversation_metadata",
+ "WebhookEventPreConversationMetadataEventType": ".webhook_event_pre_conversation_metadata_event_type",
+ "WebhookEventTranscriptTurn": ".webhook_event_transcript_turn",
+ "WebhookEventTranscriptTurnRole": ".webhook_event_transcript_turn_role",
+ "WebhookStatus": ".webhook_status",
+ "WebhookSubscription": ".webhook_subscription",
+ "WebhookSubscriptionEventType": ".webhook_subscription_event_type",
+ "WebhookSubscriptionPopulated": ".webhook_subscription_populated",
+ "WebhookSubscriptionPopulatedEventType": ".webhook_subscription_populated_event_type",
+ "WidgetConfig": ".widget_config",
+ "WidgetConfigMode": ".widget_config_mode",
+ "WidgetConfigPosition": ".widget_config_position",
+ "WidgetConfigSize": ".widget_config_size",
+ "WidgetConfigTheme": ".widget_config_theme",
+ "WorkflowGraphData": ".workflow_graph_data",
+ "WorkflowGraphDataEdgesItem": ".workflow_graph_data_edges_item",
+ "WorkflowGraphDataNodesItem": ".workflow_graph_data_nodes_item",
+ "WorkflowGraphDataNodesItemPosition": ".workflow_graph_data_nodes_item_position",
+ "WorkflowType": ".workflow_type",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "AgentDto",
+ "AgentDtoBackgroundSound",
+ "AgentDtoConfigSource",
+ "AgentDtoDenoisingConfig",
+ "AgentDtoLanguage",
+ "AgentDtoLanguageDefault",
+ "AgentDtoLanguageSwitching",
+ "AgentDtoLlmIdleTimeoutConfig",
+ "AgentDtoPreCallApi",
+ "AgentDtoPreCallApiMethod",
+ "AgentDtoPreCallApiResponseVariablesItem",
+ "AgentDtoPronunciationDictsItem",
+ "AgentDtoRedactionConfig",
+ "AgentDtoResolvedConfig",
+ "AgentDtoResolvedConfigDefaultLanguage",
+ "AgentDtoSessionTimeoutConfig",
+ "AgentDtoSlmModel",
+ "AgentDtoSmartTurnConfig",
+ "AgentDtoSynthesizer",
+ "AgentDtoSynthesizerVoiceConfig",
+ "AgentDtoSynthesizerVoiceConfigGender",
+ "AgentDtoSynthesizerVoiceConfigModel",
+ "AgentDtoTimezone",
+ "AgentDtoVersionedWorkflow",
+ "AgentDtoVoiceDetectionConfig",
+ "AgentDtoVoiceMailDetectionConfig",
+ "AgentDtoWidgetConfig",
+ "AgentDtoWidgetConfigMode",
+ "AgentDtoWidgetConfigPosition",
+ "AgentDtoWidgetConfigSize",
+ "AgentDtoWidgetConfigTheme",
+ "AgentVersion",
+ "AgentVersionBlocks",
+ "AgentVersionDiff",
+ "AgentVersionDiffDiffsItem",
+ "AgentVersionDiffDiffsItemChangesItem",
+ "AgentVersionDiffDiffsItemChangesItemNewValue",
+ "AgentVersionDiffDiffsItemChangesItemOldValue",
+ "AgentVersionMetrics",
+ "AgentVersionMetricsComparison",
+ "AgentVersionMetricsComparisonMetrics",
+ "AgentVersionMetricsComparisonMetricsDeltas",
+ "AgentVersionStatus",
+ "AnalyticsDateRange",
+ "AnalyticsTrendMetric",
+ "ApiResponse",
+ "BadRequestErrorBody",
+ "BadRequestErrorResponse",
+ "Branch",
+ "BranchStatus",
+ "BranchSummary",
+ "CallAction",
+ "CallActionActionType",
+ "CallActionCategory",
+ "CallActionCondition",
+ "CallActionConditionOperator",
+ "CallActionConfig",
+ "ComplianceApplication",
+ "ComplianceApplicationNumberType",
+ "ComplianceApplicationStatus",
+ "ComplianceApplicationUserType",
+ "ComplianceRequirement",
+ "ConflictErrorResponse",
+ "DiffChange",
+ "DiffResult",
+ "DiffSection",
+ "DispositionMetric",
+ "DispositionMetricDispositionMetricType",
+ "DraftConflictError",
+ "DraftConflictErrorData",
+ "DraftConflictErrorDataConflict",
+ "DraftDetail",
+ "DraftDetailEditHistoryItem",
+ "DraftEditHistoryEntry",
+ "ForbiddenErrorBody",
+ "InternalServerErrorBody",
+ "InternalServerErrorResponse",
+ "KnowledgeBase",
+ "KnowledgeBaseItem",
+ "KnowledgeBaseItemItemType",
+ "KnowledgeBaseItemProcessingStatus",
+ "KnowledgeBaseProcessingStatus",
+ "LockedErrorResponse",
+ "LockedErrorResponseErrorType",
+ "NotFoundErrorBody",
+ "PostCallAnalyticsConfig",
+ "PostCallAnalyticsConfigSuccessMetricsItem",
+ "PostCallAnalyticsConfigSuccessMetricsItemSuccessMetricType",
+ "Product",
+ "ProductAttributes",
+ "ProductAttributesProvider",
+ "ProductProductType",
+ "PromptScore",
+ "PromptScoreDimensionsItem",
+ "PublishResult",
+ "PublishResultState",
+ "RequiredDocumentType",
+ "RequiredDocumentTypeRequiredFieldsItem",
+ "Revision",
+ "RevisionPendingPublish",
+ "RevisionPendingPublishState",
+ "RevisionSecurityCheck",
+ "RevisionStatus",
+ "SinglePromptConfig",
+ "SinglePromptData",
+ "TestCallRequest",
+ "TestCallRequestMode",
+ "TestCallResult",
+ "TooManyRequestsErrorBody",
+ "TooManyRequestsErrorBodyRateLimit",
+ "Tool",
+ "ToolHeadersArrayItem",
+ "ToolLlmParametersItem",
+ "ToolLlmParametersItemType",
+ "ToolMethod",
+ "ToolOnHoldMusic",
+ "ToolQueryParamsItem",
+ "ToolResponseVariablesItem",
+ "ToolTransferOption",
+ "ToolTransferOptionPrivateHandoffOption",
+ "ToolTransferOptionPrivateHandoffOptionType",
+ "ToolTransferOptionPublicHandoffOption",
+ "ToolTransferOptionPublicHandoffOptionType",
+ "ToolTransferOptionType",
+ "ToolType",
+ "ToolVariablesExtractionSchemaItem",
+ "ToolVariablesExtractionSchemaItemType",
+ "UnauthorizedErrorResponse",
+ "VersionBlocks",
+ "VersioningV2MigrationRequiredResponse",
+ "VersioningV2MigrationRequiredResponseErrorType",
+ "WebEngageIntegrationSet",
+ "Webhook",
+ "WebhookAgent",
+ "WebhookEvent",
+ "WebhookEventAnalyticsCompleted",
+ "WebhookEventAnalyticsCompletedMetadata",
+ "WebhookEventAnalyticsCompletedMetadataAnalytics",
+ "WebhookEventAnalyticsCompletedMetadataEventType",
+ "WebhookEventAnalyticsMetric",
+ "WebhookEventAnalyticsMetricDispositionMetricType",
+ "WebhookEventAnalyticsMetricValue",
+ "WebhookEventCallData",
+ "WebhookEventCallDataCallDirection",
+ "WebhookEventCallDataCallStatus",
+ "WebhookEventEnvelope",
+ "WebhookEventPostConversation",
+ "WebhookEventPostConversationMetadata",
+ "WebhookEventPostConversationMetadataEventType",
+ "WebhookEventPreConversation",
+ "WebhookEventPreConversationMetadata",
+ "WebhookEventPreConversationMetadataEventType",
+ "WebhookEventTranscriptTurn",
+ "WebhookEventTranscriptTurnRole",
+ "WebhookStatus",
+ "WebhookSubscription",
+ "WebhookSubscriptionEventType",
+ "WebhookSubscriptionPopulated",
+ "WebhookSubscriptionPopulatedEventType",
+ "WidgetConfig",
+ "WidgetConfigMode",
+ "WidgetConfigPosition",
+ "WidgetConfigSize",
+ "WidgetConfigTheme",
+ "WorkflowGraphData",
+ "WorkflowGraphDataEdgesItem",
+ "WorkflowGraphDataNodesItem",
+ "WorkflowGraphDataNodesItemPosition",
+ "WorkflowType",
+]
diff --git a/src/smallestai/atoms/types/agent_dto.py b/src/smallestai/atoms/types/agent_dto.py
index 888a031d..e5b2f15a 100644
--- a/src/smallestai/atoms/types/agent_dto.py
+++ b/src/smallestai/atoms/types/agent_dto.py
@@ -173,11 +173,6 @@ class AgentDto(UncheckedBaseModel):
FieldMetadata(alias="archivedAt"),
pydantic.Field(alias="archivedAt", description="The date and time when the agent was archived"),
] = None
- is_locked: typing_extensions.Annotated[
- typing.Optional[bool],
- FieldMetadata(alias="isLocked"),
- pydantic.Field(alias="isLocked", description="Whether the agent is locked and cannot be edited"),
- ] = None
active_version_id: typing_extensions.Annotated[
typing.Optional[str],
FieldMetadata(alias="activeVersionId"),
diff --git a/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config.py b/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config.py
index 1675dd17..4f19d0a3 100644
--- a/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config.py
+++ b/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config.py
@@ -21,11 +21,6 @@ class AgentDtoSynthesizerVoiceConfig(UncheckedBaseModel):
The TTS model of the synthesizer. Use `waves_lightning_v3_1`
for the recommended Waves voice path (default), or
`gpt-realtime` / `gpt-realtime-mini` for OpenAI realtime models.
-
- The other tokens (`waves`, `waves_lightning_large`,
- `waves_lightning_large_voice_clone`, `waves_lightning_v2`,
- `waves_lightning_v3`) are kept for backwards compatibility
- with existing agents and should not be used for new agents.
"""
voice_id: typing_extensions.Annotated[
diff --git a/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config_model.py b/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config_model.py
index 3f8cd3a5..612abe6f 100644
--- a/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config_model.py
+++ b/src/smallestai/atoms/types/agent_dto_synthesizer_voice_config_model.py
@@ -3,15 +3,5 @@
import typing
AgentDtoSynthesizerVoiceConfigModel = typing.Union[
- typing.Literal[
- "waves_lightning_v3_1",
- "gpt-realtime",
- "gpt-realtime-mini",
- "waves",
- "waves_lightning_large",
- "waves_lightning_large_voice_clone",
- "waves_lightning_v2",
- "waves_lightning_v3",
- ],
- typing.Any,
+ typing.Literal["waves_lightning_v3_1", "gpt-realtime", "gpt-realtime-mini"], typing.Any
]
diff --git a/src/smallestai/atoms/types/analytics_date_range.py b/src/smallestai/atoms/types/analytics_date_range.py
new file mode 100644
index 00000000..98bb8adb
--- /dev/null
+++ b/src/smallestai/atoms/types/analytics_date_range.py
@@ -0,0 +1,26 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class AnalyticsDateRange(UncheckedBaseModel):
+ from_: typing_extensions.Annotated[
+ typing.Optional[dt.date], FieldMetadata(alias="from"), pydantic.Field(alias="from")
+ ] = None
+ to: typing.Optional[dt.date] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/types/delete_voice_waves_response.py b/src/smallestai/atoms/types/analytics_trend_metric.py
similarity index 60%
rename from src/smallestai/waves/types/delete_voice_waves_response.py
rename to src/smallestai/atoms/types/analytics_trend_metric.py
index 6536b559..20c249a6 100644
--- a/src/smallestai/waves/types/delete_voice_waves_response.py
+++ b/src/smallestai/atoms/types/analytics_trend_metric.py
@@ -9,17 +9,16 @@
from ...core.unchecked_base_model import UncheckedBaseModel
-class DeleteVoiceWavesResponse(UncheckedBaseModel):
- success: typing.Optional[bool] = pydantic.Field(default=None)
+class AnalyticsTrendMetric(UncheckedBaseModel):
"""
- Status if the voice clone was deleted successfully.
+ A metric value with current and previous period values and percent change
"""
- voice_id: typing_extensions.Annotated[
- str,
- FieldMetadata(alias="voiceId"),
- pydantic.Field(alias="voiceId", description="Voice ID of the deleted voice clone."),
- ]
+ current: typing.Optional[float] = None
+ previous: typing.Optional[float] = None
+ percent_change: typing_extensions.Annotated[
+ typing.Optional[float], FieldMetadata(alias="percentChange"), pydantic.Field(alias="percentChange")
+ ] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/types/bad_request_error_body.py b/src/smallestai/atoms/types/bad_request_error_body.py
index 9aa6f1cd..ad9708f7 100644
--- a/src/smallestai/atoms/types/bad_request_error_body.py
+++ b/src/smallestai/atoms/types/bad_request_error_body.py
@@ -8,8 +8,8 @@
class BadRequestErrorBody(UncheckedBaseModel):
- success: typing.Optional[bool] = None
- message: typing.Optional[str] = None
+ status: typing.Optional[bool] = None
+ errors: typing.Optional[typing.List[str]] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/types/branch.py b/src/smallestai/atoms/types/branch.py
new file mode 100644
index 00000000..9e09ddbb
--- /dev/null
+++ b/src/smallestai/atoms/types/branch.py
@@ -0,0 +1,94 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .branch_status import BranchStatus
+
+
+class Branch(UncheckedBaseModel):
+ """
+ An `AgentBranch` document. An editable copy of an agent with its own draft slot and revision chain.
+ """
+
+ id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="_id"),
+ pydantic.Field(alias="_id", description="Branch ID (24-character ObjectId)."),
+ ] = None
+ agent: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ ID of the agent this branch belongs to.
+ """
+
+ name: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Branch name (unique per agent among active branches). `main` is reserved for the default branch.
+ """
+
+ is_default: typing_extensions.Annotated[
+ typing.Optional[bool],
+ FieldMetadata(alias="isDefault"),
+ pydantic.Field(
+ alias="isDefault",
+ description="True for the seeded `main` branch. The default branch cannot be renamed or archived.",
+ ),
+ ] = None
+ source_branch_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="sourceBranchId"),
+ pydantic.Field(
+ alias="sourceBranchId", description="Head of the source branch at fork time. `null` for `main`."
+ ),
+ ] = None
+ source_revision_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="sourceRevisionId"),
+ pydantic.Field(alias="sourceRevisionId", description="Revision the branch was forked from. `null` for `main`."),
+ ] = None
+ head_revision_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="headRevisionId"),
+ pydantic.Field(
+ alias="headRevisionId",
+ description="ID of the branch's latest committed revision. `null` until the first commit.",
+ ),
+ ] = None
+ open_draft_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="openDraftId"),
+ pydantic.Field(
+ alias="openDraftId", description="ID of the branch's single open draft. `null` when no draft is open."
+ ),
+ ] = None
+ status: typing.Optional[BranchStatus] = pydantic.Field(default=None)
+ """
+ Archived branches are hidden from list views and cannot receive draft edits or be made live.
+ """
+
+ created_by: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="createdBy"), pydantic.Field(alias="createdBy")
+ ] = None
+ updated_by: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="updatedBy"), pydantic.Field(alias="updatedBy")
+ ] = None
+ created_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="createdAt"), pydantic.Field(alias="createdAt")
+ ] = None
+ updated_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="updatedAt"), pydantic.Field(alias="updatedAt")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/branch_status.py b/src/smallestai/atoms/types/branch_status.py
new file mode 100644
index 00000000..363f7843
--- /dev/null
+++ b/src/smallestai/atoms/types/branch_status.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+BranchStatus = typing.Union[typing.Literal["active", "archived"], typing.Any]
diff --git a/src/smallestai/atoms/types/branch_summary.py b/src/smallestai/atoms/types/branch_summary.py
new file mode 100644
index 00000000..cd084f0c
--- /dev/null
+++ b/src/smallestai/atoms/types/branch_summary.py
@@ -0,0 +1,41 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .branch import Branch
+
+
+class BranchSummary(UncheckedBaseModel):
+ """
+ Branch document plus derived fields used by list and detail views.
+ """
+
+ branch: typing.Optional[Branch] = None
+ is_live: typing_extensions.Annotated[
+ typing.Optional[bool],
+ FieldMetadata(alias="isLive"),
+ pydantic.Field(alias="isLive", description="True when the agent's live config points at this branch."),
+ ] = None
+ has_open_draft: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="hasOpenDraft"), pydantic.Field(alias="hasOpenDraft")
+ ] = None
+ revisions_count: typing_extensions.Annotated[
+ typing.Optional[int], FieldMetadata(alias="revisionsCount"), pydantic.Field(alias="revisionsCount")
+ ] = None
+ head_revision_number: typing_extensions.Annotated[
+ typing.Optional[int], FieldMetadata(alias="headRevisionNumber"), pydantic.Field(alias="headRevisionNumber")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/call_action.py b/src/smallestai/atoms/types/call_action.py
new file mode 100644
index 00000000..5b8089f8
--- /dev/null
+++ b/src/smallestai/atoms/types/call_action.py
@@ -0,0 +1,69 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .call_action_action_type import CallActionActionType
+from .call_action_category import CallActionCategory
+from .call_action_config import CallActionConfig
+
+
+class CallAction(UncheckedBaseModel):
+ """
+ A call action that fires automatically at a specific point in the call lifecycle
+ """
+
+ id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id", description="MongoDB ObjectId")
+ ] = None
+ organization: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Organization ObjectId
+ """
+
+ agent_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="agentId"),
+ pydantic.Field(alias="agentId", description="Agent ObjectId this action belongs to"),
+ ] = None
+ category: typing.Optional[CallActionCategory] = pydantic.Field(default=None)
+ """
+ When the action fires
+ """
+
+ provider: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Integration provider (e.g. `hubspot`, `salesforce`)
+ """
+
+ action_type: typing.Optional[CallActionActionType] = pydantic.Field(default=None)
+ """
+ The operation performed on the provider object
+ """
+
+ object: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Provider object type (e.g. `contact`, `deal`)
+ """
+
+ config: typing.Optional[CallActionConfig] = None
+ created_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="createdAt"), pydantic.Field(alias="createdAt")
+ ] = None
+ updated_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="updatedAt"), pydantic.Field(alias="updatedAt")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/call_action_action_type.py b/src/smallestai/atoms/types/call_action_action_type.py
new file mode 100644
index 00000000..c08b01a0
--- /dev/null
+++ b/src/smallestai/atoms/types/call_action_action_type.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+CallActionActionType = typing.Union[typing.Literal["create", "update"], typing.Any]
diff --git a/src/smallestai/atoms/types/call_action_category.py b/src/smallestai/atoms/types/call_action_category.py
new file mode 100644
index 00000000..57ff2ed9
--- /dev/null
+++ b/src/smallestai/atoms/types/call_action_category.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+CallActionCategory = typing.Union[typing.Literal["trigger", "post-call"], typing.Any]
diff --git a/src/smallestai/atoms/types/call_action_condition.py b/src/smallestai/atoms/types/call_action_condition.py
new file mode 100644
index 00000000..7b87fc13
--- /dev/null
+++ b/src/smallestai/atoms/types/call_action_condition.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .call_action_condition_operator import CallActionConditionOperator
+
+
+class CallActionCondition(UncheckedBaseModel):
+ """
+ A filter condition evaluated against provider record fields
+ """
+
+ field: str = pydantic.Field()
+ """
+ Provider record field name
+ """
+
+ operator: CallActionConditionOperator = pydantic.Field()
+ """
+ Comparison operator
+ """
+
+ value: str = pydantic.Field()
+ """
+ Value to compare against
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/call_action_condition_operator.py b/src/smallestai/atoms/types/call_action_condition_operator.py
new file mode 100644
index 00000000..f6f3ea09
--- /dev/null
+++ b/src/smallestai/atoms/types/call_action_condition_operator.py
@@ -0,0 +1,10 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+CallActionConditionOperator = typing.Union[
+ typing.Literal[
+ "eq", "neq", "gt", "gte", "lt", "lte", "in", "nin", "contains", "notContains", "startsWith", "endsWith"
+ ],
+ typing.Any,
+]
diff --git a/src/smallestai/atoms/types/call_action_config.py b/src/smallestai/atoms/types/call_action_config.py
new file mode 100644
index 00000000..97f4c749
--- /dev/null
+++ b/src/smallestai/atoms/types/call_action_config.py
@@ -0,0 +1,35 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .call_action_condition import CallActionCondition
+
+
+class CallActionConfig(UncheckedBaseModel):
+ phone_number_field_name: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="phoneNumberFieldName"),
+ pydantic.Field(
+ alias="phoneNumberFieldName",
+ description="Provider field that contains the phone number (required for `trigger` actions)",
+ ),
+ ] = None
+ conditions: typing.Optional[typing.List[CallActionCondition]] = None
+ body: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Request body template or payload for the provider action
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/conflict_error_response.py b/src/smallestai/atoms/types/conflict_error_response.py
new file mode 100644
index 00000000..0dfcfa5b
--- /dev/null
+++ b/src/smallestai/atoms/types/conflict_error_response.py
@@ -0,0 +1,26 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class ConflictErrorResponse(UncheckedBaseModel):
+ """
+ Generic conflict body. `error_type` is a stable machine-readable discriminator (for example `branch_name_exists`, `source_has_no_commit`, `source_scanning`, `publish_in_progress`, `no_committed_revision`).
+ """
+
+ status: typing.Optional[bool] = None
+ error_type: typing.Optional[str] = None
+ errors: typing.Optional[typing.List[str]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/diff_change.py b/src/smallestai/atoms/types/diff_change.py
new file mode 100644
index 00000000..0a8f047f
--- /dev/null
+++ b/src/smallestai/atoms/types/diff_change.py
@@ -0,0 +1,32 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class DiffChange(UncheckedBaseModel):
+ path: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Dot-path to the changed leaf field.
+ """
+
+ old_value: typing_extensions.Annotated[
+ typing.Optional[typing.Any], FieldMetadata(alias="oldValue"), pydantic.Field(alias="oldValue")
+ ] = None
+ new_value: typing_extensions.Annotated[
+ typing.Optional[typing.Any], FieldMetadata(alias="newValue"), pydantic.Field(alias="newValue")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/types/add_voice_waves_response_data.py b/src/smallestai/atoms/types/diff_result.py
similarity index 60%
rename from src/smallestai/waves/types/add_voice_waves_response_data.py
rename to src/smallestai/atoms/types/diff_result.py
index bcddd6af..4e18675b 100644
--- a/src/smallestai/waves/types/add_voice_waves_response_data.py
+++ b/src/smallestai/atoms/types/diff_result.py
@@ -7,21 +7,20 @@
from ...core.pydantic_utilities import IS_PYDANTIC_V2
from ...core.serialization import FieldMetadata
from ...core.unchecked_base_model import UncheckedBaseModel
+from .diff_section import DiffSection
-class AddVoiceWavesResponseData(UncheckedBaseModel):
- voice_id: typing_extensions.Annotated[
- str, FieldMetadata(alias="voiceId"), pydantic.Field(alias="voiceId", description="Unique Voice ID.")
- ]
- model: str = pydantic.Field()
+class DiffResult(UncheckedBaseModel):
"""
- Model used to generate the voice.
+ Returned by `GET /agent/{id}/diff`.
"""
- status: str = pydantic.Field()
- """
- Status of the voice creation.
- """
+ unchanged_sections: typing_extensions.Annotated[
+ typing.Optional[typing.List[str]],
+ FieldMetadata(alias="unchangedSections"),
+ pydantic.Field(alias="unchangedSections"),
+ ] = None
+ diffs: typing.Optional[typing.List[DiffSection]] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/types/unauthorized_error_body.py b/src/smallestai/atoms/types/diff_section.py
similarity index 74%
rename from src/smallestai/atoms/types/unauthorized_error_body.py
rename to src/smallestai/atoms/types/diff_section.py
index 6b754f1e..c7867c6c 100644
--- a/src/smallestai/atoms/types/unauthorized_error_body.py
+++ b/src/smallestai/atoms/types/diff_section.py
@@ -5,11 +5,12 @@
import pydantic
from ...core.pydantic_utilities import IS_PYDANTIC_V2
from ...core.unchecked_base_model import UncheckedBaseModel
+from .diff_change import DiffChange
-class UnauthorizedErrorBody(UncheckedBaseModel):
- success: typing.Optional[bool] = None
- message: typing.Optional[str] = None
+class DiffSection(UncheckedBaseModel):
+ section: typing.Optional[str] = None
+ changes: typing.Optional[typing.List[DiffChange]] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/types/draft_conflict_error.py b/src/smallestai/atoms/types/draft_conflict_error.py
new file mode 100644
index 00000000..3169c773
--- /dev/null
+++ b/src/smallestai/atoms/types/draft_conflict_error.py
@@ -0,0 +1,27 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .draft_conflict_error_data import DraftConflictErrorData
+
+
+class DraftConflictError(UncheckedBaseModel):
+ """
+ Returned by `PUT /agent/{id}/branches/{branchId}/draft` when `expectedRevision` was sent and another edit changed one of the same leaf fields since. `errors` lists the conflicting leaf field paths. `data.conflict` carries the resolution context: the client's expected revision, the current draft revision, and per-field diffs.
+ """
+
+ status: bool
+ errors: typing.List[str]
+ data: typing.Optional[DraftConflictErrorData] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/types/get_cloned_voices_waves_response.py b/src/smallestai/atoms/types/draft_conflict_error_data.py
similarity index 61%
rename from src/smallestai/waves/types/get_cloned_voices_waves_response.py
rename to src/smallestai/atoms/types/draft_conflict_error_data.py
index 35820f4d..8c14b04d 100644
--- a/src/smallestai/waves/types/get_cloned_voices_waves_response.py
+++ b/src/smallestai/atoms/types/draft_conflict_error_data.py
@@ -5,14 +5,11 @@
import pydantic
from ...core.pydantic_utilities import IS_PYDANTIC_V2
from ...core.unchecked_base_model import UncheckedBaseModel
-from .get_cloned_voices_waves_response_voices_item import GetClonedVoicesWavesResponseVoicesItem
+from .draft_conflict_error_data_conflict import DraftConflictErrorDataConflict
-class GetClonedVoicesWavesResponse(UncheckedBaseModel):
- voices: typing.Optional[typing.List[GetClonedVoicesWavesResponseVoicesItem]] = pydantic.Field(default=None)
- """
- List of available voices.
- """
+class DraftConflictErrorData(UncheckedBaseModel):
+ conflict: typing.Optional[DraftConflictErrorDataConflict] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/types/draft_conflict_error_data_conflict.py b/src/smallestai/atoms/types/draft_conflict_error_data_conflict.py
new file mode 100644
index 00000000..e268e75d
--- /dev/null
+++ b/src/smallestai/atoms/types/draft_conflict_error_data_conflict.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class DraftConflictErrorDataConflict(UncheckedBaseModel):
+ expected_revision: typing_extensions.Annotated[
+ typing.Optional[int], FieldMetadata(alias="expectedRevision"), pydantic.Field(alias="expectedRevision")
+ ] = None
+ latest_revision: typing_extensions.Annotated[
+ typing.Optional[int], FieldMetadata(alias="latestRevision"), pydantic.Field(alias="latestRevision")
+ ] = None
+ diffs: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/draft_detail.py b/src/smallestai/atoms/types/draft_detail.py
new file mode 100644
index 00000000..3adc4210
--- /dev/null
+++ b/src/smallestai/atoms/types/draft_detail.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .draft_detail_edit_history_item import DraftDetailEditHistoryItem
+from .revision import Revision
+
+
+class DraftDetail(UncheckedBaseModel):
+ """
+ Returned by `GET /agent/{id}/branches/{branchId}/draft`.
+ """
+
+ latest: typing.Optional[Revision] = None
+ edit_count: typing_extensions.Annotated[
+ typing.Optional[int], FieldMetadata(alias="editCount"), pydantic.Field(alias="editCount")
+ ] = None
+ edit_history: typing_extensions.Annotated[
+ typing.Optional[typing.List[DraftDetailEditHistoryItem]],
+ FieldMetadata(alias="editHistory"),
+ pydantic.Field(alias="editHistory"),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/draft_detail_edit_history_item.py b/src/smallestai/atoms/types/draft_detail_edit_history_item.py
new file mode 100644
index 00000000..e33b5991
--- /dev/null
+++ b/src/smallestai/atoms/types/draft_detail_edit_history_item.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .revision import Revision
+
+
+class DraftDetailEditHistoryItem(UncheckedBaseModel):
+ revision: typing.Optional[Revision] = None
+ changed_sections: typing_extensions.Annotated[
+ typing.Optional[typing.List[str]],
+ FieldMetadata(alias="changedSections"),
+ pydantic.Field(alias="changedSections"),
+ ] = None
+ editor_name: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="editorName"),
+ pydantic.Field(
+ alias="editorName",
+ description="Display name of the editor who made this draft edit. `null` when the editor's display name is not resolvable.",
+ ),
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/internal_server_error_body.py b/src/smallestai/atoms/types/internal_server_error_body.py
index 082a678a..b640850f 100644
--- a/src/smallestai/atoms/types/internal_server_error_body.py
+++ b/src/smallestai/atoms/types/internal_server_error_body.py
@@ -8,8 +8,8 @@
class InternalServerErrorBody(UncheckedBaseModel):
- success: typing.Optional[bool] = None
- message: typing.Optional[str] = None
+ status: typing.Optional[bool] = None
+ errors: typing.Optional[typing.List[str]] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/types/locked_error_response.py b/src/smallestai/atoms/types/locked_error_response.py
new file mode 100644
index 00000000..063cde67
--- /dev/null
+++ b/src/smallestai/atoms/types/locked_error_response.py
@@ -0,0 +1,27 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .locked_error_response_error_type import LockedErrorResponseErrorType
+
+
+class LockedErrorResponse(UncheckedBaseModel):
+ """
+ Config-freeze body. Returned on write endpoints during a maintenance window (`AGENT_CONFIG_FROZEN`). Reads and test-calls pass.
+ """
+
+ status: typing.Optional[bool] = None
+ error_type: typing.Optional[LockedErrorResponseErrorType] = None
+ errors: typing.Optional[typing.List[str]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/locked_error_response_error_type.py b/src/smallestai/atoms/types/locked_error_response_error_type.py
new file mode 100644
index 00000000..8bac1aa6
--- /dev/null
+++ b/src/smallestai/atoms/types/locked_error_response_error_type.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+LockedErrorResponseErrorType = typing.Union[typing.Literal["config_freeze_active"], typing.Any]
diff --git a/src/smallestai/atoms/types/prompt_score.py b/src/smallestai/atoms/types/prompt_score.py
new file mode 100644
index 00000000..1f26acf7
--- /dev/null
+++ b/src/smallestai/atoms/types/prompt_score.py
@@ -0,0 +1,35 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .prompt_score_dimensions_item import PromptScoreDimensionsItem
+
+
+class PromptScore(UncheckedBaseModel):
+ """
+ Result of the prompt-scoring pass, if any.
+ """
+
+ overall_score: typing.Optional[float] = None
+ overall_grade: typing.Optional[str] = None
+ band: typing.Optional[str] = None
+ estimated_ttft_overhead_ms: typing.Optional[float] = None
+ scored_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="scoredAt"), pydantic.Field(alias="scoredAt")
+ ] = None
+ dimensions: typing.Optional[typing.List[PromptScoreDimensionsItem]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/types/add_voice_waves_response.py b/src/smallestai/atoms/types/prompt_score_dimensions_item.py
similarity index 61%
rename from src/smallestai/waves/types/add_voice_waves_response.py
rename to src/smallestai/atoms/types/prompt_score_dimensions_item.py
index 397d07b7..83c98125 100644
--- a/src/smallestai/waves/types/add_voice_waves_response.py
+++ b/src/smallestai/atoms/types/prompt_score_dimensions_item.py
@@ -5,16 +5,14 @@
import pydantic
from ...core.pydantic_utilities import IS_PYDANTIC_V2
from ...core.unchecked_base_model import UncheckedBaseModel
-from .add_voice_waves_response_data import AddVoiceWavesResponseData
-class AddVoiceWavesResponse(UncheckedBaseModel):
- message: typing.Optional[str] = pydantic.Field(default=None)
- """
- Message if the voice clone was created successfully.
- """
-
- data: typing.Optional[AddVoiceWavesResponseData] = None
+class PromptScoreDimensionsItem(UncheckedBaseModel):
+ tier: typing.Optional[int] = None
+ level: typing.Optional[str] = None
+ evidence_span: typing.Optional[str] = None
+ title: typing.Optional[str] = None
+ description: typing.Optional[str] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/atoms/types/publish_result.py b/src/smallestai/atoms/types/publish_result.py
new file mode 100644
index 00000000..0e7788ed
--- /dev/null
+++ b/src/smallestai/atoms/types/publish_result.py
@@ -0,0 +1,33 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .publish_result_state import PublishResultState
+from .revision import Revision
+
+
+class PublishResult(UncheckedBaseModel):
+ """
+ Result of publish or restore.
+
+ - `state: "committed"` (HTTP `200`) is returned when the commit is synchronous. This happens for restore (the source revision has already been scanned) and for publishes whose content does not need a fresh scan. The response includes the new revision object under `revision`.
+ - `state: "scanning"` (HTTP `202`) is returned when a security scan is deferred. In this case the response body carries only `state`. `revision` is absent, and clients must list the branch's revisions newest-first (`GET /agent/{id}/branches/{branchId}/revisions?limit=1`) to obtain the new revision ID. Then poll `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}` until `revision.status` flips from any transient value to `"published"`. On the revision doc the lifecycle field is `status` (not `state`), and the security-scan sub-lifecycle is on the nested `securityCheck.status`.
+ """
+
+ state: typing.Optional[PublishResultState] = None
+ revision: typing.Optional[Revision] = pydantic.Field(default=None)
+ """
+ The newly committed revision. Populated when `state` is `committed`, absent when `state` is `scanning`.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/publish_result_state.py b/src/smallestai/atoms/types/publish_result_state.py
new file mode 100644
index 00000000..3af60753
--- /dev/null
+++ b/src/smallestai/atoms/types/publish_result_state.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+PublishResultState = typing.Union[typing.Literal["scanning", "committed"], typing.Any]
diff --git a/src/smallestai/atoms/types/revision.py b/src/smallestai/atoms/types/revision.py
new file mode 100644
index 00000000..e20170e8
--- /dev/null
+++ b/src/smallestai/atoms/types/revision.py
@@ -0,0 +1,145 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .prompt_score import PromptScore
+from .revision_pending_publish import RevisionPendingPublish
+from .revision_security_check import RevisionSecurityCheck
+from .revision_status import RevisionStatus
+from .version_blocks import VersionBlocks
+
+
+class Revision(UncheckedBaseModel):
+ """
+ An `AgentVersion` document. Represents either a committed revision (`status: published`, with `branch` + `revisionNumber`) or an in-progress draft revision (`status: draft`, with `draftId` + `draftRevision`). Fields that do not apply to a given row are `null`.
+ """
+
+ id: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="_id"), pydantic.Field(alias="_id")] = (
+ None
+ )
+ agent: typing.Optional[str] = None
+ status: typing.Optional[RevisionStatus] = None
+ branch: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Owning branch (v2). `null` on legacy rows until backfilled.
+ """
+
+ revision_number: typing_extensions.Annotated[
+ typing.Optional[int],
+ FieldMetadata(alias="revisionNumber"),
+ pydantic.Field(
+ alias="revisionNumber", description="Monotonic per-branch commit number. Only set on committed rows."
+ ),
+ ] = None
+ version_number: typing_extensions.Annotated[
+ typing.Optional[int],
+ FieldMetadata(alias="versionNumber"),
+ pydantic.Field(
+ alias="versionNumber",
+ description="Legacy v1 published-version number. `null` for branch revisions and drafts.",
+ ),
+ ] = None
+ label: typing.Optional[str] = None
+ description: typing.Optional[str] = None
+ is_pinned: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="isPinned"), pydantic.Field(alias="isPinned")
+ ] = None
+ published_by: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="publishedBy"), pydantic.Field(alias="publishedBy")
+ ] = None
+ published_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="publishedAt"), pydantic.Field(alias="publishedAt")
+ ] = None
+ published_by_name: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="publishedByName"),
+ pydantic.Field(alias="publishedByName", description="Display name of the publisher. `null` when unresolvable."),
+ ] = None
+ restored_from_label: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="restoredFromLabel"),
+ pydantic.Field(
+ alias="restoredFromLabel",
+ description="On a commit produced by restore, the label of the revision it copied.",
+ ),
+ ] = None
+ draft_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="draftId"), pydantic.Field(alias="draftId")
+ ] = None
+ draft_name: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="draftName"), pydantic.Field(alias="draftName")
+ ] = None
+ draft_revision: typing_extensions.Annotated[
+ typing.Optional[int], FieldMetadata(alias="draftRevision"), pydantic.Field(alias="draftRevision")
+ ] = None
+ source_version_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="sourceVersionId"), pydantic.Field(alias="sourceVersionId")
+ ] = None
+ blocks: typing.Optional[VersionBlocks] = None
+ workflow_type: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="workflowType"),
+ pydantic.Field(alias="workflowType", description="The `WorkflowType` enum for this revision."),
+ ] = None
+ parent_version: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="parentVersion"), pydantic.Field(alias="parentVersion")
+ ] = None
+ is_active: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="isActive"), pydantic.Field(alias="isActive")
+ ] = None
+ activated_by: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="activatedBy"), pydantic.Field(alias="activatedBy")
+ ] = None
+ activated_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="activatedAt"), pydantic.Field(alias="activatedAt")
+ ] = None
+ security_check: typing_extensions.Annotated[
+ typing.Optional[RevisionSecurityCheck],
+ FieldMetadata(alias="securityCheck"),
+ pydantic.Field(alias="securityCheck", description="Populated after publish. `null` on pre-feature versions."),
+ ] = None
+ pending_publish: typing_extensions.Annotated[
+ typing.Optional[RevisionPendingPublish],
+ FieldMetadata(alias="pendingPublish"),
+ pydantic.Field(alias="pendingPublish", description="Set while a publish is armed against this draft revision."),
+ ] = None
+ restored_from_revision_id: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="restoredFromRevisionId"),
+ pydantic.Field(
+ alias="restoredFromRevisionId", description="On a commit produced by restore, the revision it copied."
+ ),
+ ] = None
+ source_draft_id: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="sourceDraftId"), pydantic.Field(alias="sourceDraftId")
+ ] = None
+ prompt_score: typing_extensions.Annotated[
+ typing.Optional[PromptScore], FieldMetadata(alias="promptScore"), pydantic.Field(alias="promptScore")
+ ] = None
+ prompt_score_stale: typing_extensions.Annotated[
+ typing.Optional[bool], FieldMetadata(alias="promptScoreStale"), pydantic.Field(alias="promptScoreStale")
+ ] = None
+ created_by: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="createdBy"), pydantic.Field(alias="createdBy")
+ ] = None
+ created_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="createdAt"), pydantic.Field(alias="createdAt")
+ ] = None
+ updated_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="updatedAt"), pydantic.Field(alias="updatedAt")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/revision_pending_publish.py b/src/smallestai/atoms/types/revision_pending_publish.py
new file mode 100644
index 00000000..1d16b440
--- /dev/null
+++ b/src/smallestai/atoms/types/revision_pending_publish.py
@@ -0,0 +1,34 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .revision_pending_publish_state import RevisionPendingPublishState
+
+
+class RevisionPendingPublish(UncheckedBaseModel):
+ """
+ Set while a publish is armed against this draft revision.
+ """
+
+ state: typing.Optional[RevisionPendingPublishState] = None
+ started_by: typing_extensions.Annotated[
+ typing.Optional[str], FieldMetadata(alias="startedBy"), pydantic.Field(alias="startedBy")
+ ] = None
+ started_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="startedAt"), pydantic.Field(alias="startedAt")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/revision_pending_publish_state.py b/src/smallestai/atoms/types/revision_pending_publish_state.py
new file mode 100644
index 00000000..f377476b
--- /dev/null
+++ b/src/smallestai/atoms/types/revision_pending_publish_state.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+RevisionPendingPublishState = typing.Union[typing.Literal["active", "cancelled"], typing.Any]
diff --git a/src/smallestai/atoms/types/revision_security_check.py b/src/smallestai/atoms/types/revision_security_check.py
new file mode 100644
index 00000000..840dbab7
--- /dev/null
+++ b/src/smallestai/atoms/types/revision_security_check.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class RevisionSecurityCheck(UncheckedBaseModel):
+ """
+ Populated after publish. `null` on pre-feature versions.
+ """
+
+ status: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ The `SecurityCheckStatus` enum.
+ """
+
+ reason: typing.Optional[str] = None
+ triggered_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="triggeredAt"), pydantic.Field(alias="triggeredAt")
+ ] = None
+ completed_at: typing_extensions.Annotated[
+ typing.Optional[dt.datetime], FieldMetadata(alias="completedAt"), pydantic.Field(alias="completedAt")
+ ] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/revision_status.py b/src/smallestai/atoms/types/revision_status.py
new file mode 100644
index 00000000..2d222208
--- /dev/null
+++ b/src/smallestai/atoms/types/revision_status.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+RevisionStatus = typing.Union[typing.Literal["published", "draft", "archived"], typing.Any]
diff --git a/src/smallestai/atoms/types/test_call_result.py b/src/smallestai/atoms/types/test_call_result.py
new file mode 100644
index 00000000..a830813a
--- /dev/null
+++ b/src/smallestai/atoms/types/test_call_result.py
@@ -0,0 +1,43 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class TestCallResult(UncheckedBaseModel):
+ """
+ Returned by `POST /agent/{id}/branches/{branchId}/test-call`.
+ """
+
+ conversation_id: typing_extensions.Annotated[
+ str, FieldMetadata(alias="conversationId"), pydantic.Field(alias="conversationId")
+ ]
+ call_id: typing_extensions.Annotated[str, FieldMetadata(alias="callId"), pydantic.Field(alias="callId")]
+ token: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ LiveKit access token. Returned for `webcall` and `chat` modes only.
+ """
+
+ room_name: typing_extensions.Annotated[
+ typing.Optional[str],
+ FieldMetadata(alias="roomName"),
+ pydantic.Field(alias="roomName", description="Returned for `webcall` and `chat` modes only."),
+ ] = None
+ host: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Returned for `webcall` and `chat` modes only.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/version_blocks.py b/src/smallestai/atoms/types/version_blocks.py
new file mode 100644
index 00000000..4b748f02
--- /dev/null
+++ b/src/smallestai/atoms/types/version_blocks.py
@@ -0,0 +1,40 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class VersionBlocks(UncheckedBaseModel):
+ """
+ ObjectId references to the `AgentConfigBlock` docs that make up a revision, one per config section.
+ """
+
+ workflow_prompt: typing.Optional[str] = None
+ workflow_tools: typing.Optional[str] = None
+ workflow_graph: typing.Optional[str] = None
+ llm: typing.Optional[str] = None
+ voice: typing.Optional[str] = None
+ language: typing.Optional[str] = None
+ call_handling: typing.Optional[str] = None
+ detection: typing.Optional[str] = None
+ analytics: typing.Optional[str] = None
+ timeouts: typing.Optional[str] = None
+ audio: typing.Optional[str] = None
+ privacy: typing.Optional[str] = None
+ widget: typing.Optional[str] = None
+ playbooks: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Only present on revisions created after multi-agent playbooks shipped.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/versioning_v2migration_required_response.py b/src/smallestai/atoms/types/versioning_v2migration_required_response.py
new file mode 100644
index 00000000..4562f187
--- /dev/null
+++ b/src/smallestai/atoms/types/versioning_v2migration_required_response.py
@@ -0,0 +1,27 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .versioning_v2migration_required_response_error_type import VersioningV2MigrationRequiredResponseErrorType
+
+
+class VersioningV2MigrationRequiredResponse(UncheckedBaseModel):
+ """
+ Returned by deprecated v1 versioning writes and list reads when `ENABLE_BRANCH_MODEL` is on. Response header carries `Deprecation: true`. The `error_type` value is a stable discriminator.
+ """
+
+ status: typing.Optional[bool] = None
+ error_type: typing.Optional[VersioningV2MigrationRequiredResponseErrorType] = None
+ errors: typing.Optional[typing.List[str]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/types/versioning_v2migration_required_response_error_type.py b/src/smallestai/atoms/types/versioning_v2migration_required_response_error_type.py
new file mode 100644
index 00000000..3cbc9995
--- /dev/null
+++ b/src/smallestai/atoms/types/versioning_v2migration_required_response_error_type.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+VersioningV2MigrationRequiredResponseErrorType = typing.Union[
+ typing.Literal["versioning_v2_migration_required"], typing.Any
+]
diff --git a/src/smallestai/atoms/types/web_engage_integration_set.py b/src/smallestai/atoms/types/web_engage_integration_set.py
new file mode 100644
index 00000000..e0759658
--- /dev/null
+++ b/src/smallestai/atoms/types/web_engage_integration_set.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+import typing_extensions
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.serialization import FieldMetadata
+from ...core.unchecked_base_model import UncheckedBaseModel
+
+
+class WebEngageIntegrationSet(UncheckedBaseModel):
+ """
+ A single WebEngage credential set
+ """
+
+ license_code: typing_extensions.Annotated[
+ str,
+ FieldMetadata(alias="licenseCode"),
+ pydantic.Field(alias="licenseCode", description="WebEngage license code"),
+ ]
+ environment: str = pydantic.Field()
+ """
+ WebEngage environment identifier
+ """
+
+ api_key: typing_extensions.Annotated[
+ str, FieldMetadata(alias="apiKey"), pydantic.Field(alias="apiKey", description="WebEngage API key")
+ ]
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/user/__init__.py b/src/smallestai/atoms/user/__init__.py
index 5cde0202..a4bbc8fc 100644
--- a/src/smallestai/atoms/user/__init__.py
+++ b/src/smallestai/atoms/user/__init__.py
@@ -2,3 +2,33 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import GetUserResponse, GetUserResponseData
+_dynamic_imports: typing.Dict[str, str] = {"GetUserResponse": ".types", "GetUserResponseData": ".types"}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = ["GetUserResponse", "GetUserResponseData"]
diff --git a/src/smallestai/atoms/user/client.py b/src/smallestai/atoms/user/client.py
index fa436362..69d76627 100644
--- a/src/smallestai/atoms/user/client.py
+++ b/src/smallestai/atoms/user/client.py
@@ -5,7 +5,6 @@
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ...core.request_options import RequestOptions
from .raw_client import AsyncRawUserClient, RawUserClient
-from .types.get_subscription_user_response import GetSubscriptionUserResponse
from .types.get_user_response import GetUserResponse
@@ -48,38 +47,6 @@ def get_user_details(self, *, request_options: typing.Optional[RequestOptions] =
_response = self._raw_client.get_user_details(request_options=request_options)
return _response.data
- def get_subscription(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetSubscriptionUserResponse:
- """
- Returns the caller's subscription details (plan ID + plan-tier limits) and
- feature-flag map. Useful for client-side gating of paid features.
-
- **`limits` is omitted** for the `ENTERPRISE` plan (and for unknown plan IDs) โ
- only `features` is returned in those cases.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetSubscriptionUserResponse
- Subscription details + limits + features.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.atoms.user.get_subscription()
- """
- _response = self._raw_client.get_subscription(request_options=request_options)
- return _response.data
-
class AsyncUserClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -127,43 +94,3 @@ async def main() -> None:
"""
_response = await self._raw_client.get_user_details(request_options=request_options)
return _response.data
-
- async def get_subscription(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetSubscriptionUserResponse:
- """
- Returns the caller's subscription details (plan ID + plan-tier limits) and
- feature-flag map. Useful for client-side gating of paid features.
-
- **`limits` is omitted** for the `ENTERPRISE` plan (and for unknown plan IDs) โ
- only `features` is returned in those cases.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetSubscriptionUserResponse
- Subscription details + limits + features.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.atoms.user.get_subscription()
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.get_subscription(request_options=request_options)
- return _response.data
diff --git a/src/smallestai/atoms/user/raw_client.py b/src/smallestai/atoms/user/raw_client.py
index 09f12182..06ada627 100644
--- a/src/smallestai/atoms/user/raw_client.py
+++ b/src/smallestai/atoms/user/raw_client.py
@@ -9,10 +9,8 @@
from ...core.parse_error import ParsingError
from ...core.request_options import RequestOptions
from ...core.unchecked_base_model import construct_type
-from ..errors.bad_request_error import BadRequestError
from ..errors.internal_server_error import InternalServerError
from ..errors.unauthorized_error import UnauthorizedError
-from .types.get_subscription_user_response import GetSubscriptionUserResponse
from .types.get_user_response import GetUserResponse
from pydantic import ValidationError
@@ -82,84 +80,6 @@ def get_user_details(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def get_subscription(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetSubscriptionUserResponse]:
- """
- Returns the caller's subscription details (plan ID + plan-tier limits) and
- feature-flag map. Useful for client-side gating of paid features.
-
- **`limits` is omitted** for the `ENTERPRISE` plan (and for unknown plan IDs) โ
- only `features` is returned in those cases.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[GetSubscriptionUserResponse]
- Subscription details + limits + features.
- """
- _response = self._client_wrapper.httpx_client.request(
- "user/subscription",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetSubscriptionUserResponse,
- construct_type(
- type_=GetSubscriptionUserResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
class AsyncRawUserClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -225,81 +145,3 @@ async def get_user_details(
status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def get_subscription(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetSubscriptionUserResponse]:
- """
- Returns the caller's subscription details (plan ID + plan-tier limits) and
- feature-flag map. Useful for client-side gating of paid features.
-
- **`limits` is omitted** for the `ENTERPRISE` plan (and for unknown plan IDs) โ
- only `features` is returned in those cases.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[GetSubscriptionUserResponse]
- Subscription details + limits + features.
- """
- _response = await self._client_wrapper.httpx_client.request(
- "user/subscription",
- base_url=self._client_wrapper.get_environment().atoms,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetSubscriptionUserResponse,
- construct_type(
- type_=GetSubscriptionUserResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/smallestai/atoms/user/types/__init__.py b/src/smallestai/atoms/user/types/__init__.py
index 5cde0202..f6f4b180 100644
--- a/src/smallestai/atoms/user/types/__init__.py
+++ b/src/smallestai/atoms/user/types/__init__.py
@@ -2,3 +2,37 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .get_user_response import GetUserResponse
+ from .get_user_response_data import GetUserResponseData
+_dynamic_imports: typing.Dict[str, str] = {
+ "GetUserResponse": ".get_user_response",
+ "GetUserResponseData": ".get_user_response_data",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = ["GetUserResponse", "GetUserResponseData"]
diff --git a/src/smallestai/atoms/user/types/get_subscription_user_response_data.py b/src/smallestai/atoms/user/types/get_subscription_user_response_data.py
deleted file mode 100644
index 1529ed81..00000000
--- a/src/smallestai/atoms/user/types/get_subscription_user_response_data.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .get_subscription_user_response_data_features import GetSubscriptionUserResponseDataFeatures
-from .get_subscription_user_response_data_limits import GetSubscriptionUserResponseDataLimits
-
-
-class GetSubscriptionUserResponseData(UncheckedBaseModel):
- plan_id: typing_extensions.Annotated[
- typing.Optional[str], FieldMetadata(alias="planId"), pydantic.Field(alias="planId")
- ] = None
- limits: typing.Optional[GetSubscriptionUserResponseDataLimits] = pydantic.Field(default=None)
- """
- Per-plan caps. Omitted for ENTERPRISE.
- """
-
- features: typing.Optional[GetSubscriptionUserResponseDataFeatures] = pydantic.Field(default=None)
- """
- Feature-flag map. All keys are boolean.
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/user/types/get_subscription_user_response_data_features.py b/src/smallestai/atoms/user/types/get_subscription_user_response_data_features.py
deleted file mode 100644
index 393ea82d..00000000
--- a/src/smallestai/atoms/user/types/get_subscription_user_response_data_features.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class GetSubscriptionUserResponseDataFeatures(UncheckedBaseModel):
- """
- Feature-flag map. All keys are boolean.
- """
-
- chat: typing.Optional[bool] = None
- telephony: typing.Optional[bool] = None
- campaign: typing.Optional[bool] = None
- webcall: typing.Optional[bool] = None
- knowledge_base: typing.Optional[bool] = None
- integrations: typing.Optional[bool] = None
- webhooks: typing.Optional[bool] = None
- telephony_inbound: typing.Optional[bool] = None
- telephony_outbound: typing.Optional[bool] = None
- waves_lightning: typing.Optional[bool] = None
- waves_lightning_large: typing.Optional[bool] = None
- waves_lightning_large_voice_clone: typing.Optional[bool] = None
- openai_gpt4o: typing_extensions.Annotated[
- typing.Optional[bool], FieldMetadata(alias="openai_gpt_4o"), pydantic.Field(alias="openai_gpt_4o")
- ] = None
- electron: typing.Optional[bool] = None
- workflow_editor: typing.Optional[bool] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/user/types/get_subscription_user_response_data_limits.py b/src/smallestai/atoms/user/types/get_subscription_user_response_data_limits.py
deleted file mode 100644
index a7fb452b..00000000
--- a/src/smallestai/atoms/user/types/get_subscription_user_response_data_limits.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class GetSubscriptionUserResponseDataLimits(UncheckedBaseModel):
- """
- Per-plan caps. Omitted for ENTERPRISE.
- """
-
- agents: typing.Optional[int] = None
- campaigns: typing.Optional[int] = None
- numbers: typing.Optional[int] = None
- daily_calls: typing_extensions.Annotated[
- typing.Optional[int], FieldMetadata(alias="dailyCalls"), pydantic.Field(alias="dailyCalls")
- ] = None
- concurrent_calls: typing_extensions.Annotated[
- typing.Optional[int], FieldMetadata(alias="concurrentCalls"), pydantic.Field(alias="concurrentCalls")
- ] = None
- knowledge_base_item_limit: typing_extensions.Annotated[
- typing.Optional[int],
- FieldMetadata(alias="knowledgeBaseItemLimit"),
- pydantic.Field(alias="knowledgeBaseItemLimit"),
- ] = None
- knowledge_base_limits: typing_extensions.Annotated[
- typing.Optional[typing.Dict[str, typing.Any]],
- FieldMetadata(alias="knowledgeBaseLimits"),
- pydantic.Field(alias="knowledgeBaseLimits"),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/atoms/webhooks/types/__init__.py b/src/smallestai/atoms/webhooks/types/__init__.py
index 5cde0202..d9cbbbd8 100644
--- a/src/smallestai/atoms/webhooks/types/__init__.py
+++ b/src/smallestai/atoms/webhooks/types/__init__.py
@@ -2,3 +2,66 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .create_webhooks_request_events_item import CreateWebhooksRequestEventsItem
+ from .create_webhooks_request_events_item_event_type import CreateWebhooksRequestEventsItemEventType
+ from .create_webhooks_response import CreateWebhooksResponse
+ from .delete_agent_agent_id_webhook_subscriptions_response import DeleteAgentAgentIdWebhookSubscriptionsResponse
+ from .delete_webhooks_response import DeleteWebhooksResponse
+ from .get_agent_agent_id_webhook_subscriptions_response import GetAgentAgentIdWebhookSubscriptionsResponse
+ from .get_webhook_response import GetWebhookResponse
+ from .get_webhook_response_data import GetWebhookResponseData
+ from .post_agent_agent_id_webhook_subscriptions_request_event_types_item import (
+ PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem,
+ )
+ from .post_agent_agent_id_webhook_subscriptions_response import PostAgentAgentIdWebhookSubscriptionsResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateWebhooksRequestEventsItem": ".create_webhooks_request_events_item",
+ "CreateWebhooksRequestEventsItemEventType": ".create_webhooks_request_events_item_event_type",
+ "CreateWebhooksResponse": ".create_webhooks_response",
+ "DeleteAgentAgentIdWebhookSubscriptionsResponse": ".delete_agent_agent_id_webhook_subscriptions_response",
+ "DeleteWebhooksResponse": ".delete_webhooks_response",
+ "GetAgentAgentIdWebhookSubscriptionsResponse": ".get_agent_agent_id_webhook_subscriptions_response",
+ "GetWebhookResponse": ".get_webhook_response",
+ "GetWebhookResponseData": ".get_webhook_response_data",
+ "PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem": ".post_agent_agent_id_webhook_subscriptions_request_event_types_item",
+ "PostAgentAgentIdWebhookSubscriptionsResponse": ".post_agent_agent_id_webhook_subscriptions_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateWebhooksRequestEventsItem",
+ "CreateWebhooksRequestEventsItemEventType",
+ "CreateWebhooksResponse",
+ "DeleteAgentAgentIdWebhookSubscriptionsResponse",
+ "DeleteWebhooksResponse",
+ "GetAgentAgentIdWebhookSubscriptionsResponse",
+ "GetWebhookResponse",
+ "GetWebhookResponseData",
+ "PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem",
+ "PostAgentAgentIdWebhookSubscriptionsResponse",
+]
diff --git a/src/smallestai/atoms/webhooks/types/post_webhook_request_events_item_event_type.py b/src/smallestai/atoms/webhooks/types/post_webhook_request_events_item_event_type.py
deleted file mode 100644
index 5020ab94..00000000
--- a/src/smallestai/atoms/webhooks/types/post_webhook_request_events_item_event_type.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-PostWebhookRequestEventsItemEventType = typing.Union[
- typing.Literal["pre-conversation", "post-conversation", "analytics-completed"], typing.Any
-]
diff --git a/src/smallestai/core/__init__.py b/src/smallestai/core/__init__.py
index 0fb6a372..9e2545ad 100644
--- a/src/smallestai/core/__init__.py
+++ b/src/smallestai/core/__init__.py
@@ -6,8 +6,75 @@
from importlib import import_module
if typing.TYPE_CHECKING:
- from .file import File, with_content_type
-_dynamic_imports: typing.Dict[str, str] = {"File": ".file", "with_content_type": ".file"}
+ from .api_error import ApiError
+ from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper
+ from .datetime_utils import Rfc2822DateTime, parse_rfc2822_datetime, serialize_datetime
+ from .events import EventEmitterMixin, EventType
+ from .file import File, convert_file_dict_to_httpx_tuples, with_content_type
+ from .http_client import AsyncHttpClient, HttpClient
+ from .http_response import AsyncHttpResponse, HttpResponse
+ from .jsonable_encoder import encode_path_param, jsonable_encoder
+ from .logging import ConsoleLogger, ILogger, LogConfig, LogLevel, Logger, create_logger
+ from .parse_error import ParsingError
+ from .pydantic_utilities import (
+ IS_PYDANTIC_V2,
+ UniversalBaseModel,
+ UniversalRootModel,
+ parse_obj_as,
+ universal_field_validator,
+ universal_root_validator,
+ update_forward_refs,
+ )
+ from .query_encoder import encode_query
+ from .remove_none_from_dict import remove_none_from_dict
+ from .request_options import RequestOptions
+ from .serialization import FieldMetadata, convert_and_respect_annotation_metadata
+ from .unchecked_base_model import UncheckedBaseModel, UnionMetadata, construct_type
+ from .websocket_compat import InvalidWebSocketStatus, get_status_code
+_dynamic_imports: typing.Dict[str, str] = {
+ "ApiError": ".api_error",
+ "AsyncClientWrapper": ".client_wrapper",
+ "AsyncHttpClient": ".http_client",
+ "AsyncHttpResponse": ".http_response",
+ "BaseClientWrapper": ".client_wrapper",
+ "ConsoleLogger": ".logging",
+ "EventEmitterMixin": ".events",
+ "EventType": ".events",
+ "FieldMetadata": ".serialization",
+ "File": ".file",
+ "HttpClient": ".http_client",
+ "HttpResponse": ".http_response",
+ "ILogger": ".logging",
+ "IS_PYDANTIC_V2": ".pydantic_utilities",
+ "InvalidWebSocketStatus": ".websocket_compat",
+ "LogConfig": ".logging",
+ "LogLevel": ".logging",
+ "Logger": ".logging",
+ "ParsingError": ".parse_error",
+ "RequestOptions": ".request_options",
+ "Rfc2822DateTime": ".datetime_utils",
+ "SyncClientWrapper": ".client_wrapper",
+ "UncheckedBaseModel": ".unchecked_base_model",
+ "UnionMetadata": ".unchecked_base_model",
+ "UniversalBaseModel": ".pydantic_utilities",
+ "UniversalRootModel": ".pydantic_utilities",
+ "construct_type": ".unchecked_base_model",
+ "convert_and_respect_annotation_metadata": ".serialization",
+ "convert_file_dict_to_httpx_tuples": ".file",
+ "create_logger": ".logging",
+ "encode_path_param": ".jsonable_encoder",
+ "encode_query": ".query_encoder",
+ "get_status_code": ".websocket_compat",
+ "jsonable_encoder": ".jsonable_encoder",
+ "parse_obj_as": ".pydantic_utilities",
+ "parse_rfc2822_datetime": ".datetime_utils",
+ "remove_none_from_dict": ".remove_none_from_dict",
+ "serialize_datetime": ".datetime_utils",
+ "universal_field_validator": ".pydantic_utilities",
+ "universal_root_validator": ".pydantic_utilities",
+ "update_forward_refs": ".pydantic_utilities",
+ "with_content_type": ".file",
+}
def __getattr__(attr_name: str) -> typing.Any:
@@ -31,4 +98,47 @@ def __dir__():
return sorted(lazy_attrs)
-__all__ = ["File", "with_content_type"]
+__all__ = [
+ "ApiError",
+ "AsyncClientWrapper",
+ "AsyncHttpClient",
+ "AsyncHttpResponse",
+ "BaseClientWrapper",
+ "ConsoleLogger",
+ "EventEmitterMixin",
+ "EventType",
+ "FieldMetadata",
+ "File",
+ "HttpClient",
+ "HttpResponse",
+ "ILogger",
+ "IS_PYDANTIC_V2",
+ "InvalidWebSocketStatus",
+ "LogConfig",
+ "LogLevel",
+ "Logger",
+ "ParsingError",
+ "RequestOptions",
+ "Rfc2822DateTime",
+ "SyncClientWrapper",
+ "UncheckedBaseModel",
+ "UnionMetadata",
+ "UniversalBaseModel",
+ "UniversalRootModel",
+ "construct_type",
+ "convert_and_respect_annotation_metadata",
+ "convert_file_dict_to_httpx_tuples",
+ "create_logger",
+ "encode_path_param",
+ "encode_query",
+ "get_status_code",
+ "jsonable_encoder",
+ "parse_obj_as",
+ "parse_rfc2822_datetime",
+ "remove_none_from_dict",
+ "serialize_datetime",
+ "universal_field_validator",
+ "universal_root_validator",
+ "update_forward_refs",
+ "with_content_type",
+]
diff --git a/src/smallestai/waves/errors/__init__.py b/src/smallestai/waves/errors/__init__.py
index 5cde0202..3d0326c1 100644
--- a/src/smallestai/waves/errors/__init__.py
+++ b/src/smallestai/waves/errors/__init__.py
@@ -2,3 +2,39 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .bad_request_error import BadRequestError
+ from .internal_server_error import InternalServerError
+ from .unauthorized_error import UnauthorizedError
+_dynamic_imports: typing.Dict[str, str] = {
+ "BadRequestError": ".bad_request_error",
+ "InternalServerError": ".internal_server_error",
+ "UnauthorizedError": ".unauthorized_error",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = ["BadRequestError", "InternalServerError", "UnauthorizedError"]
diff --git a/src/smallestai/waves/lightning_v2tts/__init__.py b/src/smallestai/waves/lightning_v2tts/__init__.py
deleted file mode 100644
index 5cde0202..00000000
--- a/src/smallestai/waves/lightning_v2tts/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-# isort: skip_file
-
diff --git a/src/smallestai/waves/lightning_v2tts/client.py b/src/smallestai/waves/lightning_v2tts/client.py
deleted file mode 100644
index 53322338..00000000
--- a/src/smallestai/waves/lightning_v2tts/client.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-import urllib.parse
-from contextlib import asynccontextmanager, contextmanager
-
-import websockets.sync.client as websockets_sync_client
-from ...core.api_error import ApiError
-from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
-from ...core.jsonable_encoder import jsonable_encoder
-from ...core.query_encoder import encode_query
-from ...core.remove_none_from_dict import remove_none_from_dict
-from ...core.request_options import RequestOptions
-from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code
-from .raw_client import AsyncRawLightningV2TtsClient, RawLightningV2TtsClient
-from .socket_client import AsyncLightningV2TtsSocketClient, LightningV2TtsSocketClient
-
-try:
- from websockets.legacy.client import connect as websockets_client_connect # type: ignore
-except ImportError:
- from websockets import connect as websockets_client_connect # type: ignore
-
-
-class LightningV2TtsClient:
- def __init__(self, *, client_wrapper: SyncClientWrapper):
- self._raw_client = RawLightningV2TtsClient(client_wrapper=client_wrapper)
-
- @property
- def with_raw_response(self) -> RawLightningV2TtsClient:
- """
- Retrieves a raw implementation of this client that returns raw responses.
-
- Returns
- -------
- RawLightningV2TtsClient
- """
- return self._raw_client
-
- @contextmanager
- def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.Iterator[LightningV2TtsSocketClient]:
- """
- The Lightning v2 WebSocket API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses WebSocket to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process. Perfect for interactive applications, voice assistants, and real-time communication systems that require immediate audio feedback. For an end-to-end example of how to use the Lightning v2 WebSocket API, check out [Text to Speech (WS) Example](https://github.com/smallest-inc/waves-examples/tree/main/lightning_v2/ws_streaming)
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- LightningV2TtsSocketClient
- """
- ws_url = (
- self._raw_client._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v2/get_speech/stream"
- )
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._raw_client._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol:
- yield LightningV2TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
-
-
-class AsyncLightningV2TtsClient:
- def __init__(self, *, client_wrapper: AsyncClientWrapper):
- self._raw_client = AsyncRawLightningV2TtsClient(client_wrapper=client_wrapper)
-
- @property
- def with_raw_response(self) -> AsyncRawLightningV2TtsClient:
- """
- Retrieves a raw implementation of this client that returns raw responses.
-
- Returns
- -------
- AsyncRawLightningV2TtsClient
- """
- return self._raw_client
-
- @asynccontextmanager
- async def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncLightningV2TtsSocketClient]:
- """
- The Lightning v2 WebSocket API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses WebSocket to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process. Perfect for interactive applications, voice assistants, and real-time communication systems that require immediate audio feedback. For an end-to-end example of how to use the Lightning v2 WebSocket API, check out [Text to Speech (WS) Example](https://github.com/smallest-inc/waves-examples/tree/main/lightning_v2/ws_streaming)
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncLightningV2TtsSocketClient
- """
- ws_url = (
- self._raw_client._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v2/get_speech/stream"
- )
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._raw_client._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- async with websockets_client_connect(ws_url, extra_headers=headers) as protocol:
- yield AsyncLightningV2TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
diff --git a/src/smallestai/waves/lightning_v2tts/raw_client.py b/src/smallestai/waves/lightning_v2tts/raw_client.py
deleted file mode 100644
index f8760b5b..00000000
--- a/src/smallestai/waves/lightning_v2tts/raw_client.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-import urllib.parse
-from contextlib import asynccontextmanager, contextmanager
-
-import websockets.sync.client as websockets_sync_client
-from ...core.api_error import ApiError
-from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
-from ...core.jsonable_encoder import jsonable_encoder
-from ...core.query_encoder import encode_query
-from ...core.remove_none_from_dict import remove_none_from_dict
-from ...core.request_options import RequestOptions
-from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code
-from .socket_client import AsyncLightningV2TtsSocketClient, LightningV2TtsSocketClient
-
-try:
- from websockets.legacy.client import connect as websockets_client_connect # type: ignore
-except ImportError:
- from websockets import connect as websockets_client_connect # type: ignore
-
-
-class RawLightningV2TtsClient:
- def __init__(self, *, client_wrapper: SyncClientWrapper):
- self._client_wrapper = client_wrapper
-
- @contextmanager
- def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.Iterator[LightningV2TtsSocketClient]:
- """
- The Lightning v2 WebSocket API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses WebSocket to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process. Perfect for interactive applications, voice assistants, and real-time communication systems that require immediate audio feedback. For an end-to-end example of how to use the Lightning v2 WebSocket API, check out [Text to Speech (WS) Example](https://github.com/smallest-inc/waves-examples/tree/main/lightning_v2/ws_streaming)
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- LightningV2TtsSocketClient
- """
- ws_url = self._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v2/get_speech/stream"
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol:
- yield LightningV2TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
-
-
-class AsyncRawLightningV2TtsClient:
- def __init__(self, *, client_wrapper: AsyncClientWrapper):
- self._client_wrapper = client_wrapper
-
- @asynccontextmanager
- async def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncLightningV2TtsSocketClient]:
- """
- The Lightning v2 WebSocket API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses WebSocket to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process. Perfect for interactive applications, voice assistants, and real-time communication systems that require immediate audio feedback. For an end-to-end example of how to use the Lightning v2 WebSocket API, check out [Text to Speech (WS) Example](https://github.com/smallest-inc/waves-examples/tree/main/lightning_v2/ws_streaming)
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncLightningV2TtsSocketClient
- """
- ws_url = self._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v2/get_speech/stream"
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- async with websockets_client_connect(ws_url, extra_headers=headers) as protocol:
- yield AsyncLightningV2TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
diff --git a/src/smallestai/waves/lightning_v2tts/socket_client.py b/src/smallestai/waves/lightning_v2tts/socket_client.py
deleted file mode 100644
index 3bf4a288..00000000
--- a/src/smallestai/waves/lightning_v2tts/socket_client.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import json
-import logging
-import typing
-from json.decoder import JSONDecodeError
-
-import websockets
-import websockets.sync.connection as websockets_sync_connection
-from ...core.events import EventEmitterMixin, EventType
-from ...core.unchecked_base_model import construct_type
-from .types.lightning_v2tts_request_message import LightningV2TtsRequestMessage
-from .types.lightning_v2tts_response_message import LightningV2TtsResponseMessage
-
-try:
- from websockets.legacy.client import WebSocketClientProtocol # type: ignore
-except ImportError:
- from websockets import WebSocketClientProtocol # type: ignore
-
-_logger = logging.getLogger(__name__)
-LightningV2TtsSocketClientResponse = typing.Union[LightningV2TtsResponseMessage]
-
-
-class AsyncLightningV2TtsSocketClient(EventEmitterMixin):
- def __init__(self, *, websocket: WebSocketClientProtocol):
- super().__init__()
- self._websocket = websocket
-
- async def __aiter__(self):
- async for message in self._websocket:
- if isinstance(message, bytes):
- yield message
- else:
- try:
- yield construct_type(type_=LightningV2TtsSocketClientResponse, object_=json.loads(message)) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
-
- async def start_listening(self):
- """
- Start listening for messages on the websocket connection.
-
- Emits events in the following order:
- - EventType.OPEN when connection is established
- - EventType.MESSAGE for each message received
- - EventType.ERROR if an error occurs
- - EventType.CLOSE when connection is closed
- """
- await self._emit_async(EventType.OPEN, None)
- try:
- async for raw_message in self._websocket:
- if isinstance(raw_message, bytes):
- parsed = raw_message
- else:
- json_data = json.loads(raw_message)
- try:
- parsed = construct_type(type_=LightningV2TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
- await self._emit_async(EventType.MESSAGE, parsed)
- except (websockets.WebSocketException, JSONDecodeError) as exc:
- await self._emit_async(EventType.ERROR, exc)
- finally:
- await self._emit_async(EventType.CLOSE, None)
-
- async def synthesize_streaming_lightning_v_2(self, message: LightningV2TtsRequestMessage) -> None:
- """
- Send a message to the websocket connection.
- The message will be sent as a LightningV2TtsRequestMessage.
- """
- await self._send_model(message)
-
- async def recv(self) -> LightningV2TtsSocketClientResponse:
- """
- Receive a message from the websocket connection.
- """
- data = await self._websocket.recv()
- if isinstance(data, bytes):
- return data # type: ignore
- json_data = json.loads(data)
- try:
- return construct_type(type_=LightningV2TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.")
- return json_data # type: ignore
-
- async def _send(self, data: typing.Any) -> None:
- """
- Send a message to the websocket connection.
- """
- if isinstance(data, dict):
- data = json.dumps(data)
- await self._websocket.send(data)
-
- async def _send_model(self, data: typing.Any) -> None:
- """
- Send a Pydantic model to the websocket connection.
- """
- await self._send(data.dict())
-
-
-class LightningV2TtsSocketClient(EventEmitterMixin):
- def __init__(self, *, websocket: websockets_sync_connection.Connection):
- super().__init__()
- self._websocket = websocket
-
- def __iter__(self):
- for message in self._websocket:
- if isinstance(message, bytes):
- yield message
- else:
- try:
- yield construct_type(type_=LightningV2TtsSocketClientResponse, object_=json.loads(message)) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
-
- def start_listening(self):
- """
- Start listening for messages on the websocket connection.
-
- Emits events in the following order:
- - EventType.OPEN when connection is established
- - EventType.MESSAGE for each message received
- - EventType.ERROR if an error occurs
- - EventType.CLOSE when connection is closed
- """
- self._emit(EventType.OPEN, None)
- try:
- for raw_message in self._websocket:
- if isinstance(raw_message, bytes):
- parsed = raw_message
- else:
- json_data = json.loads(raw_message)
- try:
- parsed = construct_type(type_=LightningV2TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
- self._emit(EventType.MESSAGE, parsed)
- except (websockets.WebSocketException, JSONDecodeError) as exc:
- self._emit(EventType.ERROR, exc)
- finally:
- self._emit(EventType.CLOSE, None)
-
- def synthesize_streaming_lightning_v_2(self, message: LightningV2TtsRequestMessage) -> None:
- """
- Send a message to the websocket connection.
- The message will be sent as a LightningV2TtsRequestMessage.
- """
- self._send_model(message)
-
- def recv(self) -> LightningV2TtsSocketClientResponse:
- """
- Receive a message from the websocket connection.
- """
- data = self._websocket.recv()
- if isinstance(data, bytes):
- return data # type: ignore
- json_data = json.loads(data)
- try:
- return construct_type(type_=LightningV2TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.")
- return json_data # type: ignore
-
- def _send(self, data: typing.Any) -> None:
- """
- Send a message to the websocket connection.
- """
- if isinstance(data, dict):
- data = json.dumps(data)
- self._websocket.send(data)
-
- def _send_model(self, data: typing.Any) -> None:
- """
- Send a Pydantic model to the websocket connection.
- """
- self._send(data.dict())
diff --git a/src/smallestai/waves/lightning_v2tts/types/__init__.py b/src/smallestai/waves/lightning_v2tts/types/__init__.py
deleted file mode 100644
index 5cde0202..00000000
--- a/src/smallestai/waves/lightning_v2tts/types/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-# isort: skip_file
-
diff --git a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_request_message.py b/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_request_message.py
deleted file mode 100644
index f96eb608..00000000
--- a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_request_message.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class LightningV2TtsRequestMessage(UncheckedBaseModel):
- voice_id: str = pydantic.Field()
- """
- The ID of the voice to use
- """
-
- text: str = pydantic.Field()
- """
- The text to convert to speech
- """
-
- max_buffer_flush_ms: typing.Optional[int] = pydantic.Field(default=None)
- """
- The maximum time (in ms) to wait for more input before generating output. It flushes when either this time is reached or enough input is received for optimal outputโwhichever comes first. This is useful for input streams. Deafults to 0
- """
-
- continue_: typing_extensions.Annotated[
- typing.Optional[bool],
- FieldMetadata(alias="continue"),
- pydantic.Field(
- alias="continue",
- description="This setting controls whether the system should buffer and wait for more input after receiving the current one. If not set, it assumes no more input is coming.",
- ),
- ] = None
- flush: typing.Optional[bool] = pydantic.Field(default=None)
- """
- This setting controls whether the system should flush the current buffer.
- """
-
- complete_backoff_ms: typing.Optional[float] = pydantic.Field(default=None)
- """
- The time in ms to wait after the last chunk is sent before sending the complete response. Default is 4000ms. Maximum is 10000ms.
- """
-
- language: typing.Optional[str] = pydantic.Field(default=None)
- """
- The language code, available options: `en`, `hi`, `mr`, `kn`, `ta`, `bn`, `gu`, `de`, `fr`, `es`, `it`, `pl`, `nl`, `ru`, `ar`, `he`
- """
-
- sample_rate: typing.Optional[int] = pydantic.Field(default=None)
- """
- Audio sample rate in Hz. Supported values: 8000, 16000, 24000, 44100
- """
-
- speed: typing.Optional[float] = pydantic.Field(default=None)
- """
- Speaking speed multiplier
- """
-
- consistency: typing.Optional[float] = pydantic.Field(default=None)
- """
- Voice consistency parameter
- """
-
- enhancement: typing.Optional[int] = pydantic.Field(default=None)
- """
- Audio enhancement level
- """
-
- similarity: typing.Optional[float] = pydantic.Field(default=None)
- """
- Voice similarity parameter
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message.py b/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message.py
deleted file mode 100644
index 71b8920c..00000000
--- a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .lightning_v2tts_response_message_data import LightningV2TtsResponseMessageData
-from .lightning_v2tts_response_message_status import LightningV2TtsResponseMessageStatus
-
-
-class LightningV2TtsResponseMessage(UncheckedBaseModel):
- request_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Unique identifier for the TTS request
- """
-
- status: typing.Optional[LightningV2TtsResponseMessageStatus] = pydantic.Field(default=None)
- """
- Status of the TTS request, `chunk` indicates incoming audio chunk, `complete` indicates completion.
- """
-
- data: typing.Optional[LightningV2TtsResponseMessageData] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_data.py b/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_data.py
deleted file mode 100644
index 9cadb65a..00000000
--- a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_data.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class LightningV2TtsResponseMessageData(UncheckedBaseModel):
- audio: typing.Optional[str] = pydantic.Field(default=None)
- """
- Base64-encoded audio chunk
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_status.py b/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_status.py
deleted file mode 100644
index 11420c5f..00000000
--- a/src/smallestai/waves/lightning_v2tts/types/lightning_v2tts_response_message_status.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-LightningV2TtsResponseMessageStatus = typing.Union[typing.Literal["chunk", "complete"], typing.Any]
diff --git a/src/smallestai/waves/lightning_v31tts/__init__.py b/src/smallestai/waves/lightning_v31tts/__init__.py
deleted file mode 100644
index 5cde0202..00000000
--- a/src/smallestai/waves/lightning_v31tts/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-# isort: skip_file
-
diff --git a/src/smallestai/waves/lightning_v31tts/client.py b/src/smallestai/waves/lightning_v31tts/client.py
deleted file mode 100644
index 7c7b6269..00000000
--- a/src/smallestai/waves/lightning_v31tts/client.py
+++ /dev/null
@@ -1,337 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-import urllib.parse
-from contextlib import asynccontextmanager, contextmanager
-
-import websockets.sync.client as websockets_sync_client
-from ...core.api_error import ApiError
-from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
-from ...core.jsonable_encoder import jsonable_encoder
-from ...core.query_encoder import encode_query
-from ...core.remove_none_from_dict import remove_none_from_dict
-from ...core.request_options import RequestOptions
-from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code
-from .raw_client import AsyncRawLightningV31TtsClient, RawLightningV31TtsClient
-from .socket_client import AsyncLightningV31TtsSocketClient, LightningV31TtsSocketClient
-
-try:
- from websockets.legacy.client import connect as websockets_client_connect # type: ignore
-except ImportError:
- from websockets import connect as websockets_client_connect # type: ignore
-
-
-class LightningV31TtsClient:
- def __init__(self, *, client_wrapper: SyncClientWrapper):
- self._raw_client = RawLightningV31TtsClient(client_wrapper=client_wrapper)
-
- @property
- def with_raw_response(self) -> RawLightningV31TtsClient:
- """
- Retrieves a raw implementation of this client that returns raw responses.
-
- Returns
- -------
- RawLightningV31TtsClient
- """
- return self._raw_client
-
- @contextmanager
- def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.Iterator[LightningV31TtsSocketClient]:
- """
- # Lightning v3.1 WebSocket (endpoint will be deprecated)
-
- **Endpoint scheduled for retirement.** This URL will stop accepting WebSocket connections **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`wss://api.smallest.ai/waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/tts). The protocol and payload shape are identical; only the URL changes. Use the `model` body field to pick the model.
-
- Synthesize speech over a persistent WebSocket. Audio chunks come back as text arrives โ the fit-for-purpose path when *text itself* is streaming, like LLM token output.
-
- ## When to use this
-
- - **Use this** when text is generated incrementally and you want audio to start playing as soon as the first words are produced โ LLM streaming, live captioning, voice agents.
- - **Use SSE streaming** when you have the full text up front but want low-latency playback.
- - **Use sync `/get_speech`** when latency isn't critical and a single buffer is easier to handle.
-
- ## How it works
-
- 1. Open a WebSocket to `wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream` with `Authorization: Bearer `.
- 2. Send a JSON message per text chunk: `{ "voice_id": "magnus", "text": "...", "continue": true, "flush": false, ... }`. Set `continue: true` to keep the session open between sends.
- 3. The server pushes back JSON messages with `data.audio` (base64 PCM) and a `status` field (`chunk`, `complete`).
- 4. When you're done sending text, push one final message with `flush: true` and an empty `text` โ the server finishes the buffer and sends `status: "complete"`.
-
- ## Concurrency and rate limits
-
- - 1 concurrency unit = 1 active TTS request at a time.
- - You can open up to 5 WebSocket connections per concurrency unit.
- - Requests beyond your concurrency limit are rejected with an error โ queue on your side.
-
- Examples: 3 concurrency = up to 15 open sockets, but only 3 active synth requests at once.
-
- ## Examples
-
- **Python** (using `smallestai>=4.4.0` โ the 4.3.1 compatibility shim):
- ```python
- from smallestai.waves import WavesStreamingTTS, TTSConfig
-
- config = TTSConfig(voice_id="magnus", api_key="YOUR_API_KEY", sample_rate=24000)
- tts = WavesStreamingTTS(config)
-
- def text_chunks():
- # Pretend this is your LLM streaming tokens.
- for word in ["Hello,", " I am", " streaming", " speech."]:
- yield word
-
- with open("speech.pcm", "wb") as out:
- for audio_chunk in tts.synthesize_streaming(text_chunks(), continue_stream=True, auto_flush=True):
- out.write(audio_chunk)
- ```
-
- For new code, you can also use the namespaced Fern client: `client.waves.lightning_v31tts.connect(...)` which returns a typed socket you drive yourself.
-
- **JavaScript / TypeScript** (using `ws`)
- ```typescript
- import WebSocket from "ws";
-
- const ws = new WebSocket("wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream", {
- headers: { Authorization: `Bearer ${process.env.SMALLEST_API_KEY}` },
- });
-
- ws.on("open", () => {
- for (const text of ["Hello,", " I am", " streaming", " speech."]) {
- ws.send(JSON.stringify({
- voice_id: "magnus",
- text,
- sample_rate: 24000,
- continue: true,
- flush: false,
- }));
- }
- // Final flush
- ws.send(JSON.stringify({ voice_id: "magnus", text: "", flush: true }));
- });
-
- ws.on("message", (raw) => {
- const msg = JSON.parse(raw.toString());
- if (msg.status === "complete") return ws.close();
- const pcm = Buffer.from(msg.data.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- });
- ```
-
- ## Optional features
-
- Set `word_timestamps: true` on a request to receive per-word timing events (`status: "word_timestamp"`) interleaved with audio chunks. Supported on English + Hindi base-queue voices. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- ## Common gotchas
-
- - **`continue: true` keeps the session alive.** Without it, the server closes after the first chunk. Send `flush: true` with empty `text` when you're done.
- - **44.1 kHz is supported but most pipelines want 24 kHz.** Match your downstream sample rate to avoid resampling.
- - **Backpressure**: if you push text faster than the server can synthesize, audio chunks queue server-side. Watch for `complete_backoff_ms` in responses.
- - **One concurrency unit = one in-flight synth.** Holding 5 sockets open doesn't get you 5ร throughput unless you upgrade concurrency.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so connect with the `ws` library directly as shown above.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- LightningV31TtsSocketClient
- """
- ws_url = (
- self._raw_client._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v3.1/get_speech/stream"
- )
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._raw_client._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol:
- yield LightningV31TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
-
-
-class AsyncLightningV31TtsClient:
- def __init__(self, *, client_wrapper: AsyncClientWrapper):
- self._raw_client = AsyncRawLightningV31TtsClient(client_wrapper=client_wrapper)
-
- @property
- def with_raw_response(self) -> AsyncRawLightningV31TtsClient:
- """
- Retrieves a raw implementation of this client that returns raw responses.
-
- Returns
- -------
- AsyncRawLightningV31TtsClient
- """
- return self._raw_client
-
- @asynccontextmanager
- async def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncLightningV31TtsSocketClient]:
- """
- # Lightning v3.1 WebSocket (endpoint will be deprecated)
-
- **Endpoint scheduled for retirement.** This URL will stop accepting WebSocket connections **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`wss://api.smallest.ai/waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/tts). The protocol and payload shape are identical; only the URL changes. Use the `model` body field to pick the model.
-
- Synthesize speech over a persistent WebSocket. Audio chunks come back as text arrives โ the fit-for-purpose path when *text itself* is streaming, like LLM token output.
-
- ## When to use this
-
- - **Use this** when text is generated incrementally and you want audio to start playing as soon as the first words are produced โ LLM streaming, live captioning, voice agents.
- - **Use SSE streaming** when you have the full text up front but want low-latency playback.
- - **Use sync `/get_speech`** when latency isn't critical and a single buffer is easier to handle.
-
- ## How it works
-
- 1. Open a WebSocket to `wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream` with `Authorization: Bearer `.
- 2. Send a JSON message per text chunk: `{ "voice_id": "magnus", "text": "...", "continue": true, "flush": false, ... }`. Set `continue: true` to keep the session open between sends.
- 3. The server pushes back JSON messages with `data.audio` (base64 PCM) and a `status` field (`chunk`, `complete`).
- 4. When you're done sending text, push one final message with `flush: true` and an empty `text` โ the server finishes the buffer and sends `status: "complete"`.
-
- ## Concurrency and rate limits
-
- - 1 concurrency unit = 1 active TTS request at a time.
- - You can open up to 5 WebSocket connections per concurrency unit.
- - Requests beyond your concurrency limit are rejected with an error โ queue on your side.
-
- Examples: 3 concurrency = up to 15 open sockets, but only 3 active synth requests at once.
-
- ## Examples
-
- **Python** (using `smallestai>=4.4.0` โ the 4.3.1 compatibility shim):
- ```python
- from smallestai.waves import WavesStreamingTTS, TTSConfig
-
- config = TTSConfig(voice_id="magnus", api_key="YOUR_API_KEY", sample_rate=24000)
- tts = WavesStreamingTTS(config)
-
- def text_chunks():
- # Pretend this is your LLM streaming tokens.
- for word in ["Hello,", " I am", " streaming", " speech."]:
- yield word
-
- with open("speech.pcm", "wb") as out:
- for audio_chunk in tts.synthesize_streaming(text_chunks(), continue_stream=True, auto_flush=True):
- out.write(audio_chunk)
- ```
-
- For new code, you can also use the namespaced Fern client: `client.waves.lightning_v31tts.connect(...)` which returns a typed socket you drive yourself.
-
- **JavaScript / TypeScript** (using `ws`)
- ```typescript
- import WebSocket from "ws";
-
- const ws = new WebSocket("wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream", {
- headers: { Authorization: `Bearer ${process.env.SMALLEST_API_KEY}` },
- });
-
- ws.on("open", () => {
- for (const text of ["Hello,", " I am", " streaming", " speech."]) {
- ws.send(JSON.stringify({
- voice_id: "magnus",
- text,
- sample_rate: 24000,
- continue: true,
- flush: false,
- }));
- }
- // Final flush
- ws.send(JSON.stringify({ voice_id: "magnus", text: "", flush: true }));
- });
-
- ws.on("message", (raw) => {
- const msg = JSON.parse(raw.toString());
- if (msg.status === "complete") return ws.close();
- const pcm = Buffer.from(msg.data.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- });
- ```
-
- ## Optional features
-
- Set `word_timestamps: true` on a request to receive per-word timing events (`status: "word_timestamp"`) interleaved with audio chunks. Supported on English + Hindi base-queue voices. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- ## Common gotchas
-
- - **`continue: true` keeps the session alive.** Without it, the server closes after the first chunk. Send `flush: true` with empty `text` when you're done.
- - **44.1 kHz is supported but most pipelines want 24 kHz.** Match your downstream sample rate to avoid resampling.
- - **Backpressure**: if you push text faster than the server can synthesize, audio chunks queue server-side. Watch for `complete_backoff_ms` in responses.
- - **One concurrency unit = one in-flight synth.** Holding 5 sockets open doesn't get you 5ร throughput unless you upgrade concurrency.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so connect with the `ws` library directly as shown above.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncLightningV31TtsSocketClient
- """
- ws_url = (
- self._raw_client._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v3.1/get_speech/stream"
- )
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._raw_client._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- async with websockets_client_connect(ws_url, extra_headers=headers) as protocol:
- yield AsyncLightningV31TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
diff --git a/src/smallestai/waves/lightning_v31tts/raw_client.py b/src/smallestai/waves/lightning_v31tts/raw_client.py
deleted file mode 100644
index c78c4f18..00000000
--- a/src/smallestai/waves/lightning_v31tts/raw_client.py
+++ /dev/null
@@ -1,310 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-import urllib.parse
-from contextlib import asynccontextmanager, contextmanager
-
-import websockets.sync.client as websockets_sync_client
-from ...core.api_error import ApiError
-from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
-from ...core.jsonable_encoder import jsonable_encoder
-from ...core.query_encoder import encode_query
-from ...core.remove_none_from_dict import remove_none_from_dict
-from ...core.request_options import RequestOptions
-from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code
-from .socket_client import AsyncLightningV31TtsSocketClient, LightningV31TtsSocketClient
-
-try:
- from websockets.legacy.client import connect as websockets_client_connect # type: ignore
-except ImportError:
- from websockets import connect as websockets_client_connect # type: ignore
-
-
-class RawLightningV31TtsClient:
- def __init__(self, *, client_wrapper: SyncClientWrapper):
- self._client_wrapper = client_wrapper
-
- @contextmanager
- def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.Iterator[LightningV31TtsSocketClient]:
- """
- # Lightning v3.1 WebSocket (endpoint will be deprecated)
-
- **Endpoint scheduled for retirement.** This URL will stop accepting WebSocket connections **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`wss://api.smallest.ai/waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/tts). The protocol and payload shape are identical; only the URL changes. Use the `model` body field to pick the model.
-
- Synthesize speech over a persistent WebSocket. Audio chunks come back as text arrives โ the fit-for-purpose path when *text itself* is streaming, like LLM token output.
-
- ## When to use this
-
- - **Use this** when text is generated incrementally and you want audio to start playing as soon as the first words are produced โ LLM streaming, live captioning, voice agents.
- - **Use SSE streaming** when you have the full text up front but want low-latency playback.
- - **Use sync `/get_speech`** when latency isn't critical and a single buffer is easier to handle.
-
- ## How it works
-
- 1. Open a WebSocket to `wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream` with `Authorization: Bearer `.
- 2. Send a JSON message per text chunk: `{ "voice_id": "magnus", "text": "...", "continue": true, "flush": false, ... }`. Set `continue: true` to keep the session open between sends.
- 3. The server pushes back JSON messages with `data.audio` (base64 PCM) and a `status` field (`chunk`, `complete`).
- 4. When you're done sending text, push one final message with `flush: true` and an empty `text` โ the server finishes the buffer and sends `status: "complete"`.
-
- ## Concurrency and rate limits
-
- - 1 concurrency unit = 1 active TTS request at a time.
- - You can open up to 5 WebSocket connections per concurrency unit.
- - Requests beyond your concurrency limit are rejected with an error โ queue on your side.
-
- Examples: 3 concurrency = up to 15 open sockets, but only 3 active synth requests at once.
-
- ## Examples
-
- **Python** (using `smallestai>=4.4.0` โ the 4.3.1 compatibility shim):
- ```python
- from smallestai.waves import WavesStreamingTTS, TTSConfig
-
- config = TTSConfig(voice_id="magnus", api_key="YOUR_API_KEY", sample_rate=24000)
- tts = WavesStreamingTTS(config)
-
- def text_chunks():
- # Pretend this is your LLM streaming tokens.
- for word in ["Hello,", " I am", " streaming", " speech."]:
- yield word
-
- with open("speech.pcm", "wb") as out:
- for audio_chunk in tts.synthesize_streaming(text_chunks(), continue_stream=True, auto_flush=True):
- out.write(audio_chunk)
- ```
-
- For new code, you can also use the namespaced Fern client: `client.waves.lightning_v31tts.connect(...)` which returns a typed socket you drive yourself.
-
- **JavaScript / TypeScript** (using `ws`)
- ```typescript
- import WebSocket from "ws";
-
- const ws = new WebSocket("wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream", {
- headers: { Authorization: `Bearer ${process.env.SMALLEST_API_KEY}` },
- });
-
- ws.on("open", () => {
- for (const text of ["Hello,", " I am", " streaming", " speech."]) {
- ws.send(JSON.stringify({
- voice_id: "magnus",
- text,
- sample_rate: 24000,
- continue: true,
- flush: false,
- }));
- }
- // Final flush
- ws.send(JSON.stringify({ voice_id: "magnus", text: "", flush: true }));
- });
-
- ws.on("message", (raw) => {
- const msg = JSON.parse(raw.toString());
- if (msg.status === "complete") return ws.close();
- const pcm = Buffer.from(msg.data.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- });
- ```
-
- ## Optional features
-
- Set `word_timestamps: true` on a request to receive per-word timing events (`status: "word_timestamp"`) interleaved with audio chunks. Supported on English + Hindi base-queue voices. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- ## Common gotchas
-
- - **`continue: true` keeps the session alive.** Without it, the server closes after the first chunk. Send `flush: true` with empty `text` when you're done.
- - **44.1 kHz is supported but most pipelines want 24 kHz.** Match your downstream sample rate to avoid resampling.
- - **Backpressure**: if you push text faster than the server can synthesize, audio chunks queue server-side. Watch for `complete_backoff_ms` in responses.
- - **One concurrency unit = one in-flight synth.** Holding 5 sockets open doesn't get you 5ร throughput unless you upgrade concurrency.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so connect with the `ws` library directly as shown above.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- LightningV31TtsSocketClient
- """
- ws_url = self._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v3.1/get_speech/stream"
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol:
- yield LightningV31TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
-
-
-class AsyncRawLightningV31TtsClient:
- def __init__(self, *, client_wrapper: AsyncClientWrapper):
- self._client_wrapper = client_wrapper
-
- @asynccontextmanager
- async def connect(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncLightningV31TtsSocketClient]:
- """
- # Lightning v3.1 WebSocket (endpoint will be deprecated)
-
- **Endpoint scheduled for retirement.** This URL will stop accepting WebSocket connections **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`wss://api.smallest.ai/waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/tts). The protocol and payload shape are identical; only the URL changes. Use the `model` body field to pick the model.
-
- Synthesize speech over a persistent WebSocket. Audio chunks come back as text arrives โ the fit-for-purpose path when *text itself* is streaming, like LLM token output.
-
- ## When to use this
-
- - **Use this** when text is generated incrementally and you want audio to start playing as soon as the first words are produced โ LLM streaming, live captioning, voice agents.
- - **Use SSE streaming** when you have the full text up front but want low-latency playback.
- - **Use sync `/get_speech`** when latency isn't critical and a single buffer is easier to handle.
-
- ## How it works
-
- 1. Open a WebSocket to `wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream` with `Authorization: Bearer `.
- 2. Send a JSON message per text chunk: `{ "voice_id": "magnus", "text": "...", "continue": true, "flush": false, ... }`. Set `continue: true` to keep the session open between sends.
- 3. The server pushes back JSON messages with `data.audio` (base64 PCM) and a `status` field (`chunk`, `complete`).
- 4. When you're done sending text, push one final message with `flush: true` and an empty `text` โ the server finishes the buffer and sends `status: "complete"`.
-
- ## Concurrency and rate limits
-
- - 1 concurrency unit = 1 active TTS request at a time.
- - You can open up to 5 WebSocket connections per concurrency unit.
- - Requests beyond your concurrency limit are rejected with an error โ queue on your side.
-
- Examples: 3 concurrency = up to 15 open sockets, but only 3 active synth requests at once.
-
- ## Examples
-
- **Python** (using `smallestai>=4.4.0` โ the 4.3.1 compatibility shim):
- ```python
- from smallestai.waves import WavesStreamingTTS, TTSConfig
-
- config = TTSConfig(voice_id="magnus", api_key="YOUR_API_KEY", sample_rate=24000)
- tts = WavesStreamingTTS(config)
-
- def text_chunks():
- # Pretend this is your LLM streaming tokens.
- for word in ["Hello,", " I am", " streaming", " speech."]:
- yield word
-
- with open("speech.pcm", "wb") as out:
- for audio_chunk in tts.synthesize_streaming(text_chunks(), continue_stream=True, auto_flush=True):
- out.write(audio_chunk)
- ```
-
- For new code, you can also use the namespaced Fern client: `client.waves.lightning_v31tts.connect(...)` which returns a typed socket you drive yourself.
-
- **JavaScript / TypeScript** (using `ws`)
- ```typescript
- import WebSocket from "ws";
-
- const ws = new WebSocket("wss://api.smallest.ai/waves/v1/lightning-v3.1/get_speech/stream", {
- headers: { Authorization: `Bearer ${process.env.SMALLEST_API_KEY}` },
- });
-
- ws.on("open", () => {
- for (const text of ["Hello,", " I am", " streaming", " speech."]) {
- ws.send(JSON.stringify({
- voice_id: "magnus",
- text,
- sample_rate: 24000,
- continue: true,
- flush: false,
- }));
- }
- // Final flush
- ws.send(JSON.stringify({ voice_id: "magnus", text: "", flush: true }));
- });
-
- ws.on("message", (raw) => {
- const msg = JSON.parse(raw.toString());
- if (msg.status === "complete") return ws.close();
- const pcm = Buffer.from(msg.data.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- });
- ```
-
- ## Optional features
-
- Set `word_timestamps: true` on a request to receive per-word timing events (`status: "word_timestamp"`) interleaved with audio chunks. Supported on English + Hindi base-queue voices. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- ## Common gotchas
-
- - **`continue: true` keeps the session alive.** Without it, the server closes after the first chunk. Send `flush: true` with empty `text` when you're done.
- - **44.1 kHz is supported but most pipelines want 24 kHz.** Match your downstream sample rate to avoid resampling.
- - **Backpressure**: if you push text faster than the server can synthesize, audio chunks queue server-side. Watch for `complete_backoff_ms` in responses.
- - **One concurrency unit = one in-flight synth.** Holding 5 sockets open doesn't get you 5ร throughput unless you upgrade concurrency.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so connect with the `ws` library directly as shown above.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncLightningV31TtsSocketClient
- """
- ws_url = self._client_wrapper.get_environment().waves_ws + "/waves/v1/lightning-v3.1/get_speech/stream"
- _encoded_query_params = encode_query(
- jsonable_encoder(
- remove_none_from_dict(
- {
- **(
- request_options.get("additional_query_parameters", {}) or {}
- if request_options is not None
- else {}
- ),
- }
- )
- )
- )
- if _encoded_query_params:
- ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
- headers = self._client_wrapper.get_headers()
- if request_options and "additional_headers" in request_options:
- headers.update(request_options["additional_headers"])
- try:
- async with websockets_client_connect(ws_url, extra_headers=headers) as protocol:
- yield AsyncLightningV31TtsSocketClient(websocket=protocol)
- except InvalidWebSocketStatus as exc:
- status_code: int = get_status_code(exc)
- if status_code == 401:
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Websocket initialized with invalid credentials.",
- )
- raise ApiError(
- status_code=status_code,
- headers=dict(headers),
- body="Unexpected error when initializing websocket connection.",
- )
diff --git a/src/smallestai/waves/lightning_v31tts/socket_client.py b/src/smallestai/waves/lightning_v31tts/socket_client.py
deleted file mode 100644
index 173130f8..00000000
--- a/src/smallestai/waves/lightning_v31tts/socket_client.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import json
-import logging
-import typing
-from json.decoder import JSONDecodeError
-
-import websockets
-import websockets.sync.connection as websockets_sync_connection
-from ...core.events import EventEmitterMixin, EventType
-from ...core.unchecked_base_model import construct_type
-from .types.lightning_v31tts_request_message import LightningV31TtsRequestMessage
-from .types.lightning_v31tts_response_message import LightningV31TtsResponseMessage
-
-try:
- from websockets.legacy.client import WebSocketClientProtocol # type: ignore
-except ImportError:
- from websockets import WebSocketClientProtocol # type: ignore
-
-_logger = logging.getLogger(__name__)
-LightningV31TtsSocketClientResponse = typing.Union[LightningV31TtsResponseMessage]
-
-
-class AsyncLightningV31TtsSocketClient(EventEmitterMixin):
- def __init__(self, *, websocket: WebSocketClientProtocol):
- super().__init__()
- self._websocket = websocket
-
- async def __aiter__(self):
- async for message in self._websocket:
- if isinstance(message, bytes):
- yield message
- else:
- try:
- yield construct_type(type_=LightningV31TtsSocketClientResponse, object_=json.loads(message)) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
-
- async def start_listening(self):
- """
- Start listening for messages on the websocket connection.
-
- Emits events in the following order:
- - EventType.OPEN when connection is established
- - EventType.MESSAGE for each message received
- - EventType.ERROR if an error occurs
- - EventType.CLOSE when connection is closed
- """
- await self._emit_async(EventType.OPEN, None)
- try:
- async for raw_message in self._websocket:
- if isinstance(raw_message, bytes):
- parsed = raw_message
- else:
- json_data = json.loads(raw_message)
- try:
- parsed = construct_type(type_=LightningV31TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
- await self._emit_async(EventType.MESSAGE, parsed)
- except (websockets.WebSocketException, JSONDecodeError) as exc:
- await self._emit_async(EventType.ERROR, exc)
- finally:
- await self._emit_async(EventType.CLOSE, None)
-
- async def synthesize_streaming_lightning_v_3_1(self, message: LightningV31TtsRequestMessage) -> None:
- """
- Send a message to the websocket connection.
- The message will be sent as a LightningV31TtsRequestMessage.
- """
- await self._send_model(message)
-
- async def recv(self) -> LightningV31TtsSocketClientResponse:
- """
- Receive a message from the websocket connection.
- """
- data = await self._websocket.recv()
- if isinstance(data, bytes):
- return data # type: ignore
- json_data = json.loads(data)
- try:
- return construct_type(type_=LightningV31TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.")
- return json_data # type: ignore
-
- async def _send(self, data: typing.Any) -> None:
- """
- Send a message to the websocket connection.
- """
- if isinstance(data, dict):
- data = json.dumps(data)
- await self._websocket.send(data)
-
- async def _send_model(self, data: typing.Any) -> None:
- """
- Send a Pydantic model to the websocket connection.
- """
- await self._send(data.dict())
-
-
-class LightningV31TtsSocketClient(EventEmitterMixin):
- def __init__(self, *, websocket: websockets_sync_connection.Connection):
- super().__init__()
- self._websocket = websocket
-
- def __iter__(self):
- for message in self._websocket:
- if isinstance(message, bytes):
- yield message
- else:
- try:
- yield construct_type(type_=LightningV31TtsSocketClientResponse, object_=json.loads(message)) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
-
- def start_listening(self):
- """
- Start listening for messages on the websocket connection.
-
- Emits events in the following order:
- - EventType.OPEN when connection is established
- - EventType.MESSAGE for each message received
- - EventType.ERROR if an error occurs
- - EventType.CLOSE when connection is closed
- """
- self._emit(EventType.OPEN, None)
- try:
- for raw_message in self._websocket:
- if isinstance(raw_message, bytes):
- parsed = raw_message
- else:
- json_data = json.loads(raw_message)
- try:
- parsed = construct_type(type_=LightningV31TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning(
- "Skipping unknown WebSocket message; update your SDK version to support new message types."
- )
- continue
- self._emit(EventType.MESSAGE, parsed)
- except (websockets.WebSocketException, JSONDecodeError) as exc:
- self._emit(EventType.ERROR, exc)
- finally:
- self._emit(EventType.CLOSE, None)
-
- def synthesize_streaming_lightning_v_3_1(self, message: LightningV31TtsRequestMessage) -> None:
- """
- Send a message to the websocket connection.
- The message will be sent as a LightningV31TtsRequestMessage.
- """
- self._send_model(message)
-
- def recv(self) -> LightningV31TtsSocketClientResponse:
- """
- Receive a message from the websocket connection.
- """
- data = self._websocket.recv()
- if isinstance(data, bytes):
- return data # type: ignore
- json_data = json.loads(data)
- try:
- return construct_type(type_=LightningV31TtsSocketClientResponse, object_=json_data) # type: ignore
- except Exception:
- _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.")
- return json_data # type: ignore
-
- def _send(self, data: typing.Any) -> None:
- """
- Send a message to the websocket connection.
- """
- if isinstance(data, dict):
- data = json.dumps(data)
- self._websocket.send(data)
-
- def _send_model(self, data: typing.Any) -> None:
- """
- Send a Pydantic model to the websocket connection.
- """
- self._send(data.dict())
diff --git a/src/smallestai/waves/lightning_v31tts/types/__init__.py b/src/smallestai/waves/lightning_v31tts/types/__init__.py
deleted file mode 100644
index 5cde0202..00000000
--- a/src/smallestai/waves/lightning_v31tts/types/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-# isort: skip_file
-
diff --git a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message.py b/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message.py
deleted file mode 100644
index 53492bd4..00000000
--- a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-import typing_extensions
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.serialization import FieldMetadata
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .lightning_v31tts_request_message_language import LightningV31TtsRequestMessageLanguage
-from .lightning_v31tts_request_message_model import LightningV31TtsRequestMessageModel
-
-
-class LightningV31TtsRequestMessage(UncheckedBaseModel):
- voice_id: str = pydantic.Field()
- """
- The ID of the voice to use
- """
-
- text: str = pydantic.Field()
- """
- The text to convert to speech
- """
-
- model: typing.Optional[LightningV31TtsRequestMessageModel] = pydantic.Field(default=None)
- """
- TTS model to route the request to.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
-
- New integrations should connect to the unified
- `/waves/v1/tts/live` WebSocket instead of this endpoint, but the
- `model` field is supported here for backwards-compatible Pro
- opt-in.
- """
-
- max_buffer_flush_ms: typing.Optional[int] = pydantic.Field(default=None)
- """
- The maximum time (in ms) to wait for more input before generating output. It flushes when either this time is reached or enough input is received for optimal outputโwhichever comes first. This is useful for input streams. Defaults to 0
- """
-
- continue_: typing_extensions.Annotated[
- typing.Optional[bool],
- FieldMetadata(alias="continue"),
- pydantic.Field(
- alias="continue",
- description="This setting controls whether the system should buffer and wait for more input after receiving the current one. If not set, it assumes no more input is coming.",
- ),
- ] = None
- flush: typing.Optional[bool] = pydantic.Field(default=None)
- """
- This setting controls whether the system should flush the current buffer.
- """
-
- complete_backoff_ms: typing.Optional[float] = pydantic.Field(default=None)
- """
- The time in ms to wait after the last chunk is sent before sending the complete response. Default is 4000ms. Maximum is 10000ms.
- """
-
- language: typing.Optional[LightningV31TtsRequestMessageLanguage] = pydantic.Field(default=None)
- """
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- - Indian: `en`, `hi`, `mr`, `kn`, `ta`, `bn`, `gu`, `te`, `ml`, `pa`, `or`
- - European: `es` (Spanish)
- """
-
- sample_rate: typing.Optional[int] = pydantic.Field(default=None)
- """
- Audio sample rate in Hz. Supported values: 8000, 16000, 24000, 44100
- """
-
- speed: typing.Optional[float] = pydantic.Field(default=None)
- """
- Speaking speed multiplier
- """
-
- session_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots allowed. Max 128 characters. Echoed back in responses as `external_session_id`.
- """
-
- request_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots allowed. Max 128 characters. Echoed back in responses as `external_request_id`.
- """
-
- word_timestamps: typing.Optional[bool] = pydantic.Field(default=None)
- """
- Opt in to per-word timing events for the synthesized audio. When `true`, the server interleaves `status: "word_timestamp"` frames with the audio `chunk` frames; each carries `data: { id, word, start, end }` where `start`/`end` are floats in seconds relative to the start of the audio stream, and `word` is verbatim from the input text (un-normalized โ `"$100"` stays `"$100"`, not `"one hundred dollars"`). Supported on base-queue English + Hindi voices (`meher`, `devansh`, `kartik`, `maithili`, `liam`, `avery`); other voice families silently emit no word events (audio still works). Defaults to `false` so existing integrations see no change.
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_language.py b/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_language.py
deleted file mode 100644
index 6f50f06d..00000000
--- a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_language.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-LightningV31TtsRequestMessageLanguage = typing.Union[
- typing.Literal["en", "hi", "mr", "kn", "ta", "bn", "gu", "te", "ml", "pa", "or", "es"], typing.Any
-]
diff --git a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_model.py b/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_model.py
deleted file mode 100644
index 4a351e91..00000000
--- a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_request_message_model.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-LightningV31TtsRequestMessageModel = typing.Union[typing.Literal["lightning_v3.1", "lightning_v3.1_pro"], typing.Any]
diff --git a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message.py b/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message.py
deleted file mode 100644
index 10622efd..00000000
--- a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-from .lightning_v31tts_response_message_data import LightningV31TtsResponseMessageData
-from .lightning_v31tts_response_message_status import LightningV31TtsResponseMessageStatus
-
-
-class LightningV31TtsResponseMessage(UncheckedBaseModel):
- session_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Internal session identifier (system-generated, stable for the WebSocket connection lifetime).
- """
-
- request_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Internal request identifier (system-generated UUID, unique per TTS synthesis).
- """
-
- external_session_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Echoed client-provided session_id (omitted if not provided).
- """
-
- external_request_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Echoed client-provided request_id (omitted if not provided).
- """
-
- status: typing.Optional[LightningV31TtsResponseMessageStatus] = pydantic.Field(default=None)
- """
- Frame type discriminator:
- - `chunk` โ base64-encoded audio chunk in `data.audio`.
- - `word_timestamp` โ per-word timing event in `data.{id,word,start,end}`. Only emitted when the request set `word_timestamps: true` and the voice family supports it.
- - `complete` โ terminal frame; the server closes the WebSocket after this.
- """
-
- data: typing.Optional[LightningV31TtsResponseMessageData] = pydantic.Field(default=None)
- """
- Frame-specific payload. Shape depends on `status` โ see the per-frame examples below.
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_data.py b/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_data.py
deleted file mode 100644
index f21e7a8d..00000000
--- a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_data.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ....core.pydantic_utilities import IS_PYDANTIC_V2
-from ....core.unchecked_base_model import UncheckedBaseModel
-
-
-class LightningV31TtsResponseMessageData(UncheckedBaseModel):
- """
- Frame-specific payload. Shape depends on `status` โ see the per-frame examples below.
- """
-
- audio: typing.Optional[str] = pydantic.Field(default=None)
- """
- Base64-encoded audio chunk (present on `status: "chunk"` frames).
- """
-
- id: typing.Optional[int] = pydantic.Field(default=None)
- """
- 0-indexed position of the word within the input text (present on `status: "word_timestamp"` frames).
- """
-
- word: typing.Optional[str] = pydantic.Field(default=None)
- """
- Exact substring from the input text, un-normalized โ `"$100"` stays `"$100"`, `"25th"` stays `"25th"` (present on `status: "word_timestamp"` frames).
- """
-
- start: typing.Optional[float] = pydantic.Field(default=None)
- """
- Start of the word in seconds, relative to the start of the audio stream (present on `status: "word_timestamp"` frames).
- """
-
- end: typing.Optional[float] = pydantic.Field(default=None)
- """
- End of the word in seconds, relative to the start of the audio stream (present on `status: "word_timestamp"` frames).
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_status.py b/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_status.py
deleted file mode 100644
index e10065ae..00000000
--- a/src/smallestai/waves/lightning_v31tts/types/lightning_v31tts_response_message_status.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-LightningV31TtsResponseMessageStatus = typing.Union[typing.Literal["chunk", "word_timestamp", "complete"], typing.Any]
diff --git a/src/smallestai/waves/pulse_stt_streaming/__init__.py b/src/smallestai/waves/pulse_stt_streaming/__init__.py
index 5cde0202..218aa8fa 100644
--- a/src/smallestai/waves/pulse_stt_streaming/__init__.py
+++ b/src/smallestai/waves/pulse_stt_streaming/__init__.py
@@ -2,3 +2,69 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ PulseCloseStreamSignalMessage,
+ PulseCloseStreamSignalMessageType,
+ PulseFinalizeSignalMessage,
+ PulseFinalizeSignalMessageType,
+ PulseSpeechEndedEventMessage,
+ PulseSpeechEndedEventMessageType,
+ PulseSpeechStartedEventMessage,
+ PulseSpeechStartedEventMessageType,
+ PulseTranscriptionResponseMessage,
+ PulseTranscriptionResponseMessageUtterancesItem,
+ PulseTranscriptionResponseMessageWordsItem,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "PulseCloseStreamSignalMessage": ".types",
+ "PulseCloseStreamSignalMessageType": ".types",
+ "PulseFinalizeSignalMessage": ".types",
+ "PulseFinalizeSignalMessageType": ".types",
+ "PulseSpeechEndedEventMessage": ".types",
+ "PulseSpeechEndedEventMessageType": ".types",
+ "PulseSpeechStartedEventMessage": ".types",
+ "PulseSpeechStartedEventMessageType": ".types",
+ "PulseTranscriptionResponseMessage": ".types",
+ "PulseTranscriptionResponseMessageUtterancesItem": ".types",
+ "PulseTranscriptionResponseMessageWordsItem": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "PulseCloseStreamSignalMessage",
+ "PulseCloseStreamSignalMessageType",
+ "PulseFinalizeSignalMessage",
+ "PulseFinalizeSignalMessageType",
+ "PulseSpeechEndedEventMessage",
+ "PulseSpeechEndedEventMessageType",
+ "PulseSpeechStartedEventMessage",
+ "PulseSpeechStartedEventMessageType",
+ "PulseTranscriptionResponseMessage",
+ "PulseTranscriptionResponseMessageUtterancesItem",
+ "PulseTranscriptionResponseMessageWordsItem",
+]
diff --git a/src/smallestai/waves/pulse_stt_streaming/socket_client.py b/src/smallestai/waves/pulse_stt_streaming/socket_client.py
index 38690593..7a66c6a8 100644
--- a/src/smallestai/waves/pulse_stt_streaming/socket_client.py
+++ b/src/smallestai/waves/pulse_stt_streaming/socket_client.py
@@ -11,6 +11,8 @@
from ...core.unchecked_base_model import construct_type
from .types.pulse_close_stream_signal_message import PulseCloseStreamSignalMessage
from .types.pulse_finalize_signal_message import PulseFinalizeSignalMessage
+from .types.pulse_speech_ended_event_message import PulseSpeechEndedEventMessage
+from .types.pulse_speech_started_event_message import PulseSpeechStartedEventMessage
from .types.pulse_transcription_response_message import PulseTranscriptionResponseMessage
try:
@@ -19,7 +21,9 @@
from websockets import WebSocketClientProtocol # type: ignore
_logger = logging.getLogger(__name__)
-PulseSttStreamingSocketClientResponse = typing.Union[PulseTranscriptionResponseMessage]
+PulseSttStreamingSocketClientResponse = typing.Union[
+ PulseTranscriptionResponseMessage, PulseSpeechStartedEventMessage, PulseSpeechEndedEventMessage
+]
class AsyncPulseSttStreamingSocketClient(EventEmitterMixin):
diff --git a/src/smallestai/waves/pulse_stt_streaming/types/__init__.py b/src/smallestai/waves/pulse_stt_streaming/types/__init__.py
index 5cde0202..dbe16f17 100644
--- a/src/smallestai/waves/pulse_stt_streaming/types/__init__.py
+++ b/src/smallestai/waves/pulse_stt_streaming/types/__init__.py
@@ -2,3 +2,67 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .pulse_close_stream_signal_message import PulseCloseStreamSignalMessage
+ from .pulse_close_stream_signal_message_type import PulseCloseStreamSignalMessageType
+ from .pulse_finalize_signal_message import PulseFinalizeSignalMessage
+ from .pulse_finalize_signal_message_type import PulseFinalizeSignalMessageType
+ from .pulse_speech_ended_event_message import PulseSpeechEndedEventMessage
+ from .pulse_speech_ended_event_message_type import PulseSpeechEndedEventMessageType
+ from .pulse_speech_started_event_message import PulseSpeechStartedEventMessage
+ from .pulse_speech_started_event_message_type import PulseSpeechStartedEventMessageType
+ from .pulse_transcription_response_message import PulseTranscriptionResponseMessage
+ from .pulse_transcription_response_message_utterances_item import PulseTranscriptionResponseMessageUtterancesItem
+ from .pulse_transcription_response_message_words_item import PulseTranscriptionResponseMessageWordsItem
+_dynamic_imports: typing.Dict[str, str] = {
+ "PulseCloseStreamSignalMessage": ".pulse_close_stream_signal_message",
+ "PulseCloseStreamSignalMessageType": ".pulse_close_stream_signal_message_type",
+ "PulseFinalizeSignalMessage": ".pulse_finalize_signal_message",
+ "PulseFinalizeSignalMessageType": ".pulse_finalize_signal_message_type",
+ "PulseSpeechEndedEventMessage": ".pulse_speech_ended_event_message",
+ "PulseSpeechEndedEventMessageType": ".pulse_speech_ended_event_message_type",
+ "PulseSpeechStartedEventMessage": ".pulse_speech_started_event_message",
+ "PulseSpeechStartedEventMessageType": ".pulse_speech_started_event_message_type",
+ "PulseTranscriptionResponseMessage": ".pulse_transcription_response_message",
+ "PulseTranscriptionResponseMessageUtterancesItem": ".pulse_transcription_response_message_utterances_item",
+ "PulseTranscriptionResponseMessageWordsItem": ".pulse_transcription_response_message_words_item",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "PulseCloseStreamSignalMessage",
+ "PulseCloseStreamSignalMessageType",
+ "PulseFinalizeSignalMessage",
+ "PulseFinalizeSignalMessageType",
+ "PulseSpeechEndedEventMessage",
+ "PulseSpeechEndedEventMessageType",
+ "PulseSpeechStartedEventMessage",
+ "PulseSpeechStartedEventMessageType",
+ "PulseTranscriptionResponseMessage",
+ "PulseTranscriptionResponseMessageUtterancesItem",
+ "PulseTranscriptionResponseMessageWordsItem",
+]
diff --git a/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message.py b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message.py
new file mode 100644
index 00000000..a9baa868
--- /dev/null
+++ b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message.py
@@ -0,0 +1,34 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .pulse_speech_ended_event_message_type import PulseSpeechEndedEventMessageType
+
+
+class PulseSpeechEndedEventMessage(UncheckedBaseModel):
+ type: PulseSpeechEndedEventMessageType = pydantic.Field()
+ """
+ Discriminator. Always `speech_ended` for this event.
+ """
+
+ session_id: str = pydantic.Field()
+ """
+ Same session identifier returned on the `transcription` messages.
+ """
+
+ timestamp: float = pydantic.Field()
+ """
+ Acoustic offset of speech (end of a continuous voiced region), in seconds from the first audio frame.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message_type.py b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message_type.py
new file mode 100644
index 00000000..2b13aa8e
--- /dev/null
+++ b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_ended_event_message_type.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+PulseSpeechEndedEventMessageType = typing.Union[typing.Literal["speech_ended"], typing.Any]
diff --git a/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message.py b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message.py
new file mode 100644
index 00000000..d9b24fa9
--- /dev/null
+++ b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message.py
@@ -0,0 +1,34 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .pulse_speech_started_event_message_type import PulseSpeechStartedEventMessageType
+
+
+class PulseSpeechStartedEventMessage(UncheckedBaseModel):
+ type: PulseSpeechStartedEventMessageType = pydantic.Field()
+ """
+ Discriminator. Always `speech_started` for this event.
+ """
+
+ session_id: str = pydantic.Field()
+ """
+ Matches the `session_id` returned on `transcription` messages from the same connection. Use it to correlate VAD events with transcript turns.
+ """
+
+ timestamp: float = pydantic.Field()
+ """
+ Acoustic onset of speech, in seconds from the first audio frame sent on this connection.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message_type.py b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message_type.py
new file mode 100644
index 00000000..b594f852
--- /dev/null
+++ b/src/smallestai/waves/pulse_stt_streaming/types/pulse_speech_started_event_message_type.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+PulseSpeechStartedEventMessageType = typing.Union[typing.Literal["speech_started"], typing.Any]
diff --git a/src/smallestai/waves/speech_to_speech/__init__.py b/src/smallestai/waves/speech_to_speech/__init__.py
index 5cde0202..f713b840 100644
--- a/src/smallestai/waves/speech_to_speech/__init__.py
+++ b/src/smallestai/waves/speech_to_speech/__init__.py
@@ -2,3 +2,189 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ ConversationItemAdded,
+ ConversationItemAddedType,
+ ConversationItemCreate,
+ ConversationItemCreateType,
+ ConversationItemDone,
+ ConversationItemDoneType,
+ ErrorEvent,
+ ErrorEventError,
+ ErrorEventErrorCode,
+ ErrorEventType,
+ InputAudioBufferAppend,
+ InputAudioBufferAppendType,
+ InputAudioBufferSpeechStarted,
+ InputAudioBufferSpeechStartedType,
+ InputAudioBufferSpeechStopped,
+ InputAudioBufferSpeechStoppedType,
+ ResponseCancel,
+ ResponseCancelType,
+ ResponseCreate,
+ ResponseCreateType,
+ ResponseCreated,
+ ResponseCreatedResponse,
+ ResponseCreatedType,
+ ResponseDone,
+ ResponseDoneResponse,
+ ResponseDoneResponseStatus,
+ ResponseDoneResponseStatusDetails,
+ ResponseDoneResponseStatusDetailsError,
+ ResponseDoneResponseStatusDetailsReason,
+ ResponseDoneResponseUsage,
+ ResponseDoneType,
+ ResponseFunctionCallArgumentsDelta,
+ ResponseFunctionCallArgumentsDeltaType,
+ ResponseFunctionCallArgumentsDone,
+ ResponseFunctionCallArgumentsDoneType,
+ ResponseOutputAudioDelta,
+ ResponseOutputAudioDeltaType,
+ ResponseOutputAudioDone,
+ ResponseOutputAudioDoneType,
+ SessionConfigure,
+ SessionConfigureType,
+ SessionConfigured,
+ SessionConfiguredSession,
+ SessionConfiguredType,
+ SessionCreated,
+ SessionCreatedType,
+ SessionUpdate,
+ SessionUpdateSession,
+ SessionUpdateType,
+ SessionUpdated,
+ SessionUpdatedType,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "ConversationItemAdded": ".types",
+ "ConversationItemAddedType": ".types",
+ "ConversationItemCreate": ".types",
+ "ConversationItemCreateType": ".types",
+ "ConversationItemDone": ".types",
+ "ConversationItemDoneType": ".types",
+ "ErrorEvent": ".types",
+ "ErrorEventError": ".types",
+ "ErrorEventErrorCode": ".types",
+ "ErrorEventType": ".types",
+ "InputAudioBufferAppend": ".types",
+ "InputAudioBufferAppendType": ".types",
+ "InputAudioBufferSpeechStarted": ".types",
+ "InputAudioBufferSpeechStartedType": ".types",
+ "InputAudioBufferSpeechStopped": ".types",
+ "InputAudioBufferSpeechStoppedType": ".types",
+ "ResponseCancel": ".types",
+ "ResponseCancelType": ".types",
+ "ResponseCreate": ".types",
+ "ResponseCreateType": ".types",
+ "ResponseCreated": ".types",
+ "ResponseCreatedResponse": ".types",
+ "ResponseCreatedType": ".types",
+ "ResponseDone": ".types",
+ "ResponseDoneResponse": ".types",
+ "ResponseDoneResponseStatus": ".types",
+ "ResponseDoneResponseStatusDetails": ".types",
+ "ResponseDoneResponseStatusDetailsError": ".types",
+ "ResponseDoneResponseStatusDetailsReason": ".types",
+ "ResponseDoneResponseUsage": ".types",
+ "ResponseDoneType": ".types",
+ "ResponseFunctionCallArgumentsDelta": ".types",
+ "ResponseFunctionCallArgumentsDeltaType": ".types",
+ "ResponseFunctionCallArgumentsDone": ".types",
+ "ResponseFunctionCallArgumentsDoneType": ".types",
+ "ResponseOutputAudioDelta": ".types",
+ "ResponseOutputAudioDeltaType": ".types",
+ "ResponseOutputAudioDone": ".types",
+ "ResponseOutputAudioDoneType": ".types",
+ "SessionConfigure": ".types",
+ "SessionConfigureType": ".types",
+ "SessionConfigured": ".types",
+ "SessionConfiguredSession": ".types",
+ "SessionConfiguredType": ".types",
+ "SessionCreated": ".types",
+ "SessionCreatedType": ".types",
+ "SessionUpdate": ".types",
+ "SessionUpdateSession": ".types",
+ "SessionUpdateType": ".types",
+ "SessionUpdated": ".types",
+ "SessionUpdatedType": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ConversationItemAdded",
+ "ConversationItemAddedType",
+ "ConversationItemCreate",
+ "ConversationItemCreateType",
+ "ConversationItemDone",
+ "ConversationItemDoneType",
+ "ErrorEvent",
+ "ErrorEventError",
+ "ErrorEventErrorCode",
+ "ErrorEventType",
+ "InputAudioBufferAppend",
+ "InputAudioBufferAppendType",
+ "InputAudioBufferSpeechStarted",
+ "InputAudioBufferSpeechStartedType",
+ "InputAudioBufferSpeechStopped",
+ "InputAudioBufferSpeechStoppedType",
+ "ResponseCancel",
+ "ResponseCancelType",
+ "ResponseCreate",
+ "ResponseCreateType",
+ "ResponseCreated",
+ "ResponseCreatedResponse",
+ "ResponseCreatedType",
+ "ResponseDone",
+ "ResponseDoneResponse",
+ "ResponseDoneResponseStatus",
+ "ResponseDoneResponseStatusDetails",
+ "ResponseDoneResponseStatusDetailsError",
+ "ResponseDoneResponseStatusDetailsReason",
+ "ResponseDoneResponseUsage",
+ "ResponseDoneType",
+ "ResponseFunctionCallArgumentsDelta",
+ "ResponseFunctionCallArgumentsDeltaType",
+ "ResponseFunctionCallArgumentsDone",
+ "ResponseFunctionCallArgumentsDoneType",
+ "ResponseOutputAudioDelta",
+ "ResponseOutputAudioDeltaType",
+ "ResponseOutputAudioDone",
+ "ResponseOutputAudioDoneType",
+ "SessionConfigure",
+ "SessionConfigureType",
+ "SessionConfigured",
+ "SessionConfiguredSession",
+ "SessionConfiguredType",
+ "SessionCreated",
+ "SessionCreatedType",
+ "SessionUpdate",
+ "SessionUpdateSession",
+ "SessionUpdateType",
+ "SessionUpdated",
+ "SessionUpdatedType",
+]
diff --git a/src/smallestai/waves/speech_to_speech/client.py b/src/smallestai/waves/speech_to_speech/client.py
index 9edbe5af..d2bc3bfb 100644
--- a/src/smallestai/waves/speech_to_speech/client.py
+++ b/src/smallestai/waves/speech_to_speech/client.py
@@ -78,7 +78,7 @@ def connect(
- `1000` โ normal close (including the ~30 s server-side idle timeout).
- `1013` โ server at capacity. An `error` event with `code: "server_full"` precedes the close frame; back off with jitter and retry.
- See the [Hydra realtime guide](/waves/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
+ See the [Hydra realtime guide](/models/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
Parameters
----------
@@ -183,7 +183,7 @@ async def connect(
- `1000` โ normal close (including the ~30 s server-side idle timeout).
- `1013` โ server at capacity. An `error` event with `code: "server_full"` precedes the close frame; back off with jitter and retry.
- See the [Hydra realtime guide](/waves/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
+ See the [Hydra realtime guide](/models/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
Parameters
----------
diff --git a/src/smallestai/waves/speech_to_speech/raw_client.py b/src/smallestai/waves/speech_to_speech/raw_client.py
index 25f88af3..3b8f3758 100644
--- a/src/smallestai/waves/speech_to_speech/raw_client.py
+++ b/src/smallestai/waves/speech_to_speech/raw_client.py
@@ -66,7 +66,7 @@ def connect(
- `1000` โ normal close (including the ~30 s server-side idle timeout).
- `1013` โ server at capacity. An `error` event with `code: "server_full"` precedes the close frame; back off with jitter and retry.
- See the [Hydra realtime guide](/waves/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
+ See the [Hydra realtime guide](/models/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
Parameters
----------
@@ -160,7 +160,7 @@ async def connect(
- `1000` โ normal close (including the ~30 s server-side idle timeout).
- `1013` โ server at capacity. An `error` event with `code: "server_full"` precedes the close frame; back off with jitter and retry.
- See the [Hydra realtime guide](/waves/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
+ See the [Hydra realtime guide](/models/documentation/speech-to-speech-hydra/overview) for the full protocol reference, tool-calling pattern, interruption handling, and complete Python + browser examples.
Parameters
----------
diff --git a/src/smallestai/waves/speech_to_speech/types/__init__.py b/src/smallestai/waves/speech_to_speech/types/__init__.py
index 5cde0202..72400919 100644
--- a/src/smallestai/waves/speech_to_speech/types/__init__.py
+++ b/src/smallestai/waves/speech_to_speech/types/__init__.py
@@ -2,3 +2,187 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .conversation_item_added import ConversationItemAdded
+ from .conversation_item_added_type import ConversationItemAddedType
+ from .conversation_item_create import ConversationItemCreate
+ from .conversation_item_create_type import ConversationItemCreateType
+ from .conversation_item_done import ConversationItemDone
+ from .conversation_item_done_type import ConversationItemDoneType
+ from .error_event import ErrorEvent
+ from .error_event_error import ErrorEventError
+ from .error_event_error_code import ErrorEventErrorCode
+ from .error_event_type import ErrorEventType
+ from .input_audio_buffer_append import InputAudioBufferAppend
+ from .input_audio_buffer_append_type import InputAudioBufferAppendType
+ from .input_audio_buffer_speech_started import InputAudioBufferSpeechStarted
+ from .input_audio_buffer_speech_started_type import InputAudioBufferSpeechStartedType
+ from .input_audio_buffer_speech_stopped import InputAudioBufferSpeechStopped
+ from .input_audio_buffer_speech_stopped_type import InputAudioBufferSpeechStoppedType
+ from .response_cancel import ResponseCancel
+ from .response_cancel_type import ResponseCancelType
+ from .response_create import ResponseCreate
+ from .response_create_type import ResponseCreateType
+ from .response_created import ResponseCreated
+ from .response_created_response import ResponseCreatedResponse
+ from .response_created_type import ResponseCreatedType
+ from .response_done import ResponseDone
+ from .response_done_response import ResponseDoneResponse
+ from .response_done_response_status import ResponseDoneResponseStatus
+ from .response_done_response_status_details import ResponseDoneResponseStatusDetails
+ from .response_done_response_status_details_error import ResponseDoneResponseStatusDetailsError
+ from .response_done_response_status_details_reason import ResponseDoneResponseStatusDetailsReason
+ from .response_done_response_usage import ResponseDoneResponseUsage
+ from .response_done_type import ResponseDoneType
+ from .response_function_call_arguments_delta import ResponseFunctionCallArgumentsDelta
+ from .response_function_call_arguments_delta_type import ResponseFunctionCallArgumentsDeltaType
+ from .response_function_call_arguments_done import ResponseFunctionCallArgumentsDone
+ from .response_function_call_arguments_done_type import ResponseFunctionCallArgumentsDoneType
+ from .response_output_audio_delta import ResponseOutputAudioDelta
+ from .response_output_audio_delta_type import ResponseOutputAudioDeltaType
+ from .response_output_audio_done import ResponseOutputAudioDone
+ from .response_output_audio_done_type import ResponseOutputAudioDoneType
+ from .session_configure import SessionConfigure
+ from .session_configure_type import SessionConfigureType
+ from .session_configured import SessionConfigured
+ from .session_configured_session import SessionConfiguredSession
+ from .session_configured_type import SessionConfiguredType
+ from .session_created import SessionCreated
+ from .session_created_type import SessionCreatedType
+ from .session_update import SessionUpdate
+ from .session_update_session import SessionUpdateSession
+ from .session_update_type import SessionUpdateType
+ from .session_updated import SessionUpdated
+ from .session_updated_type import SessionUpdatedType
+_dynamic_imports: typing.Dict[str, str] = {
+ "ConversationItemAdded": ".conversation_item_added",
+ "ConversationItemAddedType": ".conversation_item_added_type",
+ "ConversationItemCreate": ".conversation_item_create",
+ "ConversationItemCreateType": ".conversation_item_create_type",
+ "ConversationItemDone": ".conversation_item_done",
+ "ConversationItemDoneType": ".conversation_item_done_type",
+ "ErrorEvent": ".error_event",
+ "ErrorEventError": ".error_event_error",
+ "ErrorEventErrorCode": ".error_event_error_code",
+ "ErrorEventType": ".error_event_type",
+ "InputAudioBufferAppend": ".input_audio_buffer_append",
+ "InputAudioBufferAppendType": ".input_audio_buffer_append_type",
+ "InputAudioBufferSpeechStarted": ".input_audio_buffer_speech_started",
+ "InputAudioBufferSpeechStartedType": ".input_audio_buffer_speech_started_type",
+ "InputAudioBufferSpeechStopped": ".input_audio_buffer_speech_stopped",
+ "InputAudioBufferSpeechStoppedType": ".input_audio_buffer_speech_stopped_type",
+ "ResponseCancel": ".response_cancel",
+ "ResponseCancelType": ".response_cancel_type",
+ "ResponseCreate": ".response_create",
+ "ResponseCreateType": ".response_create_type",
+ "ResponseCreated": ".response_created",
+ "ResponseCreatedResponse": ".response_created_response",
+ "ResponseCreatedType": ".response_created_type",
+ "ResponseDone": ".response_done",
+ "ResponseDoneResponse": ".response_done_response",
+ "ResponseDoneResponseStatus": ".response_done_response_status",
+ "ResponseDoneResponseStatusDetails": ".response_done_response_status_details",
+ "ResponseDoneResponseStatusDetailsError": ".response_done_response_status_details_error",
+ "ResponseDoneResponseStatusDetailsReason": ".response_done_response_status_details_reason",
+ "ResponseDoneResponseUsage": ".response_done_response_usage",
+ "ResponseDoneType": ".response_done_type",
+ "ResponseFunctionCallArgumentsDelta": ".response_function_call_arguments_delta",
+ "ResponseFunctionCallArgumentsDeltaType": ".response_function_call_arguments_delta_type",
+ "ResponseFunctionCallArgumentsDone": ".response_function_call_arguments_done",
+ "ResponseFunctionCallArgumentsDoneType": ".response_function_call_arguments_done_type",
+ "ResponseOutputAudioDelta": ".response_output_audio_delta",
+ "ResponseOutputAudioDeltaType": ".response_output_audio_delta_type",
+ "ResponseOutputAudioDone": ".response_output_audio_done",
+ "ResponseOutputAudioDoneType": ".response_output_audio_done_type",
+ "SessionConfigure": ".session_configure",
+ "SessionConfigureType": ".session_configure_type",
+ "SessionConfigured": ".session_configured",
+ "SessionConfiguredSession": ".session_configured_session",
+ "SessionConfiguredType": ".session_configured_type",
+ "SessionCreated": ".session_created",
+ "SessionCreatedType": ".session_created_type",
+ "SessionUpdate": ".session_update",
+ "SessionUpdateSession": ".session_update_session",
+ "SessionUpdateType": ".session_update_type",
+ "SessionUpdated": ".session_updated",
+ "SessionUpdatedType": ".session_updated_type",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "ConversationItemAdded",
+ "ConversationItemAddedType",
+ "ConversationItemCreate",
+ "ConversationItemCreateType",
+ "ConversationItemDone",
+ "ConversationItemDoneType",
+ "ErrorEvent",
+ "ErrorEventError",
+ "ErrorEventErrorCode",
+ "ErrorEventType",
+ "InputAudioBufferAppend",
+ "InputAudioBufferAppendType",
+ "InputAudioBufferSpeechStarted",
+ "InputAudioBufferSpeechStartedType",
+ "InputAudioBufferSpeechStopped",
+ "InputAudioBufferSpeechStoppedType",
+ "ResponseCancel",
+ "ResponseCancelType",
+ "ResponseCreate",
+ "ResponseCreateType",
+ "ResponseCreated",
+ "ResponseCreatedResponse",
+ "ResponseCreatedType",
+ "ResponseDone",
+ "ResponseDoneResponse",
+ "ResponseDoneResponseStatus",
+ "ResponseDoneResponseStatusDetails",
+ "ResponseDoneResponseStatusDetailsError",
+ "ResponseDoneResponseStatusDetailsReason",
+ "ResponseDoneResponseUsage",
+ "ResponseDoneType",
+ "ResponseFunctionCallArgumentsDelta",
+ "ResponseFunctionCallArgumentsDeltaType",
+ "ResponseFunctionCallArgumentsDone",
+ "ResponseFunctionCallArgumentsDoneType",
+ "ResponseOutputAudioDelta",
+ "ResponseOutputAudioDeltaType",
+ "ResponseOutputAudioDone",
+ "ResponseOutputAudioDoneType",
+ "SessionConfigure",
+ "SessionConfigureType",
+ "SessionConfigured",
+ "SessionConfiguredSession",
+ "SessionConfiguredType",
+ "SessionCreated",
+ "SessionCreatedType",
+ "SessionUpdate",
+ "SessionUpdateSession",
+ "SessionUpdateType",
+ "SessionUpdated",
+ "SessionUpdatedType",
+]
diff --git a/src/smallestai/waves/streaming_tts/__init__.py b/src/smallestai/waves/streaming_tts/__init__.py
index 5cde0202..354cbb49 100644
--- a/src/smallestai/waves/streaming_tts/__init__.py
+++ b/src/smallestai/waves/streaming_tts/__init__.py
@@ -2,3 +2,54 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ StreamTtsRequestMessage,
+ StreamTtsRequestMessageLanguage,
+ StreamTtsResponseMessage,
+ StreamTtsResponseMessageData,
+ StreamTtsResponseMessageError,
+ StreamTtsResponseMessageStatus,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "StreamTtsRequestMessage": ".types",
+ "StreamTtsRequestMessageLanguage": ".types",
+ "StreamTtsResponseMessage": ".types",
+ "StreamTtsResponseMessageData": ".types",
+ "StreamTtsResponseMessageError": ".types",
+ "StreamTtsResponseMessageStatus": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "StreamTtsRequestMessage",
+ "StreamTtsRequestMessageLanguage",
+ "StreamTtsResponseMessage",
+ "StreamTtsResponseMessageData",
+ "StreamTtsResponseMessageError",
+ "StreamTtsResponseMessageStatus",
+]
diff --git a/src/smallestai/waves/streaming_tts/types/__init__.py b/src/smallestai/waves/streaming_tts/types/__init__.py
index 5cde0202..a15ce20d 100644
--- a/src/smallestai/waves/streaming_tts/types/__init__.py
+++ b/src/smallestai/waves/streaming_tts/types/__init__.py
@@ -2,3 +2,52 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .stream_tts_request_message import StreamTtsRequestMessage
+ from .stream_tts_request_message_language import StreamTtsRequestMessageLanguage
+ from .stream_tts_response_message import StreamTtsResponseMessage
+ from .stream_tts_response_message_data import StreamTtsResponseMessageData
+ from .stream_tts_response_message_error import StreamTtsResponseMessageError
+ from .stream_tts_response_message_status import StreamTtsResponseMessageStatus
+_dynamic_imports: typing.Dict[str, str] = {
+ "StreamTtsRequestMessage": ".stream_tts_request_message",
+ "StreamTtsRequestMessageLanguage": ".stream_tts_request_message_language",
+ "StreamTtsResponseMessage": ".stream_tts_response_message",
+ "StreamTtsResponseMessageData": ".stream_tts_response_message_data",
+ "StreamTtsResponseMessageError": ".stream_tts_response_message_error",
+ "StreamTtsResponseMessageStatus": ".stream_tts_response_message_status",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "StreamTtsRequestMessage",
+ "StreamTtsRequestMessageLanguage",
+ "StreamTtsResponseMessage",
+ "StreamTtsResponseMessageData",
+ "StreamTtsResponseMessageError",
+ "StreamTtsResponseMessageStatus",
+]
diff --git a/src/smallestai/waves/tts/__init__.py b/src/smallestai/waves/tts/__init__.py
index 5cde0202..f3c16f22 100644
--- a/src/smallestai/waves/tts/__init__.py
+++ b/src/smallestai/waves/tts/__init__.py
@@ -2,3 +2,57 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ TtsRequestMessage,
+ TtsRequestMessageLanguage,
+ TtsRequestMessageModel,
+ TtsRequestMessageNumberPronunciationLanguage,
+ TtsResponseMessage,
+ TtsResponseMessageData,
+ TtsResponseMessageStatus,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "TtsRequestMessage": ".types",
+ "TtsRequestMessageLanguage": ".types",
+ "TtsRequestMessageModel": ".types",
+ "TtsRequestMessageNumberPronunciationLanguage": ".types",
+ "TtsResponseMessage": ".types",
+ "TtsResponseMessageData": ".types",
+ "TtsResponseMessageStatus": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "TtsRequestMessage",
+ "TtsRequestMessageLanguage",
+ "TtsRequestMessageModel",
+ "TtsRequestMessageNumberPronunciationLanguage",
+ "TtsResponseMessage",
+ "TtsResponseMessageData",
+ "TtsResponseMessageStatus",
+]
diff --git a/src/smallestai/waves/tts/client.py b/src/smallestai/waves/tts/client.py
index 0e7bab51..c35756d1 100644
--- a/src/smallestai/waves/tts/client.py
+++ b/src/smallestai/waves/tts/client.py
@@ -62,18 +62,29 @@ def connect(
Pass `"model": "lightning_v3.1"` (default) or
`"model": "lightning_v3.1_pro"` on each request. Concurrency and latency
are identical across both. Voice catalogs differ โ see the
- [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) and
- [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) and
+ [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
model cards for the per-model catalog.
## Language behaviour
- On `lightning_v3.1` the full 12-language catalog applies (see voice
- catalog).
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used across
+ all supported languages when `auto` is set.
- On `lightning_v3.1_pro`:
+ On `lightning_v3.1` โ 20 accepted language codes (10 European + 10
+ Indic). The trained voice catalog covers 12 of these directly; the
+ other 8 route through English or Hindi voices.
+
+ On `lightning_v3.1_pro` โ 31 languages with dedicated voices (10
+ Indic, 8 Asian & Middle Eastern, 13 European including Dutch and
+ Swedish):
- Pass `language: en` โ UK + American accented English.
- Pass `language: hi` โ Indian accented English + Hindi (code-switching).
+ - Pass the ISO 639-1 code of any other Pro language (e.g. `ta`, `de`,
+ `ja`) with a matching Pro voice. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages)
+ for the full list.
- Omit `language` โ defaults to `en + hi` (mixed Indian + Western
English coverage).
@@ -82,7 +93,7 @@ def connect(
Set `word_timestamps: true` to receive per-word timing events
interleaved with the audio chunks (`status: "word_timestamp"`).
Supported on English + Hindi base-queue voices. See
- [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
## Connection timeout
@@ -214,18 +225,29 @@ async def connect(
Pass `"model": "lightning_v3.1"` (default) or
`"model": "lightning_v3.1_pro"` on each request. Concurrency and latency
are identical across both. Voice catalogs differ โ see the
- [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) and
- [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) and
+ [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
model cards for the per-model catalog.
## Language behaviour
- On `lightning_v3.1` the full 12-language catalog applies (see voice
- catalog).
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used across
+ all supported languages when `auto` is set.
+
+ On `lightning_v3.1` โ 20 accepted language codes (10 European + 10
+ Indic). The trained voice catalog covers 12 of these directly; the
+ other 8 route through English or Hindi voices.
- On `lightning_v3.1_pro`:
+ On `lightning_v3.1_pro` โ 31 languages with dedicated voices (10
+ Indic, 8 Asian & Middle Eastern, 13 European including Dutch and
+ Swedish):
- Pass `language: en` โ UK + American accented English.
- Pass `language: hi` โ Indian accented English + Hindi (code-switching).
+ - Pass the ISO 639-1 code of any other Pro language (e.g. `ta`, `de`,
+ `ja`) with a matching Pro voice. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages)
+ for the full list.
- Omit `language` โ defaults to `en + hi` (mixed Indian + Western
English coverage).
@@ -234,7 +256,7 @@ async def connect(
Set `word_timestamps: true` to receive per-word timing events
interleaved with the audio chunks (`status: "word_timestamp"`).
Supported on English + Hindi base-queue voices. See
- [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
## Connection timeout
diff --git a/src/smallestai/waves/tts/raw_client.py b/src/smallestai/waves/tts/raw_client.py
index 21a7d615..38ca0a9a 100644
--- a/src/smallestai/waves/tts/raw_client.py
+++ b/src/smallestai/waves/tts/raw_client.py
@@ -50,18 +50,29 @@ def connect(
Pass `"model": "lightning_v3.1"` (default) or
`"model": "lightning_v3.1_pro"` on each request. Concurrency and latency
are identical across both. Voice catalogs differ โ see the
- [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) and
- [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) and
+ [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
model cards for the per-model catalog.
## Language behaviour
- On `lightning_v3.1` the full 12-language catalog applies (see voice
- catalog).
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used across
+ all supported languages when `auto` is set.
- On `lightning_v3.1_pro`:
+ On `lightning_v3.1` โ 20 accepted language codes (10 European + 10
+ Indic). The trained voice catalog covers 12 of these directly; the
+ other 8 route through English or Hindi voices.
+
+ On `lightning_v3.1_pro` โ 31 languages with dedicated voices (10
+ Indic, 8 Asian & Middle Eastern, 13 European including Dutch and
+ Swedish):
- Pass `language: en` โ UK + American accented English.
- Pass `language: hi` โ Indian accented English + Hindi (code-switching).
+ - Pass the ISO 639-1 code of any other Pro language (e.g. `ta`, `de`,
+ `ja`) with a matching Pro voice. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages)
+ for the full list.
- Omit `language` โ defaults to `en + hi` (mixed Indian + Western
English coverage).
@@ -70,7 +81,7 @@ def connect(
Set `word_timestamps: true` to receive per-word timing events
interleaved with the audio chunks (`status: "word_timestamp"`).
Supported on English + Hindi base-queue voices. See
- [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
## Connection timeout
@@ -191,18 +202,29 @@ async def connect(
Pass `"model": "lightning_v3.1"` (default) or
`"model": "lightning_v3.1_pro"` on each request. Concurrency and latency
are identical across both. Voice catalogs differ โ see the
- [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) and
- [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) and
+ [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
model cards for the per-model catalog.
## Language behaviour
- On `lightning_v3.1` the full 12-language catalog applies (see voice
- catalog).
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used across
+ all supported languages when `auto` is set.
+
+ On `lightning_v3.1` โ 20 accepted language codes (10 European + 10
+ Indic). The trained voice catalog covers 12 of these directly; the
+ other 8 route through English or Hindi voices.
- On `lightning_v3.1_pro`:
+ On `lightning_v3.1_pro` โ 31 languages with dedicated voices (10
+ Indic, 8 Asian & Middle Eastern, 13 European including Dutch and
+ Swedish):
- Pass `language: en` โ UK + American accented English.
- Pass `language: hi` โ Indian accented English + Hindi (code-switching).
+ - Pass the ISO 639-1 code of any other Pro language (e.g. `ta`, `de`,
+ `ja`) with a matching Pro voice. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages)
+ for the full list.
- Omit `language` โ defaults to `en + hi` (mixed Indian + Western
English coverage).
@@ -211,7 +233,7 @@ async def connect(
Set `word_timestamps: true` to receive per-word timing events
interleaved with the audio chunks (`status: "word_timestamp"`).
Supported on English + Hindi base-queue voices. See
- [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
## Connection timeout
diff --git a/src/smallestai/waves/tts/types/__init__.py b/src/smallestai/waves/tts/types/__init__.py
index 5cde0202..9e9ccef4 100644
--- a/src/smallestai/waves/tts/types/__init__.py
+++ b/src/smallestai/waves/tts/types/__init__.py
@@ -2,3 +2,55 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .tts_request_message import TtsRequestMessage
+ from .tts_request_message_language import TtsRequestMessageLanguage
+ from .tts_request_message_model import TtsRequestMessageModel
+ from .tts_request_message_number_pronunciation_language import TtsRequestMessageNumberPronunciationLanguage
+ from .tts_response_message import TtsResponseMessage
+ from .tts_response_message_data import TtsResponseMessageData
+ from .tts_response_message_status import TtsResponseMessageStatus
+_dynamic_imports: typing.Dict[str, str] = {
+ "TtsRequestMessage": ".tts_request_message",
+ "TtsRequestMessageLanguage": ".tts_request_message_language",
+ "TtsRequestMessageModel": ".tts_request_message_model",
+ "TtsRequestMessageNumberPronunciationLanguage": ".tts_request_message_number_pronunciation_language",
+ "TtsResponseMessage": ".tts_response_message",
+ "TtsResponseMessageData": ".tts_response_message_data",
+ "TtsResponseMessageStatus": ".tts_response_message_status",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "TtsRequestMessage",
+ "TtsRequestMessageLanguage",
+ "TtsRequestMessageModel",
+ "TtsRequestMessageNumberPronunciationLanguage",
+ "TtsResponseMessage",
+ "TtsResponseMessageData",
+ "TtsResponseMessageStatus",
+]
diff --git a/src/smallestai/waves/tts/types/tts_request_message.py b/src/smallestai/waves/tts/types/tts_request_message.py
index a7122b5b..c6315884 100644
--- a/src/smallestai/waves/tts/types/tts_request_message.py
+++ b/src/smallestai/waves/tts/types/tts_request_message.py
@@ -9,6 +9,7 @@
from ....core.unchecked_base_model import UncheckedBaseModel
from .tts_request_message_language import TtsRequestMessageLanguage
from .tts_request_message_model import TtsRequestMessageModel
+from .tts_request_message_number_pronunciation_language import TtsRequestMessageNumberPronunciationLanguage
class TtsRequestMessage(UncheckedBaseModel):
@@ -30,7 +31,7 @@ class TtsRequestMessage(UncheckedBaseModel):
- `lightning_v3.1` (default) โ standard Lightning v3.1.
- `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a
curated voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
+ [Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro).
Same concurrency and latency profile across both. Other
request fields behave identically.
@@ -69,14 +70,55 @@ class TtsRequestMessage(UncheckedBaseModel):
the voice was trained on; passing other codes is accepted by the
API but produces English-pronounced output.
- **On `lightning_v3.1`**, the full 12-language catalog applies.
+ **`auto`:** routes internally based on the input text. Any
+ English or Hindi voice can be used across all supported
+ languages when `auto` is set; the platform handles
+ language-appropriate routing without needing a code per call.
- **On `lightning_v3.1_pro`**:
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
+
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
- Pass `en` โ UK + American accented English.
- Pass `hi` โ Indian accented English + Hindi (code-switching).
- Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
"""
+ number_pronunciation_language: typing.Optional[TtsRequestMessageNumberPronunciationLanguage] = pydantic.Field(
+ default=None
+ )
+ """
+ Optional. Sets the language used to read out numeric content
+ โ numbers, currency amounts, times, and the numeric parts of
+ dates and years โ independently of the synthesis voice.
+ Ordinary words are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing
+ follow it.
+ - If you **set `language` explicitly**, `language` always wins
+ for synthesis and `number_pronunciation_language` only
+ changes how numeric content is normalized. It works both
+ ways โ read numbers in Hindi under an English voice, or in
+ English under a Hindi voice (tuned for Indian, often
+ mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ
+ normalization follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names
+ may also render in the target script (e.g. "Smith" โ
+ "เคธเฅเคฎเคฟเคฅ"), which is generally the desired reading for
+ native-language voices.
+
+ Accepts the same language codes as `language` (including
+ `auto`, `nl`, `sv`).
+ """
+
sample_rate: typing.Optional[int] = pydantic.Field(default=None)
"""
Audio sample rate in Hz. Supported values: 8000, 16000, 24000, 44100
diff --git a/src/smallestai/waves/tts/types/tts_request_message_language.py b/src/smallestai/waves/tts/types/tts_request_message_language.py
index a584222f..be6bd8f0 100644
--- a/src/smallestai/waves/tts/types/tts_request_message_language.py
+++ b/src/smallestai/waves/tts/types/tts_request_message_language.py
@@ -3,5 +3,39 @@
import typing
TtsRequestMessageLanguage = typing.Union[
- typing.Literal["en", "hi", "mr", "kn", "ta", "bn", "gu", "te", "ml", "pa", "or", "es"], typing.Any
+ typing.Literal[
+ "auto",
+ "en",
+ "hi",
+ "mr",
+ "kn",
+ "ta",
+ "bn",
+ "gu",
+ "te",
+ "ml",
+ "pa",
+ "or",
+ "es",
+ "de",
+ "fr",
+ "it",
+ "nl",
+ "sv",
+ "pt",
+ "ru",
+ "el",
+ "fi",
+ "no",
+ "pl",
+ "ar",
+ "zh",
+ "id",
+ "ja",
+ "ko",
+ "ms",
+ "tr",
+ "vi",
+ ],
+ typing.Any,
]
diff --git a/src/smallestai/waves/tts/types/tts_request_message_number_pronunciation_language.py b/src/smallestai/waves/tts/types/tts_request_message_number_pronunciation_language.py
new file mode 100644
index 00000000..0be3c4d6
--- /dev/null
+++ b/src/smallestai/waves/tts/types/tts_request_message_number_pronunciation_language.py
@@ -0,0 +1,41 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+TtsRequestMessageNumberPronunciationLanguage = typing.Union[
+ typing.Literal[
+ "auto",
+ "en",
+ "hi",
+ "mr",
+ "kn",
+ "ta",
+ "bn",
+ "gu",
+ "te",
+ "ml",
+ "pa",
+ "or",
+ "es",
+ "de",
+ "fr",
+ "it",
+ "nl",
+ "sv",
+ "pt",
+ "ru",
+ "el",
+ "fi",
+ "no",
+ "pl",
+ "ar",
+ "zh",
+ "id",
+ "ja",
+ "ko",
+ "ms",
+ "tr",
+ "vi",
+ ],
+ typing.Any,
+]
diff --git a/src/smallestai/waves/types/__init__.py b/src/smallestai/waves/types/__init__.py
index 5cde0202..390ab068 100644
--- a/src/smallestai/waves/types/__init__.py
+++ b/src/smallestai/waves/types/__init__.py
@@ -2,3 +2,240 @@
# isort: skip_file
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .audio_chunk import AudioChunk
+ from .audio_chunk_data import AudioChunkData
+ from .audio_chunk_status import AudioChunkStatus
+ from .bad_request_error_body import BadRequestErrorBody
+ from .completion_status import CompletionStatus
+ from .completion_status_status import CompletionStatusStatus
+ from .conversation_item import ConversationItem
+ from .conversation_item_content_item import ConversationItemContentItem
+ from .conversation_item_content_item_type import ConversationItemContentItemType
+ from .conversation_item_role import ConversationItemRole
+ from .conversation_item_status import ConversationItemStatus
+ from .conversation_item_type import ConversationItemType
+ from .delete_pronunciation_dict_response import DeletePronunciationDictResponse
+ from .error_response import ErrorResponse
+ from .error_response_error import ErrorResponseError
+ from .error_response_status import ErrorResponseStatus
+ from .get_voices_waves_request_model import GetVoicesWavesRequestModel
+ from .get_voices_waves_response import GetVoicesWavesResponse
+ from .get_voices_waves_response_voices_item import GetVoicesWavesResponseVoicesItem
+ from .get_voices_waves_response_voices_item_tags import GetVoicesWavesResponseVoicesItemTags
+ from .internal_server_error_body import InternalServerErrorBody
+ from .lightning_large_request import LightningLargeRequest
+ from .lightning_large_request_language import LightningLargeRequestLanguage
+ from .lightning_large_request_output_format import LightningLargeRequestOutputFormat
+ from .lightning_request import LightningRequest
+ from .lightning_request_language import LightningRequestLanguage
+ from .lightning_request_output_format import LightningRequestOutputFormat
+ from .lightningv2request import Lightningv2Request
+ from .lightningv2request_language import Lightningv2RequestLanguage
+ from .lightningv2request_output_format import Lightningv2RequestOutputFormat
+ from .pronunciation_dict import PronunciationDict
+ from .pronunciation_item import PronunciationItem
+ from .pulse_stt_auth_missing_response import PulseSttAuthMissingResponse
+ from .pulse_stt_error_response import PulseSttErrorResponse
+ from .pulse_stt_error_response_legacy import PulseSttErrorResponseLegacy
+ from .pulse_stt_error_response_status import PulseSttErrorResponseStatus
+ from .session_config import SessionConfig
+ from .session_config_voice import SessionConfigVoice
+ from .streaming_tts_config import StreamingTtsConfig
+ from .synthesize_lightning_large_waves_request_output_format import SynthesizeLightningLargeWavesRequestOutputFormat
+ from .synthesize_lightning_v2waves_request_output_format import SynthesizeLightningV2WavesRequestOutputFormat
+ from .synthesize_lightning_waves_request_output_format import SynthesizeLightningWavesRequestOutputFormat
+ from .synthesize_sse_lightning_large_waves_request_output_format import (
+ SynthesizeSseLightningLargeWavesRequestOutputFormat,
+ )
+ from .synthesize_sse_lightning_v2waves_request_output_format import SynthesizeSseLightningV2WavesRequestOutputFormat
+ from .tool import Tool
+ from .tool_type import ToolType
+ from .transcribe_pulse_waves_request_capitalize import TranscribePulseWavesRequestCapitalize
+ from .transcribe_pulse_waves_request_emotion_detection import TranscribePulseWavesRequestEmotionDetection
+ from .transcribe_pulse_waves_request_encoding import TranscribePulseWavesRequestEncoding
+ from .transcribe_pulse_waves_request_format import TranscribePulseWavesRequestFormat
+ from .transcribe_pulse_waves_request_gender_detection import TranscribePulseWavesRequestGenderDetection
+ from .transcribe_pulse_waves_request_language import TranscribePulseWavesRequestLanguage
+ from .transcribe_pulse_waves_request_punctuate import TranscribePulseWavesRequestPunctuate
+ from .transcribe_pulse_waves_response import TranscribePulseWavesResponse
+ from .transcribe_pulse_waves_response_emotions import TranscribePulseWavesResponseEmotions
+ from .transcribe_pulse_waves_response_gender import TranscribePulseWavesResponseGender
+ from .transcribe_pulse_waves_response_metadata import TranscribePulseWavesResponseMetadata
+ from .transcribe_pulse_waves_response_utterances_item import TranscribePulseWavesResponseUtterancesItem
+ from .transcribe_pulse_waves_response_words_item import TranscribePulseWavesResponseWordsItem
+ from .transcription_result import TranscriptionResult
+ from .tts_error import TtsError
+ from .tts_request import TtsRequest
+ from .tts_request_language import TtsRequestLanguage
+ from .tts_request_model import TtsRequestModel
+ from .tts_request_number_pronunciation_language import TtsRequestNumberPronunciationLanguage
+ from .tts_request_output_format import TtsRequestOutputFormat
+ from .unauthorized_error_body import UnauthorizedErrorBody
+ from .update_pronunciation_dict_response import UpdatePronunciationDictResponse
+_dynamic_imports: typing.Dict[str, str] = {
+ "AudioChunk": ".audio_chunk",
+ "AudioChunkData": ".audio_chunk_data",
+ "AudioChunkStatus": ".audio_chunk_status",
+ "BadRequestErrorBody": ".bad_request_error_body",
+ "CompletionStatus": ".completion_status",
+ "CompletionStatusStatus": ".completion_status_status",
+ "ConversationItem": ".conversation_item",
+ "ConversationItemContentItem": ".conversation_item_content_item",
+ "ConversationItemContentItemType": ".conversation_item_content_item_type",
+ "ConversationItemRole": ".conversation_item_role",
+ "ConversationItemStatus": ".conversation_item_status",
+ "ConversationItemType": ".conversation_item_type",
+ "DeletePronunciationDictResponse": ".delete_pronunciation_dict_response",
+ "ErrorResponse": ".error_response",
+ "ErrorResponseError": ".error_response_error",
+ "ErrorResponseStatus": ".error_response_status",
+ "GetVoicesWavesRequestModel": ".get_voices_waves_request_model",
+ "GetVoicesWavesResponse": ".get_voices_waves_response",
+ "GetVoicesWavesResponseVoicesItem": ".get_voices_waves_response_voices_item",
+ "GetVoicesWavesResponseVoicesItemTags": ".get_voices_waves_response_voices_item_tags",
+ "InternalServerErrorBody": ".internal_server_error_body",
+ "LightningLargeRequest": ".lightning_large_request",
+ "LightningLargeRequestLanguage": ".lightning_large_request_language",
+ "LightningLargeRequestOutputFormat": ".lightning_large_request_output_format",
+ "LightningRequest": ".lightning_request",
+ "LightningRequestLanguage": ".lightning_request_language",
+ "LightningRequestOutputFormat": ".lightning_request_output_format",
+ "Lightningv2Request": ".lightningv2request",
+ "Lightningv2RequestLanguage": ".lightningv2request_language",
+ "Lightningv2RequestOutputFormat": ".lightningv2request_output_format",
+ "PronunciationDict": ".pronunciation_dict",
+ "PronunciationItem": ".pronunciation_item",
+ "PulseSttAuthMissingResponse": ".pulse_stt_auth_missing_response",
+ "PulseSttErrorResponse": ".pulse_stt_error_response",
+ "PulseSttErrorResponseLegacy": ".pulse_stt_error_response_legacy",
+ "PulseSttErrorResponseStatus": ".pulse_stt_error_response_status",
+ "SessionConfig": ".session_config",
+ "SessionConfigVoice": ".session_config_voice",
+ "StreamingTtsConfig": ".streaming_tts_config",
+ "SynthesizeLightningLargeWavesRequestOutputFormat": ".synthesize_lightning_large_waves_request_output_format",
+ "SynthesizeLightningV2WavesRequestOutputFormat": ".synthesize_lightning_v2waves_request_output_format",
+ "SynthesizeLightningWavesRequestOutputFormat": ".synthesize_lightning_waves_request_output_format",
+ "SynthesizeSseLightningLargeWavesRequestOutputFormat": ".synthesize_sse_lightning_large_waves_request_output_format",
+ "SynthesizeSseLightningV2WavesRequestOutputFormat": ".synthesize_sse_lightning_v2waves_request_output_format",
+ "Tool": ".tool",
+ "ToolType": ".tool_type",
+ "TranscribePulseWavesRequestCapitalize": ".transcribe_pulse_waves_request_capitalize",
+ "TranscribePulseWavesRequestEmotionDetection": ".transcribe_pulse_waves_request_emotion_detection",
+ "TranscribePulseWavesRequestEncoding": ".transcribe_pulse_waves_request_encoding",
+ "TranscribePulseWavesRequestFormat": ".transcribe_pulse_waves_request_format",
+ "TranscribePulseWavesRequestGenderDetection": ".transcribe_pulse_waves_request_gender_detection",
+ "TranscribePulseWavesRequestLanguage": ".transcribe_pulse_waves_request_language",
+ "TranscribePulseWavesRequestPunctuate": ".transcribe_pulse_waves_request_punctuate",
+ "TranscribePulseWavesResponse": ".transcribe_pulse_waves_response",
+ "TranscribePulseWavesResponseEmotions": ".transcribe_pulse_waves_response_emotions",
+ "TranscribePulseWavesResponseGender": ".transcribe_pulse_waves_response_gender",
+ "TranscribePulseWavesResponseMetadata": ".transcribe_pulse_waves_response_metadata",
+ "TranscribePulseWavesResponseUtterancesItem": ".transcribe_pulse_waves_response_utterances_item",
+ "TranscribePulseWavesResponseWordsItem": ".transcribe_pulse_waves_response_words_item",
+ "TranscriptionResult": ".transcription_result",
+ "TtsError": ".tts_error",
+ "TtsRequest": ".tts_request",
+ "TtsRequestLanguage": ".tts_request_language",
+ "TtsRequestModel": ".tts_request_model",
+ "TtsRequestNumberPronunciationLanguage": ".tts_request_number_pronunciation_language",
+ "TtsRequestOutputFormat": ".tts_request_output_format",
+ "UnauthorizedErrorBody": ".unauthorized_error_body",
+ "UpdatePronunciationDictResponse": ".update_pronunciation_dict_response",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "AudioChunk",
+ "AudioChunkData",
+ "AudioChunkStatus",
+ "BadRequestErrorBody",
+ "CompletionStatus",
+ "CompletionStatusStatus",
+ "ConversationItem",
+ "ConversationItemContentItem",
+ "ConversationItemContentItemType",
+ "ConversationItemRole",
+ "ConversationItemStatus",
+ "ConversationItemType",
+ "DeletePronunciationDictResponse",
+ "ErrorResponse",
+ "ErrorResponseError",
+ "ErrorResponseStatus",
+ "GetVoicesWavesRequestModel",
+ "GetVoicesWavesResponse",
+ "GetVoicesWavesResponseVoicesItem",
+ "GetVoicesWavesResponseVoicesItemTags",
+ "InternalServerErrorBody",
+ "LightningLargeRequest",
+ "LightningLargeRequestLanguage",
+ "LightningLargeRequestOutputFormat",
+ "LightningRequest",
+ "LightningRequestLanguage",
+ "LightningRequestOutputFormat",
+ "Lightningv2Request",
+ "Lightningv2RequestLanguage",
+ "Lightningv2RequestOutputFormat",
+ "PronunciationDict",
+ "PronunciationItem",
+ "PulseSttAuthMissingResponse",
+ "PulseSttErrorResponse",
+ "PulseSttErrorResponseLegacy",
+ "PulseSttErrorResponseStatus",
+ "SessionConfig",
+ "SessionConfigVoice",
+ "StreamingTtsConfig",
+ "SynthesizeLightningLargeWavesRequestOutputFormat",
+ "SynthesizeLightningV2WavesRequestOutputFormat",
+ "SynthesizeLightningWavesRequestOutputFormat",
+ "SynthesizeSseLightningLargeWavesRequestOutputFormat",
+ "SynthesizeSseLightningV2WavesRequestOutputFormat",
+ "Tool",
+ "ToolType",
+ "TranscribePulseWavesRequestCapitalize",
+ "TranscribePulseWavesRequestEmotionDetection",
+ "TranscribePulseWavesRequestEncoding",
+ "TranscribePulseWavesRequestFormat",
+ "TranscribePulseWavesRequestGenderDetection",
+ "TranscribePulseWavesRequestLanguage",
+ "TranscribePulseWavesRequestPunctuate",
+ "TranscribePulseWavesResponse",
+ "TranscribePulseWavesResponseEmotions",
+ "TranscribePulseWavesResponseGender",
+ "TranscribePulseWavesResponseMetadata",
+ "TranscribePulseWavesResponseUtterancesItem",
+ "TranscribePulseWavesResponseWordsItem",
+ "TranscriptionResult",
+ "TtsError",
+ "TtsRequest",
+ "TtsRequestLanguage",
+ "TtsRequestModel",
+ "TtsRequestNumberPronunciationLanguage",
+ "TtsRequestOutputFormat",
+ "UnauthorizedErrorBody",
+ "UpdatePronunciationDictResponse",
+]
diff --git a/src/smallestai/waves/types/audio_chunk.py b/src/smallestai/waves/types/audio_chunk.py
index 6590b906..a58b4da9 100644
--- a/src/smallestai/waves/types/audio_chunk.py
+++ b/src/smallestai/waves/types/audio_chunk.py
@@ -6,10 +6,17 @@
from ...core.pydantic_utilities import IS_PYDANTIC_V2
from ...core.unchecked_base_model import UncheckedBaseModel
from .audio_chunk_data import AudioChunkData
+from .audio_chunk_status import AudioChunkStatus
class AudioChunk(UncheckedBaseModel):
- data: typing.Optional[AudioChunkData] = None
+ request_id: str = pydantic.Field()
+ """
+ Unique identifier for the TTS request
+ """
+
+ status: AudioChunkStatus
+ data: AudioChunkData
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/smallestai/waves/types/audio_chunk_data.py b/src/smallestai/waves/types/audio_chunk_data.py
index 0b0415d4..5a63ab72 100644
--- a/src/smallestai/waves/types/audio_chunk_data.py
+++ b/src/smallestai/waves/types/audio_chunk_data.py
@@ -8,9 +8,19 @@
class AudioChunkData(UncheckedBaseModel):
- audio: typing.Optional[str] = pydantic.Field(default=None)
+ audio: str = pydantic.Field()
"""
- Base64-encoded audio chunk
+ Base64-encoded PCM audio chunk
+ """
+
+ format: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Audio format (PCM 16-bit mono)
+ """
+
+ sample_rate: typing.Optional[int] = pydantic.Field(default=None)
+ """
+ Audio sample rate in Hz
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/waves/types/audio_chunk_status.py b/src/smallestai/waves/types/audio_chunk_status.py
new file mode 100644
index 00000000..c5abefc5
--- /dev/null
+++ b/src/smallestai/waves/types/audio_chunk_status.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+AudioChunkStatus = typing.Union[typing.Literal["chunk"], typing.Any]
diff --git a/src/smallestai/waves/types/bad_request_error_body.py b/src/smallestai/waves/types/bad_request_error_body.py
index 3106900e..75c34cb6 100644
--- a/src/smallestai/waves/types/bad_request_error_body.py
+++ b/src/smallestai/waves/types/bad_request_error_body.py
@@ -10,12 +10,12 @@
class BadRequestErrorBody(UncheckedBaseModel):
error: typing.Optional[str] = pydantic.Field(default=None)
"""
- Error type.
+ Error type
"""
message: typing.Optional[str] = pydantic.Field(default=None)
"""
- Error message.
+ Error message
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/waves/types/completion_status.py b/src/smallestai/waves/types/completion_status.py
index f5e8aafe..abf0cf8a 100644
--- a/src/smallestai/waves/types/completion_status.py
+++ b/src/smallestai/waves/types/completion_status.py
@@ -9,29 +9,14 @@
class CompletionStatus(UncheckedBaseModel):
- session_id: typing.Optional[str] = pydantic.Field(default=None)
+ request_id: str = pydantic.Field()
"""
- Internal session identifier (system-generated).
+ Unique identifier for the TTS request
"""
- request_id: typing.Optional[str] = pydantic.Field(default=None)
+ status: CompletionStatusStatus = pydantic.Field()
"""
- Internal request identifier (system-generated).
- """
-
- external_session_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Echoed client-provided session_id.
- """
-
- external_request_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Echoed client-provided request_id.
- """
-
- status: typing.Optional[CompletionStatusStatus] = pydantic.Field(default=None)
- """
- Indicates that the streaming is complete
+ Indicates that the streaming synthesis is complete
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/waves/types/get_cloned_voices_waves_response_voices_item.py b/src/smallestai/waves/types/get_cloned_voices_waves_response_voices_item.py
deleted file mode 100644
index 8e312785..00000000
--- a/src/smallestai/waves/types/get_cloned_voices_waves_response_voices_item.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import datetime as dt
-import typing
-
-import pydantic
-import typing_extensions
-from ...core.pydantic_utilities import IS_PYDANTIC_V2
-from ...core.serialization import FieldMetadata
-from ...core.unchecked_base_model import UncheckedBaseModel
-
-
-class GetClonedVoicesWavesResponseVoicesItem(UncheckedBaseModel):
- display_name: typing_extensions.Annotated[
- str,
- FieldMetadata(alias="displayName"),
- pydantic.Field(alias="displayName", description="Display name for the voice."),
- ]
- accent: typing.Optional[str] = pydantic.Field(default=None)
- """
- Accent of the voice.
- """
-
- tags: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
- """
- List of tags associated with the voice.
- """
-
- voice_id: typing_extensions.Annotated[
- str, FieldMetadata(alias="voiceId"), pydantic.Field(alias="voiceId", description="Unique Voice ID.")
- ]
- model: typing.Optional[str] = pydantic.Field(default=None)
- """
- Model used to generate the voice.
- """
-
- status: typing.Optional[str] = pydantic.Field(default=None)
- """
- Status of the voice generation.
- """
-
- created_at: typing_extensions.Annotated[
- typing.Optional[dt.datetime],
- FieldMetadata(alias="createdAt"),
- pydantic.Field(alias="createdAt", description="Date and time the voice was created."),
- ] = None
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/types/get_voices_waves_response_voices_item.py b/src/smallestai/waves/types/get_voices_waves_response_voices_item.py
index 10cd3fff..927de035 100644
--- a/src/smallestai/waves/types/get_voices_waves_response_voices_item.py
+++ b/src/smallestai/waves/types/get_voices_waves_response_voices_item.py
@@ -12,7 +12,12 @@
class GetVoicesWavesResponseVoicesItem(UncheckedBaseModel):
voice_id: typing_extensions.Annotated[
- str, FieldMetadata(alias="voiceId"), pydantic.Field(alias="voiceId", description="Unique Voice ID.")
+ str,
+ FieldMetadata(alias="voiceId"),
+ pydantic.Field(
+ alias="voiceId",
+ description="Unique Voice ID. Pass this value as `voice_id` on `POST /waves/v1/tts` (or the streaming route).",
+ ),
]
display_name: typing_extensions.Annotated[
str,
@@ -21,7 +26,7 @@ class GetVoicesWavesResponseVoicesItem(UncheckedBaseModel):
]
tags: typing.Optional[GetVoicesWavesResponseVoicesItemTags] = pydantic.Field(default=None)
"""
- Tag metadata used to identify the voice's characteristics. Filter on these fields to find voices for a target language, accent, or use case.
+ Tag metadata used to identify the voice's characteristics. Filter on these fields to find voices for a target language, accent, or use case. Fields may be empty on some voices.
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/waves/types/get_voices_waves_response_voices_item_tags.py b/src/smallestai/waves/types/get_voices_waves_response_voices_item_tags.py
index ca02d48e..0e79b33b 100644
--- a/src/smallestai/waves/types/get_voices_waves_response_voices_item_tags.py
+++ b/src/smallestai/waves/types/get_voices_waves_response_voices_item_tags.py
@@ -9,7 +9,7 @@
class GetVoicesWavesResponseVoicesItemTags(UncheckedBaseModel):
"""
- Tag metadata used to identify the voice's characteristics. Filter on these fields to find voices for a target language, accent, or use case.
+ Tag metadata used to identify the voice's characteristics. Filter on these fields to find voices for a target language, accent, or use case. Fields may be empty on some voices.
"""
language: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
@@ -29,12 +29,12 @@ class GetVoicesWavesResponseVoicesItemTags(UncheckedBaseModel):
age: typing.Optional[str] = pydantic.Field(default=None)
"""
- Age range of the voice (e.g., `young`, `middle-aged`, `senior`).
+ Age range of the voice (e.g., `young`, `middle aged`, `senior`).
"""
emotions: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
"""
- Emotional ranges the voice supports.
+ Emotional ranges the voice supports. May be empty.
"""
usecases: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
diff --git a/src/smallestai/waves/types/internal_server_error_body.py b/src/smallestai/waves/types/internal_server_error_body.py
index 7bbaed61..a7c82eb7 100644
--- a/src/smallestai/waves/types/internal_server_error_body.py
+++ b/src/smallestai/waves/types/internal_server_error_body.py
@@ -10,12 +10,12 @@
class InternalServerErrorBody(UncheckedBaseModel):
error: typing.Optional[str] = pydantic.Field(default=None)
"""
- Error type.
+ Error type
"""
message: typing.Optional[str] = pydantic.Field(default=None)
"""
- Error message.
+ Error message
"""
if IS_PYDANTIC_V2:
diff --git a/src/smallestai/waves/types/lightning_request.py b/src/smallestai/waves/types/lightning_request.py
new file mode 100644
index 00000000..9de825b3
--- /dev/null
+++ b/src/smallestai/waves/types/lightning_request.py
@@ -0,0 +1,50 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ...core.pydantic_utilities import IS_PYDANTIC_V2
+from ...core.unchecked_base_model import UncheckedBaseModel
+from .lightning_request_language import LightningRequestLanguage
+from .lightning_request_output_format import LightningRequestOutputFormat
+
+
+class LightningRequest(UncheckedBaseModel):
+ text: str = pydantic.Field()
+ """
+ The text to convert to speech.
+ """
+
+ voice_id: str = pydantic.Field()
+ """
+ The voice identifier to use for speech generation.
+ """
+
+ sample_rate: typing.Optional[int] = pydantic.Field(default=None)
+ """
+ The sample rate for the generated audio.
+ """
+
+ speed: typing.Optional[float] = pydantic.Field(default=None)
+ """
+ The speed of the generated speech.
+ """
+
+ language: typing.Optional[LightningRequestLanguage] = pydantic.Field(default=None)
+ """
+ Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
+ """
+
+ output_format: typing.Optional[LightningRequestOutputFormat] = pydantic.Field(default=None)
+ """
+ The format of the output audio.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/types/lightning_v31request.py b/src/smallestai/waves/types/lightning_v31request.py
deleted file mode 100644
index 2553fd19..00000000
--- a/src/smallestai/waves/types/lightning_v31request.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-import pydantic
-from ...core.pydantic_utilities import IS_PYDANTIC_V2
-from ...core.unchecked_base_model import UncheckedBaseModel
-from .lightning_v31request_language import LightningV31RequestLanguage
-from .lightning_v31request_model import LightningV31RequestModel
-from .lightning_v31request_output_format import LightningV31RequestOutputFormat
-
-
-class LightningV31Request(UncheckedBaseModel):
- text: str = pydantic.Field()
- """
- The text to convert to speech.
- """
-
- voice_id: str = pydantic.Field()
- """
- The voice identifier to use for speech generation.
- """
-
- model: typing.Optional[LightningV31RequestModel] = pydantic.Field(default=None)
- """
- TTS model to route the request to.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
-
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
- """
-
- sample_rate: typing.Optional[int] = pydantic.Field(default=None)
- """
- The sample rate for the generated audio.
- """
-
- speed: typing.Optional[float] = pydantic.Field(default=None)
- """
- The speed of the generated speech.
- """
-
- language: typing.Optional[LightningV31RequestLanguage] = pydantic.Field(default=None)
- """
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
- """
-
- output_format: typing.Optional[LightningV31RequestOutputFormat] = pydantic.Field(default=None)
- """
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
- """
-
- pronunciation_dicts: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
- """
- The IDs of the pronunciation dictionaries to use for speech generation.
- """
-
- session_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
- """
-
- request_id: typing.Optional[str] = pydantic.Field(default=None)
- """
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
- """
-
- if IS_PYDANTIC_V2:
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
- else:
-
- class Config:
- frozen = True
- smart_union = True
- extra = pydantic.Extra.allow
diff --git a/src/smallestai/waves/types/lightning_v31request_language.py b/src/smallestai/waves/types/lightning_v31request_language.py
deleted file mode 100644
index 905f48f8..00000000
--- a/src/smallestai/waves/types/lightning_v31request_language.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-LightningV31RequestLanguage = typing.Union[
- typing.Literal["en", "hi", "mr", "kn", "ta", "bn", "gu", "te", "ml", "pa", "or", "es"], typing.Any
-]
diff --git a/src/smallestai/waves/types/lightning_v31request_model.py b/src/smallestai/waves/types/lightning_v31request_model.py
deleted file mode 100644
index 48051ade..00000000
--- a/src/smallestai/waves/types/lightning_v31request_model.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-LightningV31RequestModel = typing.Union[typing.Literal["lightning_v3.1", "lightning_v3.1_pro"], typing.Any]
diff --git a/src/smallestai/waves/types/lightning_v31request_output_format.py b/src/smallestai/waves/types/lightning_v31request_output_format.py
deleted file mode 100644
index c3223816..00000000
--- a/src/smallestai/waves/types/lightning_v31request_output_format.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was auto-generated by Fern from our API Definition.
-
-import typing
-
-LightningV31RequestOutputFormat = typing.Union[typing.Literal["mp3", "pcm", "wav", "ulaw", "alaw"], typing.Any]
diff --git a/src/smallestai/waves/types/synthesize_lightning_large_waves_request_output_format.py b/src/smallestai/waves/types/synthesize_lightning_large_waves_request_output_format.py
new file mode 100644
index 00000000..67ec4b19
--- /dev/null
+++ b/src/smallestai/waves/types/synthesize_lightning_large_waves_request_output_format.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SynthesizeLightningLargeWavesRequestOutputFormat = typing.Union[
+ typing.Literal["pcm", "wav", "ulaw", "alaw"], typing.Any
+]
diff --git a/src/smallestai/waves/types/synthesize_lightning_v2waves_request_output_format.py b/src/smallestai/waves/types/synthesize_lightning_v2waves_request_output_format.py
new file mode 100644
index 00000000..8e48dd8b
--- /dev/null
+++ b/src/smallestai/waves/types/synthesize_lightning_v2waves_request_output_format.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SynthesizeLightningV2WavesRequestOutputFormat = typing.Union[typing.Literal["pcm", "wav", "ulaw", "alaw"], typing.Any]
diff --git a/src/smallestai/waves/types/synthesize_lightning_waves_request_output_format.py b/src/smallestai/waves/types/synthesize_lightning_waves_request_output_format.py
new file mode 100644
index 00000000..5c04cb60
--- /dev/null
+++ b/src/smallestai/waves/types/synthesize_lightning_waves_request_output_format.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SynthesizeLightningWavesRequestOutputFormat = typing.Union[typing.Literal["pcm", "wav", "ulaw", "alaw"], typing.Any]
diff --git a/src/smallestai/waves/types/synthesize_sse_lightning_large_waves_request_output_format.py b/src/smallestai/waves/types/synthesize_sse_lightning_large_waves_request_output_format.py
new file mode 100644
index 00000000..6736e59f
--- /dev/null
+++ b/src/smallestai/waves/types/synthesize_sse_lightning_large_waves_request_output_format.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SynthesizeSseLightningLargeWavesRequestOutputFormat = typing.Union[
+ typing.Literal["pcm", "wav", "ulaw", "alaw"], typing.Any
+]
diff --git a/src/smallestai/waves/types/synthesize_sse_lightning_v2waves_request_output_format.py b/src/smallestai/waves/types/synthesize_sse_lightning_v2waves_request_output_format.py
new file mode 100644
index 00000000..68d1b8a6
--- /dev/null
+++ b/src/smallestai/waves/types/synthesize_sse_lightning_v2waves_request_output_format.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SynthesizeSseLightningV2WavesRequestOutputFormat = typing.Union[
+ typing.Literal["pcm", "wav", "ulaw", "alaw"], typing.Any
+]
diff --git a/src/smallestai/waves/types/transcribe_pulse_waves_request_language.py b/src/smallestai/waves/types/transcribe_pulse_waves_request_language.py
index 2aae3ca3..a49c533c 100644
--- a/src/smallestai/waves/types/transcribe_pulse_waves_request_language.py
+++ b/src/smallestai/waves/types/transcribe_pulse_waves_request_language.py
@@ -32,6 +32,7 @@
"ko",
"multi-eu",
"multi-asian",
+ "multi-indic",
],
typing.Any,
]
diff --git a/src/smallestai/waves/types/tts_request.py b/src/smallestai/waves/types/tts_request.py
index 797ca43e..8b7a541e 100644
--- a/src/smallestai/waves/types/tts_request.py
+++ b/src/smallestai/waves/types/tts_request.py
@@ -7,6 +7,7 @@
from ...core.unchecked_base_model import UncheckedBaseModel
from .tts_request_language import TtsRequestLanguage
from .tts_request_model import TtsRequestModel
+from .tts_request_number_pronunciation_language import TtsRequestNumberPronunciationLanguage
from .tts_request_output_format import TtsRequestOutputFormat
@@ -29,7 +30,7 @@ class TtsRequest(UncheckedBaseModel):
- `lightning_v3.1` (default) โ standard Lightning v3.1.
- `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
for supported voice IDs.
Same concurrency and latency profile across both. Other request
@@ -51,17 +52,58 @@ class TtsRequest(UncheckedBaseModel):
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
Each voice has its own `tags.language` set in the voice catalog โ
query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
the voice was trained on; passing other codes is accepted by the
API but produces English-pronounced output.
- **On `lightning_v3.1`**, the full 12-language catalog applies.
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
- **On `lightning_v3.1_pro`**:
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
- Pass `en` โ UK + American accented English.
- Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+ """
+
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = pydantic.Field(default=None)
+ """
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
"""
output_format: typing.Optional[TtsRequestOutputFormat] = pydantic.Field(default=None)
@@ -80,7 +122,7 @@ class TtsRequest(UncheckedBaseModel):
word_timestamps: typing.Optional[bool] = pydantic.Field(default=None)
"""
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
"""
session_id: typing.Optional[str] = pydantic.Field(default=None)
diff --git a/src/smallestai/waves/types/tts_request_language.py b/src/smallestai/waves/types/tts_request_language.py
index bc63e9d6..c4476ca9 100644
--- a/src/smallestai/waves/types/tts_request_language.py
+++ b/src/smallestai/waves/types/tts_request_language.py
@@ -3,5 +3,39 @@
import typing
TtsRequestLanguage = typing.Union[
- typing.Literal["en", "hi", "mr", "kn", "ta", "bn", "gu", "te", "ml", "pa", "or", "es"], typing.Any
+ typing.Literal[
+ "auto",
+ "en",
+ "hi",
+ "mr",
+ "kn",
+ "ta",
+ "bn",
+ "gu",
+ "te",
+ "ml",
+ "pa",
+ "or",
+ "es",
+ "de",
+ "fr",
+ "it",
+ "nl",
+ "sv",
+ "pt",
+ "ru",
+ "el",
+ "fi",
+ "no",
+ "pl",
+ "ar",
+ "zh",
+ "id",
+ "ja",
+ "ko",
+ "ms",
+ "tr",
+ "vi",
+ ],
+ typing.Any,
]
diff --git a/src/smallestai/waves/types/tts_request_number_pronunciation_language.py b/src/smallestai/waves/types/tts_request_number_pronunciation_language.py
new file mode 100644
index 00000000..6719283e
--- /dev/null
+++ b/src/smallestai/waves/types/tts_request_number_pronunciation_language.py
@@ -0,0 +1,41 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+TtsRequestNumberPronunciationLanguage = typing.Union[
+ typing.Literal[
+ "auto",
+ "en",
+ "hi",
+ "mr",
+ "kn",
+ "ta",
+ "bn",
+ "gu",
+ "te",
+ "ml",
+ "pa",
+ "or",
+ "es",
+ "de",
+ "fr",
+ "it",
+ "nl",
+ "sv",
+ "pt",
+ "ru",
+ "el",
+ "fi",
+ "no",
+ "pl",
+ "ar",
+ "zh",
+ "id",
+ "ja",
+ "ko",
+ "ms",
+ "tr",
+ "vi",
+ ],
+ typing.Any,
+]
diff --git a/tests/wire/test_atoms_agentVersioningBranches.py b/tests/wire/test_atoms_agentVersioningBranches.py
new file mode 100644
index 00000000..c6c17935
--- /dev/null
+++ b/tests/wire/test_atoms_agentVersioningBranches.py
@@ -0,0 +1,134 @@
+from .conftest import get_client, verify_request_count
+
+
+def test_atoms_agentVersioningBranches_list_() -> None:
+ """Test list endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.list_.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.list(
+ id="id",
+ )
+ verify_request_count(test_id, "GET", "/agent/id/branches", None, 1)
+
+
+def test_atoms_agentVersioningBranches_create_branch() -> None:
+ """Test create_branch endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.create_branch.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.create_branch(
+ id="id",
+ source_branch_id="sourceBranchId",
+ name="name",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/branches", None, 1)
+
+
+def test_atoms_agentVersioningBranches_get() -> None:
+ """Test get endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.get.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.get(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "GET", "/agent/id/branches/branchId", None, 1)
+
+
+def test_atoms_agentVersioningBranches_rename() -> None:
+ """Test rename endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.rename.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.rename(
+ id="id",
+ branch_id="branchId",
+ name="name",
+ )
+ verify_request_count(test_id, "PATCH", "/agent/id/branches/branchId", None, 1)
+
+
+def test_atoms_agentVersioningBranches_archive() -> None:
+ """Test archive endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.archive.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.archive(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/branches/branchId/archive", None, 1)
+
+
+def test_atoms_agentVersioningBranches_make_live() -> None:
+ """Test make_live endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.make_live.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.make_live(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/branches/branchId/live", None, 1)
+
+
+def test_atoms_agentVersioningBranches_get_draft() -> None:
+ """Test get_draft endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.get_draft.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.get_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "GET", "/agent/id/branches/branchId/draft", None, 1)
+
+
+def test_atoms_agentVersioningBranches_update_draft() -> None:
+ """Test update_draft endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.update_draft.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.update_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "PUT", "/agent/id/branches/branchId/draft", None, 1)
+
+
+def test_atoms_agentVersioningBranches_discard_draft() -> None:
+ """Test discard_draft endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.discard_draft.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.discard_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "DELETE", "/agent/id/branches/branchId/draft", None, 1)
+
+
+def test_atoms_agentVersioningBranches_publish_draft() -> None:
+ """Test publish_draft endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.publish_draft.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.publish_draft(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/branches/branchId/draft/publish", None, 1)
+
+
+def test_atoms_agentVersioningBranches_cancel_publish() -> None:
+ """Test cancel_publish endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.cancel_publish.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.cancel_publish(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/branches/branchId/draft/publish/cancel", None, 1)
+
+
+def test_atoms_agentVersioningBranches_test_call() -> None:
+ """Test test_call endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_branches.test_call.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_branches.test_call(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/branches/branchId/test-call", None, 1)
diff --git a/tests/wire/test_atoms_agentVersioningDrafts.py b/tests/wire/test_atoms_agentVersioningDrafts.py
index c5f9eba4..73c04f83 100644
--- a/tests/wire/test_atoms_agentVersioningDrafts.py
+++ b/tests/wire/test_atoms_agentVersioningDrafts.py
@@ -6,9 +6,9 @@ def test_atoms_agentVersioningDrafts_list_active_drafts() -> None:
test_id = "atoms.agent_versioning_drafts.list_active_drafts.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.list_active_drafts(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
- verify_request_count(test_id, "GET", "/agent/60d0fe4f5311236168a109ca/drafts", None, 1)
+ verify_request_count(test_id, "GET", "/agent/id/drafts", None, 1)
def test_atoms_agentVersioningDrafts_create_draft() -> None:
@@ -16,9 +16,9 @@ def test_atoms_agentVersioningDrafts_create_draft() -> None:
test_id = "atoms.agent_versioning_drafts.create_draft.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.create_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
- verify_request_count(test_id, "POST", "/agent/60d0fe4f5311236168a109ca/drafts", None, 1)
+ verify_request_count(test_id, "POST", "/agent/id/drafts", None, 1)
def test_atoms_agentVersioningDrafts_get_draft_detail() -> None:
@@ -26,10 +26,10 @@ def test_atoms_agentVersioningDrafts_get_draft_detail() -> None:
test_id = "atoms.agent_versioning_drafts.get_draft_detail.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.get_draft_detail(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
- verify_request_count(test_id, "GET", "/agent/60d0fe4f5311236168a109ca/drafts/draftId", None, 1)
+ verify_request_count(test_id, "GET", "/agent/id/drafts/draftId", None, 1)
def test_atoms_agentVersioningDrafts_discard_draft() -> None:
@@ -37,10 +37,10 @@ def test_atoms_agentVersioningDrafts_discard_draft() -> None:
test_id = "atoms.agent_versioning_drafts.discard_draft.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.discard_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
- verify_request_count(test_id, "DELETE", "/agent/60d0fe4f5311236168a109ca/drafts/draftId", None, 1)
+ verify_request_count(test_id, "DELETE", "/agent/id/drafts/draftId", None, 1)
def test_atoms_agentVersioningDrafts_rename_draft() -> None:
@@ -48,11 +48,11 @@ def test_atoms_agentVersioningDrafts_rename_draft() -> None:
test_id = "atoms.agent_versioning_drafts.rename_draft.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.rename_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
draft_name="draftName",
)
- verify_request_count(test_id, "PATCH", "/agent/60d0fe4f5311236168a109ca/drafts/draftId", None, 1)
+ verify_request_count(test_id, "PATCH", "/agent/id/drafts/draftId", None, 1)
def test_atoms_agentVersioningDrafts_get_draft_diff() -> None:
@@ -60,10 +60,10 @@ def test_atoms_agentVersioningDrafts_get_draft_diff() -> None:
test_id = "atoms.agent_versioning_drafts.get_draft_diff.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.get_draft_diff(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
- verify_request_count(test_id, "GET", "/agent/60d0fe4f5311236168a109ca/drafts/draftId/diff", None, 1)
+ verify_request_count(test_id, "GET", "/agent/id/drafts/draftId/diff", None, 1)
def test_atoms_agentVersioningDrafts_publish_draft() -> None:
@@ -71,10 +71,10 @@ def test_atoms_agentVersioningDrafts_publish_draft() -> None:
test_id = "atoms.agent_versioning_drafts.publish_draft.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.publish_draft(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
- verify_request_count(test_id, "POST", "/agent/60d0fe4f5311236168a109ca/drafts/draftId/publish", None, 1)
+ verify_request_count(test_id, "POST", "/agent/id/drafts/draftId/publish", None, 1)
def test_atoms_agentVersioningDrafts_test_call_with_draft_config() -> None:
@@ -82,10 +82,10 @@ def test_atoms_agentVersioningDrafts_test_call_with_draft_config() -> None:
test_id = "atoms.agent_versioning_drafts.test_call_with_draft_config.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.test_call_with_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
- verify_request_count(test_id, "POST", "/agent/60d0fe4f5311236168a109ca/drafts/draftId/test-call", None, 1)
+ verify_request_count(test_id, "POST", "/agent/id/drafts/draftId/test-call", None, 1)
def test_atoms_agentVersioningDrafts_update_draft_config() -> None:
@@ -93,7 +93,7 @@ def test_atoms_agentVersioningDrafts_update_draft_config() -> None:
test_id = "atoms.agent_versioning_drafts.update_draft_config.0"
client = get_client(test_id)
client.atoms.agent_versioning_drafts.update_draft_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
draft_id="draftId",
)
- verify_request_count(test_id, "PATCH", "/agent/60d0fe4f5311236168a109ca/drafts/draftId/config", None, 1)
+ verify_request_count(test_id, "PATCH", "/agent/id/drafts/draftId/config", None, 1)
diff --git a/tests/wire/test_atoms_agentVersioningRevisions.py b/tests/wire/test_atoms_agentVersioningRevisions.py
new file mode 100644
index 00000000..d68bf998
--- /dev/null
+++ b/tests/wire/test_atoms_agentVersioningRevisions.py
@@ -0,0 +1,60 @@
+from .conftest import get_client, verify_request_count
+
+
+def test_atoms_agentVersioningRevisions_list_() -> None:
+ """Test list endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_revisions.list_.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_revisions.list(
+ id="id",
+ branch_id="branchId",
+ )
+ verify_request_count(test_id, "GET", "/agent/id/branches/branchId/revisions", None, 1)
+
+
+def test_atoms_agentVersioningRevisions_get() -> None:
+ """Test get endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_revisions.get.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_revisions.get(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+ verify_request_count(test_id, "GET", "/agent/id/branches/branchId/revisions/revisionId", None, 1)
+
+
+def test_atoms_agentVersioningRevisions_get_history() -> None:
+ """Test get_history endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_revisions.get_history.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_revisions.get_history(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+ verify_request_count(test_id, "GET", "/agent/id/branches/branchId/revisions/revisionId/history", None, 1)
+
+
+def test_atoms_agentVersioningRevisions_restore() -> None:
+ """Test restore endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_revisions.restore.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_revisions.restore(
+ id="id",
+ branch_id="branchId",
+ revision_id="revisionId",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/branches/branchId/revisions/revisionId/restore", None, 1)
+
+
+def test_atoms_agentVersioningRevisions_diff() -> None:
+ """Test diff endpoint with WireMock"""
+ test_id = "atoms.agent_versioning_revisions.diff.0"
+ client = get_client(test_id)
+ client.atoms.agent_versioning_revisions.diff(
+ id="id",
+ a="a",
+ b="b",
+ )
+ verify_request_count(test_id, "GET", "/agent/id/diff", {"a": "a", "b": "b"}, 1)
diff --git a/tests/wire/test_atoms_agentVersioningVersions.py b/tests/wire/test_atoms_agentVersioningVersions.py
index 855162f6..e0f17b83 100644
--- a/tests/wire/test_atoms_agentVersioningVersions.py
+++ b/tests/wire/test_atoms_agentVersioningVersions.py
@@ -1,5 +1,3 @@
-import datetime
-
from .conftest import get_client, verify_request_count
@@ -8,9 +6,9 @@ def test_atoms_agentVersioningVersions_list_published_versions() -> None:
test_id = "atoms.agent_versioning_versions.list_published_versions.0"
client = get_client(test_id)
client.atoms.agent_versioning_versions.list_published_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
)
- verify_request_count(test_id, "GET", "/agent/60d0fe4f5311236168a109ca/versions", None, 1)
+ verify_request_count(test_id, "GET", "/agent/id/versions", None, 1)
def test_atoms_agentVersioningVersions_diff_two_versions() -> None:
@@ -18,37 +16,11 @@ def test_atoms_agentVersioningVersions_diff_two_versions() -> None:
test_id = "atoms.agent_versioning_versions.diff_two_versions.0"
client = get_client(test_id)
client.atoms.agent_versioning_versions.diff_two_versions(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_a="versionA",
version_b="versionB",
)
- verify_request_count(
- test_id,
- "GET",
- "/agent/60d0fe4f5311236168a109ca/versions/diff",
- {"versionA": "versionA", "versionB": "versionB"},
- 1,
- )
-
-
-def test_atoms_agentVersioningVersions_compare_version_metrics() -> None:
- """Test compare_version_metrics endpoint with WireMock"""
- test_id = "atoms.agent_versioning_versions.compare_version_metrics.0"
- client = get_client(test_id)
- client.atoms.agent_versioning_versions.compare_version_metrics(
- id="60d0fe4f5311236168a109ca",
- version_a="versionA",
- version_b="versionB",
- date_from=datetime.date.fromisoformat("2026-05-01"),
- date_to=datetime.date.fromisoformat("2026-05-31"),
- )
- verify_request_count(
- test_id,
- "GET",
- "/agent/60d0fe4f5311236168a109ca/versions/compare-metrics",
- {"versionA": "versionA", "versionB": "versionB", "dateFrom": "2026-05-01", "dateTo": "2026-05-31"},
- 1,
- )
+ verify_request_count(test_id, "GET", "/agent/id/versions/diff", {"versionA": "versionA", "versionB": "versionB"}, 1)
def test_atoms_agentVersioningVersions_get_version_detail() -> None:
@@ -56,10 +28,10 @@ def test_atoms_agentVersioningVersions_get_version_detail() -> None:
test_id = "atoms.agent_versioning_versions.get_version_detail.0"
client = get_client(test_id)
client.atoms.agent_versioning_versions.get_version_detail(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
- verify_request_count(test_id, "GET", "/agent/60d0fe4f5311236168a109ca/versions/versionId", None, 1)
+ verify_request_count(test_id, "GET", "/agent/id/versions/versionId", None, 1)
def test_atoms_agentVersioningVersions_update_version_metadata() -> None:
@@ -67,10 +39,10 @@ def test_atoms_agentVersioningVersions_update_version_metadata() -> None:
test_id = "atoms.agent_versioning_versions.update_version_metadata.0"
client = get_client(test_id)
client.atoms.agent_versioning_versions.update_version_metadata(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
- verify_request_count(test_id, "PATCH", "/agent/60d0fe4f5311236168a109ca/versions/versionId", None, 1)
+ verify_request_count(test_id, "PATCH", "/agent/id/versions/versionId", None, 1)
def test_atoms_agentVersioningVersions_activate_version() -> None:
@@ -78,10 +50,10 @@ def test_atoms_agentVersioningVersions_activate_version() -> None:
test_id = "atoms.agent_versioning_versions.activate_version.0"
client = get_client(test_id)
client.atoms.agent_versioning_versions.activate_version(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
- verify_request_count(test_id, "PATCH", "/agent/60d0fe4f5311236168a109ca/versions/versionId/activate", None, 1)
+ verify_request_count(test_id, "PATCH", "/agent/id/versions/versionId/activate", None, 1)
def test_atoms_agentVersioningVersions_test_call_with_version_config() -> None:
@@ -89,7 +61,7 @@ def test_atoms_agentVersioningVersions_test_call_with_version_config() -> None:
test_id = "atoms.agent_versioning_versions.test_call_with_version_config.0"
client = get_client(test_id)
client.atoms.agent_versioning_versions.test_call_with_version_config(
- id="60d0fe4f5311236168a109ca",
+ id="id",
version_id="versionId",
)
- verify_request_count(test_id, "POST", "/agent/60d0fe4f5311236168a109ca/versions/versionId/test-call", None, 1)
+ verify_request_count(test_id, "POST", "/agent/id/versions/versionId/test-call", None, 1)
diff --git a/tests/wire/test_atoms_agents.py b/tests/wire/test_atoms_agents.py
index b431db63..bb6c1b0e 100644
--- a/tests/wire/test_atoms_agents.py
+++ b/tests/wire/test_atoms_agents.py
@@ -1,7 +1,5 @@
from .conftest import get_client, verify_request_count
-from smallestai.atoms import WidgetConfig
-
def test_atoms_agents_list_agents() -> None:
"""Test list_agents endpoint with WireMock"""
@@ -21,6 +19,17 @@ def test_atoms_agents_create_agent() -> None:
verify_request_count(test_id, "POST", "/agent", None, 1)
+def test_atoms_agents_duplicate_agent() -> None:
+ """Test duplicate_agent endpoint with WireMock"""
+ test_id = "atoms.agents.duplicate_agent.0"
+ client = get_client(test_id)
+ client.atoms.agents.duplicate_agent(
+ id="id",
+ target_organization_id="60d0fe4f5311236168a109ca",
+ )
+ verify_request_count(test_id, "POST", "/agent/id/duplicate", None, 1)
+
+
def test_atoms_agents_get_agent() -> None:
"""Test get_agent endpoint with WireMock"""
test_id = "atoms.agents.get_agent.0"
@@ -41,90 +50,54 @@ def test_atoms_agents_update_agent() -> None:
verify_request_count(test_id, "PATCH", "/agent/id", None, 1)
-def test_atoms_agents_archive_agent() -> None:
- """Test archive_agent endpoint with WireMock"""
- test_id = "atoms.agents.archive_agent.0"
+def test_atoms_agents_get_agent_widget_config() -> None:
+ """Test getAgentWidgetConfig endpoint with WireMock"""
+ test_id = "atoms.agents.get_agent_widget_config.0"
client = get_client(test_id)
- client.atoms.agents.archive_agent(
+ client.atoms.agents.get_agent_widget_config(
id="id",
)
- verify_request_count(test_id, "DELETE", "/agent/id/archive", None, 1)
+ verify_request_count(test_id, "GET", "/agent/id/widget-config", None, 1)
-def test_atoms_agents_duplicate_agent() -> None:
- """Test duplicate_agent endpoint with WireMock"""
- test_id = "atoms.agents.duplicate_agent.0"
+def test_atoms_agents_update_agent_widget_config() -> None:
+ """Test updateAgentWidgetConfig endpoint with WireMock"""
+ test_id = "atoms.agents.update_agent_widget_config.0"
client = get_client(test_id)
- client.atoms.agents.duplicate_agent(
+ client.atoms.agents.update_agent_widget_config(
id="id",
- target_organization_id="60d0fe4f5311236168a109ca",
)
- verify_request_count(test_id, "POST", "/agent/id/duplicate", None, 1)
+ verify_request_count(test_id, "PATCH", "/agent/id/widget-config", None, 1)
-def test_atoms_agents_get_agent_workflow() -> None:
- """Test getAgentWorkflow endpoint with WireMock"""
- test_id = "atoms.agents.get_agent_workflow.0"
+def test_atoms_agents_get_agent_avatar_presigned_url() -> None:
+ """Test getAgentAvatarPresignedUrl endpoint with WireMock"""
+ test_id = "atoms.agents.get_agent_avatar_presigned_url.0"
client = get_client(test_id)
- client.atoms.agents.get_agent_workflow(
+ client.atoms.agents.get_agent_avatar_presigned_url(
id="id",
+ file_name="fileName",
+ content_type="contentType",
+ file_size=1.1,
)
- verify_request_count(test_id, "GET", "/agent/id/workflow", None, 1)
-
-
-def test_atoms_agents_update_workflow_configuration() -> None:
- """Test update_workflow_configuration endpoint with WireMock"""
- test_id = "atoms.agents.update_workflow_configuration.0"
- client = get_client(test_id)
- client.atoms.agents.update_workflow_configuration(
- id="60d0fe4f5311236168a109ca",
- type="workflow_graph",
- )
- verify_request_count(test_id, "PATCH", "/workflow/60d0fe4f5311236168a109ca", None, 1)
-
-
-def test_atoms_agents_create_with_ai() -> None:
- """Test create_with_ai endpoint with WireMock"""
- test_id = "atoms.agents.create_with_ai.0"
- client = get_client(test_id)
- client.atoms.agents.create_with_ai()
- verify_request_count(test_id, "POST", "/agent/with-ai", None, 1)
-
-
-def test_atoms_agents_list_call_logs() -> None:
- """Test list_call_logs endpoint with WireMock"""
- test_id = "atoms.agents.list_call_logs.0"
- client = get_client(test_id)
- client.atoms.agents.list_call_logs(
- id="60d0fe4f5311236168a109ca",
- )
- verify_request_count(test_id, "GET", "/agent/60d0fe4f5311236168a109ca/call-logs", None, 1)
+ verify_request_count(test_id, "POST", "/agent/id/avatar/presigned-url", None, 1)
-def test_atoms_agents_get_widget_config() -> None:
- """Test get_widget_config endpoint with WireMock"""
- test_id = "atoms.agents.get_widget_config.0"
+def test_atoms_agents_get_agent_call_logs() -> None:
+ """Test getAgentCallLogs endpoint with WireMock"""
+ test_id = "atoms.agents.get_agent_call_logs.0"
client = get_client(test_id)
- client.atoms.agents.get_widget_config(
- id="60d0fe4f5311236168a109ca",
+ client.atoms.agents.get_agent_call_logs(
+ id="id",
)
- verify_request_count(test_id, "GET", "/agent/60d0fe4f5311236168a109ca/widget-config", None, 1)
+ verify_request_count(test_id, "GET", "/agent/id/call-logs", None, 1)
-def test_atoms_agents_update_widget_config() -> None:
- """Test update_widget_config endpoint with WireMock"""
- test_id = "atoms.agents.update_widget_config.0"
+def test_atoms_agents_archive_agent() -> None:
+ """Test archive_agent endpoint with WireMock"""
+ test_id = "atoms.agents.archive_agent.0"
client = get_client(test_id)
- client.atoms.agents.update_widget_config(
- id="60d0fe4f5311236168a109ca",
- widget_config=WidgetConfig(),
+ client.atoms.agents.archive_agent(
+ id="id",
)
- verify_request_count(test_id, "PATCH", "/agent/60d0fe4f5311236168a109ca/widget-config", None, 1)
-
-
-def test_atoms_agents_get_prompt_config() -> None:
- """Test get_prompt_config endpoint with WireMock"""
- test_id = "atoms.agents.get_prompt_config.0"
- client = get_client(test_id)
- client.atoms.agents.get_prompt_config()
- verify_request_count(test_id, "GET", "/agent/prompt-config", None, 1)
+ verify_request_count(test_id, "DELETE", "/agent/id/archive", None, 1)
diff --git a/tests/wire/test_atoms_analytics.py b/tests/wire/test_atoms_analytics.py
new file mode 100644
index 00000000..7b8fff8d
--- /dev/null
+++ b/tests/wire/test_atoms_analytics.py
@@ -0,0 +1,155 @@
+import datetime
+
+from .conftest import get_client, verify_request_count
+
+
+def test_atoms_analytics_get_call_counts_log() -> None:
+ """Test getCallCountsLog endpoint with WireMock"""
+ test_id = "atoms.analytics.get_call_counts_log.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_call_counts_log()
+ verify_request_count(test_id, "GET", "/analytics/call-counts-log", None, 1)
+
+
+def test_atoms_analytics_get_call_counts_by_day() -> None:
+ """Test getCallCountsByDay endpoint with WireMock"""
+ test_id = "atoms.analytics.get_call_counts_by_day.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_call_counts_by_day()
+ verify_request_count(test_id, "GET", "/analytics/call-counts-by-day", None, 1)
+
+
+def test_atoms_analytics_get_conversation_details() -> None:
+ """Test getConversationDetails endpoint with WireMock"""
+ test_id = "atoms.analytics.get_conversation_details.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_conversation_details(
+ call_id="callId",
+ )
+ verify_request_count(test_id, "GET", "/analytics/conversation-details/callId", None, 1)
+
+
+def test_atoms_analytics_get_usage_timeseries() -> None:
+ """Test getUsageTimeseries endpoint with WireMock"""
+ test_id = "atoms.analytics.get_usage_timeseries.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_usage_timeseries()
+ verify_request_count(test_id, "GET", "/analytics/usage/timeseries", None, 1)
+
+
+def test_atoms_analytics_get_dashboard() -> None:
+ """Test getDashboard endpoint with WireMock"""
+ test_id = "atoms.analytics.get_dashboard.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_dashboard()
+ verify_request_count(test_id, "GET", "/analytics/dashboard", None, 1)
+
+
+def test_atoms_analytics_get_analytics_summary() -> None:
+ """Test getAnalyticsSummary endpoint with WireMock"""
+ test_id = "atoms.analytics.get_analytics_summary.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_analytics_summary()
+ verify_request_count(test_id, "GET", "/analytics/summary", None, 1)
+
+
+def test_atoms_analytics_get_call_volume_timeseries() -> None:
+ """Test getCallVolumeTimeseries endpoint with WireMock"""
+ test_id = "atoms.analytics.get_call_volume_timeseries.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_call_volume_timeseries()
+ verify_request_count(test_id, "GET", "/analytics/call-volume-timeseries", None, 1)
+
+
+def test_atoms_analytics_get_pickup_rate_by_number() -> None:
+ """Test getPickupRateByNumber endpoint with WireMock"""
+ test_id = "atoms.analytics.get_pickup_rate_by_number.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_pickup_rate_by_number()
+ verify_request_count(test_id, "GET", "/analytics/pickup-rate-by-number", None, 1)
+
+
+def test_atoms_analytics_get_phone_number_trends() -> None:
+ """Test getPhoneNumberTrends endpoint with WireMock"""
+ test_id = "atoms.analytics.get_phone_number_trends.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_phone_number_trends()
+ verify_request_count(test_id, "GET", "/analytics/phone-number-trends", None, 1)
+
+
+def test_atoms_analytics_get_hourly_performance() -> None:
+ """Test getHourlyPerformance endpoint with WireMock"""
+ test_id = "atoms.analytics.get_hourly_performance.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_hourly_performance()
+ verify_request_count(test_id, "GET", "/analytics/hourly-performance", None, 1)
+
+
+def test_atoms_analytics_get_call_outcomes_timeseries() -> None:
+ """Test getCallOutcomesTimeseries endpoint with WireMock"""
+ test_id = "atoms.analytics.get_call_outcomes_timeseries.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_call_outcomes_timeseries()
+ verify_request_count(test_id, "GET", "/analytics/call-outcomes-timeseries", None, 1)
+
+
+def test_atoms_analytics_get_duration_stats() -> None:
+ """Test getDurationStats endpoint with WireMock"""
+ test_id = "atoms.analytics.get_duration_stats.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_duration_stats()
+ verify_request_count(test_id, "GET", "/analytics/duration-stats", None, 1)
+
+
+def test_atoms_analytics_get_weekly_trends() -> None:
+ """Test getWeeklyTrends endpoint with WireMock"""
+ test_id = "atoms.analytics.get_weekly_trends.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_weekly_trends()
+ verify_request_count(test_id, "GET", "/analytics/weekly-trends", None, 1)
+
+
+def test_atoms_analytics_get_agent_performance() -> None:
+ """Test getAgentPerformance endpoint with WireMock"""
+ test_id = "atoms.analytics.get_agent_performance.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_agent_performance()
+ verify_request_count(test_id, "GET", "/analytics/agent-performance", None, 1)
+
+
+def test_atoms_analytics_get_analytics_concurrency() -> None:
+ """Test getAnalyticsConcurrency endpoint with WireMock"""
+ test_id = "atoms.analytics.get_analytics_concurrency.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_analytics_concurrency(
+ date=datetime.date.fromisoformat("2023-01-15"),
+ )
+ verify_request_count(test_id, "GET", "/analytics/concurrency", {"date": "2023-01-15"}, 1)
+
+
+def test_atoms_analytics_get_call_start_distribution() -> None:
+ """Test getCallStartDistribution endpoint with WireMock"""
+ test_id = "atoms.analytics.get_call_start_distribution.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_call_start_distribution(
+ date=datetime.date.fromisoformat("2023-01-15"),
+ )
+ verify_request_count(test_id, "GET", "/analytics/call-start-distribution", {"date": "2023-01-15"}, 1)
+
+
+def test_atoms_analytics_get_daily_call_summary() -> None:
+ """Test getDailyCallSummary endpoint with WireMock"""
+ test_id = "atoms.analytics.get_daily_call_summary.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_daily_call_summary(
+ date=datetime.date.fromisoformat("2023-01-15"),
+ )
+ verify_request_count(test_id, "GET", "/analytics/daily-call-summary", {"date": "2023-01-15"}, 1)
+
+
+def test_atoms_analytics_get_attempt_cohort() -> None:
+ """Test getAttemptCohort endpoint with WireMock"""
+ test_id = "atoms.analytics.get_attempt_cohort.0"
+ client = get_client(test_id)
+ client.atoms.analytics.get_attempt_cohort()
+ verify_request_count(test_id, "GET", "/analytics/attempt-cohort", None, 1)
diff --git a/tests/wire/test_atoms_callActions.py b/tests/wire/test_atoms_callActions.py
new file mode 100644
index 00000000..fce6bff3
--- /dev/null
+++ b/tests/wire/test_atoms_callActions.py
@@ -0,0 +1,56 @@
+from .conftest import get_client, verify_request_count
+
+from smallestai.atoms.call_actions import CreateCallActionRequestConfig
+
+
+def test_atoms_callActions_list_call_actions() -> None:
+ """Test listCallActions endpoint with WireMock"""
+ test_id = "atoms.call_actions.list_call_actions.0"
+ client = get_client(test_id)
+ client.atoms.call_actions.list_call_actions(
+ agent_id="agentId",
+ )
+ verify_request_count(test_id, "GET", "/call-actions", {"agentId": "agentId"}, 1)
+
+
+def test_atoms_callActions_create_call_action() -> None:
+ """Test createCallAction endpoint with WireMock"""
+ test_id = "atoms.call_actions.create_call_action.0"
+ client = get_client(test_id)
+ client.atoms.call_actions.create_call_action(
+ agent_id="agentId",
+ category="trigger",
+ provider="provider",
+ config=CreateCallActionRequestConfig(),
+ )
+ verify_request_count(test_id, "POST", "/call-actions", None, 1)
+
+
+def test_atoms_callActions_get_call_action() -> None:
+ """Test getCallAction endpoint with WireMock"""
+ test_id = "atoms.call_actions.get_call_action.0"
+ client = get_client(test_id)
+ client.atoms.call_actions.get_call_action(
+ id="id",
+ )
+ verify_request_count(test_id, "GET", "/call-actions/id", None, 1)
+
+
+def test_atoms_callActions_update_call_action() -> None:
+ """Test updateCallAction endpoint with WireMock"""
+ test_id = "atoms.call_actions.update_call_action.0"
+ client = get_client(test_id)
+ client.atoms.call_actions.update_call_action(
+ id="id",
+ )
+ verify_request_count(test_id, "PUT", "/call-actions/id", None, 1)
+
+
+def test_atoms_callActions_delete_call_action() -> None:
+ """Test deleteCallAction endpoint with WireMock"""
+ test_id = "atoms.call_actions.delete_call_action.0"
+ client = get_client(test_id)
+ client.atoms.call_actions.delete_call_action(
+ id="id",
+ )
+ verify_request_count(test_id, "DELETE", "/call-actions/id", None, 1)
diff --git a/tests/wire/test_atoms_campaigns.py b/tests/wire/test_atoms_campaigns.py
index 802c4d85..abf61846 100644
--- a/tests/wire/test_atoms_campaigns.py
+++ b/tests/wire/test_atoms_campaigns.py
@@ -59,13 +59,3 @@ def test_atoms_campaigns_pause() -> None:
id="id",
)
verify_request_count(test_id, "POST", "/campaign/id/pause", None, 1)
-
-
-def test_atoms_campaigns_export_logs() -> None:
- """Test export_logs endpoint with WireMock"""
- test_id = "atoms.campaigns.export_logs.0"
- client = get_client(test_id)
- client.atoms.campaigns.export_logs(
- id="id",
- )
- verify_request_count(test_id, "GET", "/campaign/id/logs/export", None, 1)
diff --git a/tests/wire/test_atoms_concurrency.py b/tests/wire/test_atoms_concurrency.py
new file mode 100644
index 00000000..2a00cd83
--- /dev/null
+++ b/tests/wire/test_atoms_concurrency.py
@@ -0,0 +1,29 @@
+from .conftest import get_client, verify_request_count
+
+from smallestai.atoms.concurrency import UpdateConcurrencyReservationsRequestReservationsItem
+
+
+def test_atoms_concurrency_get_concurrency() -> None:
+ """Test getConcurrency endpoint with WireMock"""
+ test_id = "atoms.concurrency.get_concurrency.0"
+ client = get_client(test_id)
+ client.atoms.concurrency.get_concurrency()
+ verify_request_count(test_id, "GET", "/concurrency", None, 1)
+
+
+def test_atoms_concurrency_update_concurrency_reservations() -> None:
+ """Test updateConcurrencyReservations endpoint with WireMock"""
+ test_id = "atoms.concurrency.update_concurrency_reservations.0"
+ client = get_client(test_id)
+ client.atoms.concurrency.update_concurrency_reservations(
+ reservations=[
+ UpdateConcurrencyReservationsRequestReservationsItem(
+ agent_id="agentId",
+ webcall=1,
+ outbound=1,
+ inbound=1,
+ chat=1,
+ )
+ ],
+ )
+ verify_request_count(test_id, "PUT", "/concurrency/reservations", None, 1)
diff --git a/tests/wire/test_atoms_dispositionMetricTemplates.py b/tests/wire/test_atoms_dispositionMetricTemplates.py
new file mode 100644
index 00000000..21cd86a5
--- /dev/null
+++ b/tests/wire/test_atoms_dispositionMetricTemplates.py
@@ -0,0 +1,9 @@
+from .conftest import get_client, verify_request_count
+
+
+def test_atoms_dispositionMetricTemplates_list_disposition_metric_templates() -> None:
+ """Test listDispositionMetricTemplates endpoint with WireMock"""
+ test_id = "atoms.disposition_metric_templates.list_disposition_metric_templates.0"
+ client = get_client(test_id)
+ client.atoms.disposition_metric_templates.list_disposition_metric_templates()
+ verify_request_count(test_id, "GET", "/disposition-metric-templates", None, 1)
diff --git a/tests/wire/test_atoms_integrations.py b/tests/wire/test_atoms_integrations.py
new file mode 100644
index 00000000..e2dbc0a9
--- /dev/null
+++ b/tests/wire/test_atoms_integrations.py
@@ -0,0 +1,27 @@
+from .conftest import get_client, verify_request_count
+
+from smallestai.atoms import WebEngageIntegrationSet
+
+
+def test_atoms_integrations_modify_web_engage_integration() -> None:
+ """Test modifyWebEngageIntegration endpoint with WireMock"""
+ test_id = "atoms.integrations.modify_web_engage_integration.0"
+ client = get_client(test_id)
+ client.atoms.integrations.modify_web_engage_integration(
+ integration_sets=[
+ WebEngageIntegrationSet(
+ license_code="licenseCode",
+ environment="environment",
+ api_key="apiKey",
+ )
+ ],
+ )
+ verify_request_count(test_id, "POST", "/integration/modify-webengage-integration", None, 1)
+
+
+def test_atoms_integrations_get_web_engage_details() -> None:
+ """Test getWebEngageDetails endpoint with WireMock"""
+ test_id = "atoms.integrations.get_web_engage_details.0"
+ client = get_client(test_id)
+ client.atoms.integrations.get_web_engage_details()
+ verify_request_count(test_id, "GET", "/integration/get-webengage-details", None, 1)
diff --git a/tests/wire/test_atoms_organization.py b/tests/wire/test_atoms_organization.py
index c601ee01..b00e73d2 100644
--- a/tests/wire/test_atoms_organization.py
+++ b/tests/wire/test_atoms_organization.py
@@ -7,21 +7,3 @@ def test_atoms_organization_get_organization_details() -> None:
client = get_client(test_id)
client.atoms.organization.get_organization_details()
verify_request_count(test_id, "GET", "/organization", None, 1)
-
-
-def test_atoms_organization_get_account_details() -> None:
- """Test get_account_details endpoint with WireMock"""
- test_id = "atoms.organization.get_account_details.0"
- client = get_client(test_id)
- client.atoms.organization.get_account_details()
- verify_request_count(test_id, "GET", "/account/get-account-details", None, 1)
-
-
-def test_atoms_organization_update_name() -> None:
- """Test update_name endpoint with WireMock"""
- test_id = "atoms.organization.update_name.0"
- client = get_client(test_id)
- client.atoms.organization.update_name(
- name="name",
- )
- verify_request_count(test_id, "PUT", "/account/update-org-name", None, 1)
diff --git a/tests/wire/test_atoms_phoneNumbers.py b/tests/wire/test_atoms_phoneNumbers.py
index 112bf2e3..5e15c55c 100644
--- a/tests/wire/test_atoms_phoneNumbers.py
+++ b/tests/wire/test_atoms_phoneNumbers.py
@@ -81,7 +81,7 @@ def test_atoms_phoneNumbers_import_sip() -> None:
client = get_client(test_id)
client.atoms.phone_numbers.import_sip(
phone_number="+14155551234",
- sip_termination_url="sip:trunk.your-provider.com",
+ sip_termination_url="trunk.your-provider.com",
name="Main Support Line",
sip_username="",
sip_password="",
diff --git a/tests/wire/test_atoms_realtime.py b/tests/wire/test_atoms_realtime.py
new file mode 100644
index 00000000..1320a212
--- /dev/null
+++ b/tests/wire/test_atoms_realtime.py
@@ -0,0 +1,11 @@
+from .conftest import get_client, verify_request_count
+
+
+def test_atoms_realtime_register_call() -> None:
+ """Test register_call endpoint with WireMock"""
+ test_id = "atoms.realtime.register_call.0"
+ client = get_client(test_id)
+ client.atoms.realtime.register_call(
+ agent_id="69da0b4c20c0e03cfa4ee258",
+ )
+ verify_request_count(test_id, "POST", "/conversation/register-call", None, 1)
diff --git a/tests/wire/test_atoms_user.py b/tests/wire/test_atoms_user.py
index bae617e9..aa8eac3d 100644
--- a/tests/wire/test_atoms_user.py
+++ b/tests/wire/test_atoms_user.py
@@ -7,11 +7,3 @@ def test_atoms_user_get_user_details() -> None:
client = get_client(test_id)
client.atoms.user.get_user_details()
verify_request_count(test_id, "GET", "/user", None, 1)
-
-
-def test_atoms_user_get_subscription() -> None:
- """Test get_subscription endpoint with WireMock"""
- test_id = "atoms.user.get_subscription.0"
- client = get_client(test_id)
- client.atoms.user.get_subscription()
- verify_request_count(test_id, "GET", "/user/subscription", None, 1)
diff --git a/tests/wire/test_waves.py b/tests/wire/test_waves.py
index 4423fb6c..c42e1124 100644
--- a/tests/wire/test_waves.py
+++ b/tests/wire/test_waves.py
@@ -56,11 +56,7 @@ def test_waves_synthesize_lightning() -> None:
"""Test synthesize_lightning endpoint with WireMock"""
test_id = "waves.synthesize_lightning.0"
client = get_client(test_id)
- for _ in client.waves.synthesize_lightning(
- text="text",
- voice_id="voice_id",
- ):
- pass
+ client.waves.synthesize_lightning()
verify_request_count(test_id, "POST", "/waves/v1/lightning/get_speech", None, 1)
@@ -68,47 +64,31 @@ def test_waves_synthesize_lightning_large() -> None:
"""Test synthesize_lightning_large endpoint with WireMock"""
test_id = "waves.synthesize_lightning_large.0"
client = get_client(test_id)
- for _ in client.waves.synthesize_lightning_large(
- text="text",
- voice_id="voice_id",
- ):
- pass
+ client.waves.synthesize_lightning_large()
verify_request_count(test_id, "POST", "/waves/v1/lightning-large/get_speech", None, 1)
-def test_waves_synthesize_lightning_v2() -> None:
- """Test synthesize_lightning_v2 endpoint with WireMock"""
- test_id = "waves.synthesize_lightning_v2.0"
- client = get_client(test_id)
- for _ in client.waves.synthesize_lightning_v2(
- text="Hey i am your a text to speech model",
- voice_id="malcom",
- ):
- pass
- verify_request_count(test_id, "POST", "/waves/v1/lightning-v2/get_speech", None, 1)
-
-
def test_waves_synthesize_sse_lightning_large() -> None:
"""Test synthesize_sse_lightning_large endpoint with WireMock"""
test_id = "waves.synthesize_sse_lightning_large.0"
client = get_client(test_id)
- for _ in client.waves.synthesize_sse_lightning_large(
- text="text",
- voice_id="voice_id",
- ):
- pass
+ client.waves.synthesize_sse_lightning_large()
verify_request_count(test_id, "POST", "/waves/v1/lightning-large/stream", None, 1)
+def test_waves_synthesize_lightning_v2() -> None:
+ """Test synthesize_lightning_v2 endpoint with WireMock"""
+ test_id = "waves.synthesize_lightning_v2.0"
+ client = get_client(test_id)
+ client.waves.synthesize_lightning_v2()
+ verify_request_count(test_id, "POST", "/waves/v1/lightning-v2/get_speech", None, 1)
+
+
def test_waves_synthesize_sse_lightning_v2() -> None:
"""Test synthesize_sse_lightning_v2 endpoint with WireMock"""
test_id = "waves.synthesize_sse_lightning_v2.0"
client = get_client(test_id)
- for _ in client.waves.synthesize_sse_lightning_v2(
- text="text",
- voice_id="voice_id",
- ):
- pass
+ client.waves.synthesize_sse_lightning_v2()
verify_request_count(test_id, "POST", "/waves/v1/lightning-v2/stream", None, 1)
@@ -122,35 +102,6 @@ def test_waves_get_voices() -> None:
verify_request_count(test_id, "GET", "/waves/v1/lightning-v3.1/get_voices", None, 1)
-def test_waves_add_voice() -> None:
- """Test add_voice endpoint with WireMock"""
- test_id = "waves.add_voice.0"
- client = get_client(test_id)
- client.waves.add_voice(
- file="example_file",
- display_name="displayName",
- )
- verify_request_count(test_id, "POST", "/waves/v1/lightning-large/add_voice", None, 1)
-
-
-def test_waves_get_cloned_voices() -> None:
- """Test get_cloned_voices endpoint with WireMock"""
- test_id = "waves.get_cloned_voices.0"
- client = get_client(test_id)
- client.waves.get_cloned_voices()
- verify_request_count(test_id, "GET", "/waves/v1/lightning-large/get_cloned_voices", None, 1)
-
-
-def test_waves_delete_voice() -> None:
- """Test delete_voice endpoint with WireMock"""
- test_id = "waves.delete_voice.0"
- client = get_client(test_id)
- client.waves.delete_voice(
- voice_id="voiceId",
- )
- verify_request_count(test_id, "DELETE", "/waves/v1/lightning-large", None, 1)
-
-
def test_waves_synthesize_tts() -> None:
"""Test synthesize_tts endpoint with WireMock"""
test_id = "waves.synthesize_tts.0"
@@ -173,27 +124,3 @@ def test_waves_synthesize_sse_tts() -> None:
):
pass
verify_request_count(test_id, "POST", "/waves/v1/tts/live", None, 1)
-
-
-def test_waves_synthesize_lightning_v31() -> None:
- """Test synthesize_lightning_v3_1 endpoint with WireMock"""
- test_id = "waves.synthesize_lightning_v31.0"
- client = get_client(test_id)
- for _ in client.waves.synthesize_lightning_v31(
- text="Hey i am your a text to speech model",
- voice_id="daniel",
- ):
- pass
- verify_request_count(test_id, "POST", "/waves/v1/lightning-v3.1/get_speech", None, 1)
-
-
-def test_waves_synthesize_sse_lightning_v31() -> None:
- """Test synthesize_sse_lightning_v3_1 endpoint with WireMock"""
- test_id = "waves.synthesize_sse_lightning_v31.0"
- client = get_client(test_id)
- for _ in client.waves.synthesize_sse_lightning_v31(
- text="text",
- voice_id="voice_id",
- ):
- pass
- verify_request_count(test_id, "POST", "/waves/v1/lightning-v3.1/stream", None, 1)
diff --git a/wiremock/wiremock-mappings.json b/wiremock/wiremock-mappings.json
index ecf6f260..5767c74a 100644
--- a/wiremock/wiremock-mappings.json
+++ b/wiremock/wiremock-mappings.json
@@ -32,38 +32,6 @@
},
"postServeActions": []
},
- {
- "id": "518bdac0-6c3e-4e8e-be47-41ef25f575df",
- "name": "Get caller's subscription + plan limits + feature flags - default",
- "request": {
- "urlPathTemplate": "/user/subscription",
- "method": "GET",
- "headers": {
- "Authorization": {
- "matches": "Bearer .+"
- }
- }
- },
- "response": {
- "status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"planId\": \"planId\",\n \"limits\": {\n \"agents\": 1,\n \"campaigns\": 1,\n \"numbers\": 1,\n \"dailyCalls\": 1,\n \"concurrentCalls\": 1,\n \"knowledgeBaseItemLimit\": 1,\n \"knowledgeBaseLimits\": {\n \"key\": \"value\"\n }\n },\n \"features\": {\n \"chat\": true,\n \"telephony\": true,\n \"campaign\": true,\n \"webcall\": true,\n \"knowledge_base\": true,\n \"integrations\": true,\n \"webhooks\": true,\n \"telephony_inbound\": true,\n \"telephony_outbound\": true,\n \"waves_lightning\": true,\n \"waves_lightning_large\": true,\n \"waves_lightning_large_voice_clone\": true,\n \"openai_gpt_4o\": true,\n \"electron\": true,\n \"workflow_editor\": true\n }\n }\n}",
- "headers": {
- "Content-Type": "application/json"
- }
- },
- "uuid": "518bdac0-6c3e-4e8e-be47-41ef25f575df",
- "persistent": true,
- "priority": 3,
- "metadata": {
- "mocklab": {
- "created": {
- "at": "2020-01-01T00:00:00.000Z",
- "via": "SYSTEM"
- }
- }
- },
- "postServeActions": []
- },
{
"id": "3142d983-0755-42b2-a57e-8e870c673b42",
"name": "Get organization details - default",
@@ -96,69 +64,6 @@
},
"postServeActions": []
},
- {
- "id": "e45e67be-1ec4-4196-83bd-8859310299a2",
- "name": "Get authenticated user + organizations - default",
- "request": {
- "urlPathTemplate": "/account/get-account-details",
- "method": "GET",
- "headers": {
- "Authorization": {
- "matches": "Bearer .+"
- }
- }
- },
- "response": {
- "status": 200,
- "body": "{\n \"email\": \"email\",\n \"phone\": \"phone\",\n \"firstName\": \"firstName\",\n \"lastName\": \"lastName\",\n \"userId\": \"userId\",\n \"picture\": \"picture\",\n \"organizations\": [\n {\n \"orgId\": \"orgId\",\n \"roleId\": \"roleId\",\n \"name\": \"name\",\n \"ownerEmail\": \"ownerEmail\",\n \"onPremEnabled\": true\n }\n ],\n \"hasOnboarded\": true\n}",
- "headers": {
- "Content-Type": "application/json"
- }
- },
- "uuid": "e45e67be-1ec4-4196-83bd-8859310299a2",
- "persistent": true,
- "priority": 3,
- "metadata": {
- "mocklab": {
- "created": {
- "at": "2020-01-01T00:00:00.000Z",
- "via": "SYSTEM"
- }
- }
- },
- "postServeActions": []
- },
- {
- "id": "96640e02-66c6-4ea9-86ad-9c945a20c28a",
- "name": "Update organization display name - default",
- "request": {
- "urlPathTemplate": "/account/update-org-name",
- "method": "PUT",
- "headers": {
- "Authorization": {
- "matches": "Bearer .+"
- }
- }
- },
- "response": {
- "status": 200,
- "body": "{\n \"success\": true,\n \"name\": \"name\"\n}",
- "headers": {
- "Content-Type": "application/json"
- }
- },
- "uuid": "96640e02-66c6-4ea9-86ad-9c945a20c28a",
- "persistent": true,
- "priority": 3,
- "metadata": {
- "mocklab": {
- "created": {
- "at": "2020-01-01T00:00:00.000Z",
- "via": "SYSTEM"
- }
- }
- }
- },
{
"id": "1100955a-2a1f-45a0-89e1-b536b8bf9553",
"name": "Get agent templates - default",
@@ -286,11 +191,11 @@
}
},
{
- "id": "082fb16e-c097-4973-a8ca-d737cccb9340",
- "name": "Get agent by ID - default",
+ "id": "3210590b-c9d0-4ffb-bf7f-960e80930740",
+ "name": "Duplicate agent to another organization - default",
"request": {
- "urlPathTemplate": "/agent/{id}",
- "method": "GET",
+ "urlPathTemplate": "/agent/{id}/duplicate",
+ "method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
@@ -303,13 +208,13 @@
}
},
"response": {
- "status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"name\": \"name\",\n \"description\": \"description\",\n \"backgroundSound\": \"\",\n \"organization\": \"organization\",\n \"workflowId\": \"workflowId\",\n \"workflowType\": \"workflow_graph\",\n \"createdBy\": \"createdBy\",\n \"globalKnowledgeBaseId\": \"globalKnowledgeBaseId\",\n \"language\": {\n \"default\": \"en\",\n \"supported\": [\n \"supported\"\n ]\n },\n \"synthesizer\": {\n \"speed\": 1.1,\n \"consistency\": 1.1,\n \"similarity\": 1.1,\n \"enhancement\": 1.1,\n \"sampleRate\": 1.1\n },\n \"slmModel\": \"electron\",\n \"defaultVariables\": {\n \"key\": \"value\"\n },\n \"preCallAPI\": {\n \"isEnabled\": true,\n \"url\": \"url\",\n \"method\": \"GET\",\n \"headers\": {\n \"key\": \"value\"\n },\n \"body\": {\n \"key\": \"value\"\n },\n \"timeout\": 1,\n \"queryParams\": {\n \"key\": \"value\"\n },\n \"responseVariables\": [\n {\n \"variableName\": \"variableName\",\n \"jsonPath\": \"jsonPath\"\n }\n ]\n },\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\",\n \"avatarUrl\": \"avatarUrl\",\n \"firstMessage\": \"firstMessage\",\n \"allowInterruptions\": true,\n \"waitForUserToSpeakFirst\": true,\n \"totalCalls\": 1.1,\n \"transcriberType\": \"transcriberType\",\n \"globalPrompt\": \"globalPrompt\",\n \"archived\": true,\n \"archivedAt\": \"2024-01-15T09:30:00Z\",\n \"isLocked\": true,\n \"activeVersionId\": \"activeVersionId\",\n \"versionId\": \"versionId\",\n \"allowInboundCall\": true,\n \"phoneNumber\": [\n \"phoneNumber\"\n ],\n \"visibleToEveryone\": true,\n \"speechFormatting\": true,\n \"muteUserUntilFirstBotResponse\": true,\n \"interruptionBackoffTimer\": 1.1,\n \"enableStyleGuide\": true,\n \"callDispositionConfig\": \"callDispositionConfig\",\n \"voiceMailDetectionConfig\": {\n \"enabled\": true,\n \"endText\": \"endText\"\n },\n \"smartTurnConfig\": {\n \"isEnabled\": true,\n \"waitTimeInSecs\": 1.1\n },\n \"voiceDetectionConfig\": {\n \"confidence\": 1.1,\n \"minVolume\": 1.1,\n \"triggerTimeInSecs\": 1.1,\n \"releaseTimeInSecs\": 1.1\n },\n \"denoisingConfig\": {\n \"isEnabled\": true\n },\n \"redactionConfig\": {\n \"isEnabled\": true\n },\n \"pronunciationDicts\": [\n {\n \"word\": \"word\",\n \"pronunciation\": \"pronunciation\"\n }\n ],\n \"llmIdleTimeoutConfig\": {\n \"chatTimeoutTimeInSecs\": 1.1,\n \"webcallTimeoutTimeInSecs\": 1.1,\n \"telephonyTimeoutTimeInSecs\": 1.1,\n \"maxRetries\": 1.1\n },\n \"sessionTimeoutConfig\": {\n \"timeoutTimeInSecs\": 1.1\n },\n \"timezone\": {\n \"label\": \"label\",\n \"offset\": 1.1\n },\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ],\n \"useInternalAnalyticsModel\": true,\n \"useReasoningModel\": true\n },\n \"widgetConfig\": {\n \"position\": \"bottom-right\",\n \"size\": \"tiny\",\n \"mode\": \"chat\",\n \"theme\": \"light\",\n \"baseColor\": \"baseColor\",\n \"accentColor\": \"accentColor\",\n \"agentBubbleColor\": \"agentBubbleColor\",\n \"textOnAccentColor\": \"textOnAccentColor\",\n \"secondaryTextColor\": \"secondaryTextColor\",\n \"primaryTextColor\": \"primaryTextColor\",\n \"startButtonText\": \"startButtonText\",\n \"endButtonText\": \"endButtonText\",\n \"ctaName\": \"ctaName\",\n \"widgetName\": \"widgetName\",\n \"avatarUrl\": \"avatarUrl\",\n \"chatPlaceholder\": \"chatPlaceholder\",\n \"consentRequired\": true,\n \"consentTitle\": \"consentTitle\",\n \"consentContent\": \"consentContent\",\n \"assistantId\": \"assistantId\",\n \"allowlist\": [\n \"allowlist\"\n ]\n },\n \"_resolvedConfig\": {\n \"prompt\": \"prompt\",\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ],\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ]\n },\n \"callDispositionConfig\": \"callDispositionConfig\",\n \"modelName\": \"modelName\",\n \"transcriberType\": \"transcriberType\",\n \"defaultLanguage\": \"en\",\n \"supportedLanguages\": [\n \"supportedLanguages\"\n ],\n \"languageSwitching\": {\n \"key\": \"value\"\n },\n \"firstMessage\": \"firstMessage\",\n \"globalPrompt\": \"globalPrompt\",\n \"preCallAPI\": {\n \"key\": \"value\"\n },\n \"workflowGraph\": {\n \"key\": \"value\"\n },\n \"muteUserUntilFirstBotResponse\": true,\n \"allowInterruptions\": true,\n \"voiceDetectionConfig\": {\n \"key\": \"value\"\n },\n \"smartTurnConfig\": {\n \"key\": \"value\"\n },\n \"backgroundSound\": \"backgroundSound\",\n \"denoisingConfig\": {\n \"key\": \"value\"\n },\n \"redactionConfig\": {\n \"key\": \"value\"\n },\n \"llmIdleTimeoutConfig\": {\n \"key\": \"value\"\n },\n \"sessionTimeoutConfig\": {\n \"key\": \"value\"\n }\n },\n \"_configSource\": \"active\",\n \"_versionedWorkflow\": {\n \"prompt\": \"prompt\",\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ],\n \"workflowGraph\": {\n \"key\": \"value\"\n }\n }\n }\n}",
+ "status": 201,
+ "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"60d0fe4f5311236168a109cb\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "082fb16e-c097-4973-a8ca-d737cccb9340",
+ "uuid": "3210590b-c9d0-4ffb-bf7f-960e80930740",
"persistent": true,
"priority": 3,
"metadata": {
@@ -322,11 +227,11 @@
}
},
{
- "id": "08ff74b9-da80-4e4b-bc41-e329e704cf8e",
- "name": "Update agent metadata - default",
+ "id": "082fb16e-c097-4973-a8ca-d737cccb9340",
+ "name": "Get agent by ID - default",
"request": {
"urlPathTemplate": "/agent/{id}",
- "method": "PATCH",
+ "method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
@@ -340,12 +245,12 @@
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": \"60d0fe4f5311236168a109ca\"\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"name\": \"name\",\n \"description\": \"description\",\n \"backgroundSound\": \"\",\n \"organization\": \"organization\",\n \"workflowId\": \"workflowId\",\n \"workflowType\": \"workflow_graph\",\n \"createdBy\": \"createdBy\",\n \"globalKnowledgeBaseId\": \"globalKnowledgeBaseId\",\n \"language\": {\n \"default\": \"en\",\n \"supported\": [\n \"supported\"\n ]\n },\n \"synthesizer\": {\n \"speed\": 1.1,\n \"consistency\": 1.1,\n \"similarity\": 1.1,\n \"enhancement\": 1.1,\n \"sampleRate\": 1.1\n },\n \"slmModel\": \"electron\",\n \"defaultVariables\": {\n \"key\": \"value\"\n },\n \"preCallAPI\": {\n \"isEnabled\": true,\n \"url\": \"url\",\n \"method\": \"GET\",\n \"headers\": {\n \"key\": \"value\"\n },\n \"body\": {\n \"key\": \"value\"\n },\n \"timeout\": 1,\n \"queryParams\": {\n \"key\": \"value\"\n },\n \"responseVariables\": [\n {\n \"variableName\": \"variableName\",\n \"jsonPath\": \"jsonPath\"\n }\n ]\n },\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\",\n \"avatarUrl\": \"avatarUrl\",\n \"firstMessage\": \"firstMessage\",\n \"allowInterruptions\": true,\n \"waitForUserToSpeakFirst\": true,\n \"totalCalls\": 1.1,\n \"transcriberType\": \"transcriberType\",\n \"globalPrompt\": \"globalPrompt\",\n \"archived\": true,\n \"archivedAt\": \"2024-01-15T09:30:00Z\",\n \"activeVersionId\": \"activeVersionId\",\n \"versionId\": \"versionId\",\n \"allowInboundCall\": true,\n \"phoneNumber\": [\n \"phoneNumber\"\n ],\n \"visibleToEveryone\": true,\n \"speechFormatting\": true,\n \"muteUserUntilFirstBotResponse\": true,\n \"interruptionBackoffTimer\": 1.1,\n \"enableStyleGuide\": true,\n \"callDispositionConfig\": \"callDispositionConfig\",\n \"voiceMailDetectionConfig\": {\n \"enabled\": true,\n \"endText\": \"endText\"\n },\n \"smartTurnConfig\": {\n \"isEnabled\": true,\n \"waitTimeInSecs\": 1.1\n },\n \"voiceDetectionConfig\": {\n \"confidence\": 1.1,\n \"minVolume\": 1.1,\n \"triggerTimeInSecs\": 1.1,\n \"releaseTimeInSecs\": 1.1\n },\n \"denoisingConfig\": {\n \"isEnabled\": true\n },\n \"redactionConfig\": {\n \"isEnabled\": true\n },\n \"pronunciationDicts\": [\n {\n \"word\": \"word\",\n \"pronunciation\": \"pronunciation\"\n }\n ],\n \"llmIdleTimeoutConfig\": {\n \"chatTimeoutTimeInSecs\": 1.1,\n \"webcallTimeoutTimeInSecs\": 1.1,\n \"telephonyTimeoutTimeInSecs\": 1.1,\n \"maxRetries\": 1.1\n },\n \"sessionTimeoutConfig\": {\n \"timeoutTimeInSecs\": 1.1\n },\n \"timezone\": {\n \"label\": \"label\",\n \"offset\": 1.1\n },\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ],\n \"useInternalAnalyticsModel\": true,\n \"useReasoningModel\": true\n },\n \"widgetConfig\": {\n \"position\": \"bottom-right\",\n \"size\": \"tiny\",\n \"mode\": \"chat\",\n \"theme\": \"light\",\n \"baseColor\": \"baseColor\",\n \"accentColor\": \"accentColor\",\n \"agentBubbleColor\": \"agentBubbleColor\",\n \"textOnAccentColor\": \"textOnAccentColor\",\n \"secondaryTextColor\": \"secondaryTextColor\",\n \"primaryTextColor\": \"primaryTextColor\",\n \"startButtonText\": \"startButtonText\",\n \"endButtonText\": \"endButtonText\",\n \"ctaName\": \"ctaName\",\n \"widgetName\": \"widgetName\",\n \"avatarUrl\": \"avatarUrl\",\n \"chatPlaceholder\": \"chatPlaceholder\",\n \"consentRequired\": true,\n \"consentTitle\": \"consentTitle\",\n \"consentContent\": \"consentContent\",\n \"assistantId\": \"assistantId\",\n \"allowlist\": [\n \"allowlist\"\n ]\n },\n \"_resolvedConfig\": {\n \"prompt\": \"prompt\",\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ],\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ]\n },\n \"callDispositionConfig\": \"callDispositionConfig\",\n \"modelName\": \"modelName\",\n \"transcriberType\": \"transcriberType\",\n \"defaultLanguage\": \"en\",\n \"supportedLanguages\": [\n \"supportedLanguages\"\n ],\n \"languageSwitching\": {\n \"key\": \"value\"\n },\n \"firstMessage\": \"firstMessage\",\n \"globalPrompt\": \"globalPrompt\",\n \"preCallAPI\": {\n \"key\": \"value\"\n },\n \"workflowGraph\": {\n \"key\": \"value\"\n },\n \"muteUserUntilFirstBotResponse\": true,\n \"allowInterruptions\": true,\n \"voiceDetectionConfig\": {\n \"key\": \"value\"\n },\n \"smartTurnConfig\": {\n \"key\": \"value\"\n },\n \"backgroundSound\": \"backgroundSound\",\n \"denoisingConfig\": {\n \"key\": \"value\"\n },\n \"redactionConfig\": {\n \"key\": \"value\"\n },\n \"llmIdleTimeoutConfig\": {\n \"key\": \"value\"\n },\n \"sessionTimeoutConfig\": {\n \"key\": \"value\"\n }\n },\n \"_configSource\": \"active\",\n \"_versionedWorkflow\": {\n \"prompt\": \"prompt\",\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ],\n \"workflowGraph\": {\n \"key\": \"value\"\n }\n }\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "08ff74b9-da80-4e4b-bc41-e329e704cf8e",
+ "uuid": "082fb16e-c097-4973-a8ca-d737cccb9340",
"persistent": true,
"priority": 3,
"metadata": {
@@ -358,11 +263,11 @@
}
},
{
- "id": "25c363b2-c864-42fb-a287-4dfe08d8f415",
- "name": "Archive or unarchive an agent - default",
+ "id": "08ff74b9-da80-4e4b-bc41-e329e704cf8e",
+ "name": "Update agent metadata - default",
"request": {
- "urlPathTemplate": "/agent/{id}/archive",
- "method": "DELETE",
+ "urlPathTemplate": "/agent/{id}",
+ "method": "PATCH",
"headers": {
"Authorization": {
"matches": "Bearer .+"
@@ -376,48 +281,12 @@
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"message\": \"Agent archived\"\n }\n}",
- "headers": {
- "Content-Type": "application/json"
- }
- },
- "uuid": "25c363b2-c864-42fb-a287-4dfe08d8f415",
- "persistent": true,
- "priority": 3,
- "metadata": {
- "mocklab": {
- "created": {
- "at": "2020-01-01T00:00:00.000Z",
- "via": "SYSTEM"
- }
- }
- }
- },
- {
- "id": "3210590b-c9d0-4ffb-bf7f-960e80930740",
- "name": "Duplicate agent to another organization - default",
- "request": {
- "urlPathTemplate": "/agent/{id}/duplicate",
- "method": "POST",
- "headers": {
- "Authorization": {
- "matches": "Bearer .+"
- }
- },
- "pathParameters": {
- "id": {
- "equalTo": "id"
- }
- }
- },
- "response": {
- "status": 201,
- "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"60d0fe4f5311236168a109cb\"\n }\n}",
+ "body": "{\n \"status\": true,\n \"data\": \"60d0fe4f5311236168a109ca\"\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "3210590b-c9d0-4ffb-bf7f-960e80930740",
+ "uuid": "08ff74b9-da80-4e4b-bc41-e329e704cf8e",
"persistent": true,
"priority": 3,
"metadata": {
@@ -430,10 +299,10 @@
}
},
{
- "id": "24a71b84-4392-4201-9b9f-7e672c1a6662",
- "name": "Get agent workflow - default",
+ "id": "28dfba80-4c28-4bbf-adb1-509f91e93511",
+ "name": "Get widget configuration - default",
"request": {
- "urlPathTemplate": "/agent/{id}/workflow",
+ "urlPathTemplate": "/agent/{id}/widget-config",
"method": "GET",
"headers": {
"Authorization": {
@@ -448,12 +317,12 @@
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"prompt\": \"prompt\",\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ],\n \"type\": \"workflow_graph\",\n \"data\": {\n \"nodes\": [\n {}\n ],\n \"edges\": [\n {}\n ]\n }\n }\n}",
+ "body": "{\n \"success\": true,\n \"data\": {\n \"assistantId\": \"assistantId\",\n \"position\": \"bottom-right\",\n \"size\": \"tiny\",\n \"borderRadius\": 1.1,\n \"mode\": \"chat\",\n \"theme\": \"light\",\n \"baseColor\": \"baseColor\",\n \"accentColor\": \"accentColor\",\n \"title\": \"title\",\n \"startButtonText\": \"startButtonText\",\n \"endButtonText\": \"endButtonText\",\n \"avatarUrl\": \"avatarUrl\",\n \"voiceShowTranscript\": true,\n \"consentRequired\": true\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "24a71b84-4392-4201-9b9f-7e672c1a6662",
+ "uuid": "28dfba80-4c28-4bbf-adb1-509f91e93511",
"persistent": true,
"priority": 3,
"metadata": {
@@ -466,10 +335,10 @@
}
},
{
- "id": "bcd0cab9-501c-4dba-ab50-31b159dbc68b",
- "name": "Update workflow configuration - default",
+ "id": "a3147775-2206-4de1-889b-5390dacb7ec3",
+ "name": "Update widget configuration - default",
"request": {
- "urlPathTemplate": "/workflow/{id}",
+ "urlPathTemplate": "/agent/{id}/widget-config",
"method": "PATCH",
"headers": {
"Authorization": {
@@ -478,18 +347,18 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
}
},
"response": {
"status": 200,
- "body": "\"\"",
+ "body": "{\n \"success\": true,\n \"data\": {\n \"key\": \"value\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "bcd0cab9-501c-4dba-ab50-31b159dbc68b",
+ "uuid": "a3147775-2206-4de1-889b-5390dacb7ec3",
"persistent": true,
"priority": 3,
"metadata": {
@@ -502,42 +371,11 @@
}
},
{
- "id": "d7d9d0f0-c6fb-492b-9409-833a40a406c6",
- "name": "Create agent with AI brief - default",
+ "id": "f14ee553-4809-4987-94bf-a2396a7fc00d",
+ "name": "Get avatar upload URL - default",
"request": {
- "urlPathTemplate": "/agent/with-ai",
+ "urlPathTemplate": "/agent/{id}/avatar/presigned-url",
"method": "POST",
- "headers": {
- "Authorization": {
- "matches": "Bearer .+"
- }
- }
- },
- "response": {
- "status": 201,
- "body": "{\n \"status\": true,\n \"data\": \"60d0fe4f5311236168a109ca\"\n}",
- "headers": {
- "Content-Type": "application/json"
- }
- },
- "uuid": "d7d9d0f0-c6fb-492b-9409-833a40a406c6",
- "persistent": true,
- "priority": 3,
- "metadata": {
- "mocklab": {
- "created": {
- "at": "2020-01-01T00:00:00.000Z",
- "via": "SYSTEM"
- }
- }
- }
- },
- {
- "id": "68618a7e-0429-4aa5-9e98-0dbd3c0ce1da",
- "name": "Get conversation logs for an agent - default",
- "request": {
- "urlPathTemplate": "/agent/{id}/call-logs",
- "method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
@@ -545,18 +383,18 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
}
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"callLogs\": [\n {}\n ],\n \"pagination\": {\n \"currentPage\": 1,\n \"totalPages\": 1,\n \"totalCount\": 1,\n \"offset\": 1\n }\n }\n}",
+ "body": "{\n \"success\": true,\n \"data\": {\n \"presignedUrl\": \"presignedUrl\",\n \"cdnUrl\": \"cdnUrl\",\n \"key\": \"key\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "68618a7e-0429-4aa5-9e98-0dbd3c0ce1da",
+ "uuid": "f14ee553-4809-4987-94bf-a2396a7fc00d",
"persistent": true,
"priority": 3,
"metadata": {
@@ -569,10 +407,10 @@
}
},
{
- "id": "c6014ed5-e8dd-4537-824d-a07ac4c44e18",
- "name": "Get widget configuration for an agent - default",
+ "id": "8e6d23ea-b98e-409c-8d95-0525c9d67350",
+ "name": "Get agent call logs - default",
"request": {
- "urlPathTemplate": "/agent/{id}/widget-config",
+ "urlPathTemplate": "/agent/{id}/call-logs",
"method": "GET",
"headers": {
"Authorization": {
@@ -581,18 +419,18 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
}
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"position\": \"bottom-right\",\n \"size\": \"tiny\",\n \"borderRadius\": 1.1,\n \"mode\": \"chat\",\n \"theme\": \"light\",\n \"baseColor\": \"baseColor\",\n \"accentColor\": \"accentColor\",\n \"agentBubbleColor\": \"agentBubbleColor\",\n \"textOnAccentColor\": \"textOnAccentColor\",\n \"secondaryTextColor\": \"secondaryTextColor\",\n \"primaryTextColor\": \"primaryTextColor\",\n \"title\": \"title\",\n \"startButtonText\": \"startButtonText\",\n \"endButtonText\": \"endButtonText\",\n \"ctaTitle\": \"ctaTitle\",\n \"ctaSubtitle\": \"ctaSubtitle\",\n \"ctaName\": \"ctaName\",\n \"widgetName\": \"widgetName\",\n \"avatarUrl\": \"avatarUrl\",\n \"voiceEmptyMessage\": \"voiceEmptyMessage\",\n \"voiceActiveEmptyMessage\": \"voiceActiveEmptyMessage\",\n \"chatEmptyMessage\": \"chatEmptyMessage\",\n \"chatFirstMessage\": \"chatFirstMessage\",\n \"chatPlaceholder\": \"chatPlaceholder\",\n \"voiceShowTranscript\": true,\n \"consentRequired\": true,\n \"consentTitle\": \"consentTitle\",\n \"consentContent\": \"consentContent\",\n \"consentStorageKey\": \"consentStorageKey\",\n \"publicKey\": \"publicKey\",\n \"assistantId\": \"assistantId\",\n \"allowlist\": [\n \"allowlist\"\n ]\n }\n}",
+ "body": "{\n \"success\": true,\n \"data\": {\n \"callLogs\": [\n {}\n ],\n \"totalCount\": 1.1,\n \"page\": 1.1,\n \"offset\": 1.1\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "c6014ed5-e8dd-4537-824d-a07ac4c44e18",
+ "uuid": "8e6d23ea-b98e-409c-8d95-0525c9d67350",
"persistent": true,
"priority": 3,
"metadata": {
@@ -605,11 +443,11 @@
}
},
{
- "id": "49dc3f3f-8c33-42df-8d71-a300d9f04b59",
- "name": "Update widget configuration for an agent - default",
+ "id": "25c363b2-c864-42fb-a287-4dfe08d8f415",
+ "name": "Archive or unarchive an agent - default",
"request": {
- "urlPathTemplate": "/agent/{id}/widget-config",
- "method": "PATCH",
+ "urlPathTemplate": "/agent/{id}/archive",
+ "method": "DELETE",
"headers": {
"Authorization": {
"matches": "Bearer .+"
@@ -617,18 +455,18 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
}
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"position\": \"bottom-right\",\n \"size\": \"tiny\",\n \"borderRadius\": 1.1,\n \"mode\": \"chat\",\n \"theme\": \"light\",\n \"baseColor\": \"baseColor\",\n \"accentColor\": \"accentColor\",\n \"agentBubbleColor\": \"agentBubbleColor\",\n \"textOnAccentColor\": \"textOnAccentColor\",\n \"secondaryTextColor\": \"secondaryTextColor\",\n \"primaryTextColor\": \"primaryTextColor\",\n \"title\": \"title\",\n \"startButtonText\": \"startButtonText\",\n \"endButtonText\": \"endButtonText\",\n \"ctaTitle\": \"ctaTitle\",\n \"ctaSubtitle\": \"ctaSubtitle\",\n \"ctaName\": \"ctaName\",\n \"widgetName\": \"widgetName\",\n \"avatarUrl\": \"avatarUrl\",\n \"voiceEmptyMessage\": \"voiceEmptyMessage\",\n \"voiceActiveEmptyMessage\": \"voiceActiveEmptyMessage\",\n \"chatEmptyMessage\": \"chatEmptyMessage\",\n \"chatFirstMessage\": \"chatFirstMessage\",\n \"chatPlaceholder\": \"chatPlaceholder\",\n \"voiceShowTranscript\": true,\n \"consentRequired\": true,\n \"consentTitle\": \"consentTitle\",\n \"consentContent\": \"consentContent\",\n \"consentStorageKey\": \"consentStorageKey\",\n \"publicKey\": \"publicKey\",\n \"assistantId\": \"assistantId\",\n \"allowlist\": [\n \"allowlist\"\n ]\n }\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"message\": \"Agent archived\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "49dc3f3f-8c33-42df-8d71-a300d9f04b59",
+ "uuid": "25c363b2-c864-42fb-a287-4dfe08d8f415",
"persistent": true,
"priority": 3,
"metadata": {
@@ -641,11 +479,11 @@
}
},
{
- "id": "2cecf2d1-fc91-431d-9a4d-2648cce2b40f",
- "name": "Get prompt-config metadata (question definitions + labels) - default",
+ "id": "c04e81c5-80ee-4829-a45a-4390ca4cc3ab",
+ "name": "Register a realtime agent call - default",
"request": {
- "urlPathTemplate": "/agent/prompt-config",
- "method": "GET",
+ "urlPathTemplate": "/conversation/register-call",
+ "method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
@@ -653,13 +491,13 @@
}
},
"response": {
- "status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"questions\": [\n {}\n ],\n \"exampleLabels\": [\n \"exampleLabels\"\n ],\n \"defaultLabel\": \"defaultLabel\"\n }\n}",
+ "status": 201,
+ "body": "{\n \"status\": true,\n \"data\": {\n \"access_token\": \"wct_9f8c2b1e-4d5a-4e6f-a7b8-c9d0e1f2a3b4\",\n \"expires_in\": 30,\n \"sample_rate\": 24000\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "2cecf2d1-fc91-431d-9a4d-2648cce2b40f",
+ "uuid": "c04e81c5-80ee-4829-a45a-4390ca4cc3ab",
"persistent": true,
"priority": 3,
"metadata": {
@@ -669,8 +507,7 @@
"via": "SYSTEM"
}
}
- },
- "postServeActions": []
+ }
},
{
"id": "6447319e-1409-4d0d-b668-1def86a791ff",
@@ -783,7 +620,7 @@
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"60d0fe4f5311236168a109ca\",\n \"callId\": \"CALL-1737000000000-abc123\",\n \"agent\": {\n \"_id\": \"_id\",\n \"name\": \"name\",\n \"description\": \"description\",\n \"backgroundSound\": \"\",\n \"organization\": \"organization\",\n \"workflowId\": \"workflowId\",\n \"workflowType\": \"workflow_graph\",\n \"createdBy\": \"createdBy\",\n \"globalKnowledgeBaseId\": \"globalKnowledgeBaseId\",\n \"slmModel\": \"electron\",\n \"defaultVariables\": {\n \"key\": \"value\"\n },\n \"preCallAPI\": {\n \"url\": \"url\",\n \"method\": \"GET\"\n },\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\",\n \"avatarUrl\": \"avatarUrl\",\n \"firstMessage\": \"firstMessage\",\n \"allowInterruptions\": true,\n \"waitForUserToSpeakFirst\": true,\n \"totalCalls\": 1.1,\n \"transcriberType\": \"transcriberType\",\n \"globalPrompt\": \"globalPrompt\",\n \"archived\": true,\n \"archivedAt\": \"2024-01-15T09:30:00Z\",\n \"isLocked\": true,\n \"activeVersionId\": \"activeVersionId\",\n \"versionId\": \"versionId\",\n \"allowInboundCall\": true,\n \"phoneNumber\": [\n \"phoneNumber\"\n ],\n \"visibleToEveryone\": true,\n \"speechFormatting\": true,\n \"muteUserUntilFirstBotResponse\": true,\n \"interruptionBackoffTimer\": 1.1,\n \"enableStyleGuide\": true,\n \"callDispositionConfig\": \"callDispositionConfig\",\n \"pronunciationDicts\": [\n {\n \"word\": \"word\",\n \"pronunciation\": \"pronunciation\"\n }\n ],\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ]\n },\n \"_resolvedConfig\": {\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ],\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ]\n }\n },\n \"_configSource\": \"active\",\n \"_versionedWorkflow\": {\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ]\n }\n },\n \"status\": \"pending\",\n \"duration\": 300,\n \"recordingUrl\": \"recordingUrl\",\n \"recordingDualUrl\": \"recordingDualUrl\",\n \"from\": \"from\",\n \"to\": \"to\",\n \"type\": \"telephony_inbound\",\n \"transcript\": [\n {}\n ],\n \"variables\": {\n \"key\": \"value\"\n },\n \"events\": [\n {\n \"key\": \"value\"\n }\n ],\n \"callCost\": 1.1,\n \"callFailureReason\": \"callFailureReason\",\n \"retryCallId\": \"retryCallId\",\n \"postCallAnalytics\": {\n \"summary\": \"summary\",\n \"dispositionMetrics\": [\n {}\n ]\n },\n \"turnLatencyMetrics\": {\n \"turns\": 1.1,\n \"avgLatency\": 1.1,\n \"medianLatency\": 1.1,\n \"minLatency\": 1.1,\n \"maxLatency\": 1.1,\n \"latencies\": [\n 1.1\n ],\n \"transitions\": [\n {}\n ],\n \"processedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"voiceConfigUsed\": {\n \"model\": \"model\",\n \"voiceId\": \"voiceId\",\n \"gender\": \"gender\"\n },\n \"slmModelUsed\": \"slmModelUsed\"\n }\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"60d0fe4f5311236168a109ca\",\n \"callId\": \"CALL-1737000000000-abc123\",\n \"agent\": {\n \"_id\": \"_id\",\n \"name\": \"name\",\n \"description\": \"description\",\n \"backgroundSound\": \"\",\n \"organization\": \"organization\",\n \"workflowId\": \"workflowId\",\n \"workflowType\": \"workflow_graph\",\n \"createdBy\": \"createdBy\",\n \"globalKnowledgeBaseId\": \"globalKnowledgeBaseId\",\n \"slmModel\": \"electron\",\n \"defaultVariables\": {\n \"key\": \"value\"\n },\n \"preCallAPI\": {\n \"url\": \"url\",\n \"method\": \"GET\"\n },\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\",\n \"avatarUrl\": \"avatarUrl\",\n \"firstMessage\": \"firstMessage\",\n \"allowInterruptions\": true,\n \"waitForUserToSpeakFirst\": true,\n \"totalCalls\": 1.1,\n \"transcriberType\": \"transcriberType\",\n \"globalPrompt\": \"globalPrompt\",\n \"archived\": true,\n \"archivedAt\": \"2024-01-15T09:30:00Z\",\n \"activeVersionId\": \"activeVersionId\",\n \"versionId\": \"versionId\",\n \"allowInboundCall\": true,\n \"phoneNumber\": [\n \"phoneNumber\"\n ],\n \"visibleToEveryone\": true,\n \"speechFormatting\": true,\n \"muteUserUntilFirstBotResponse\": true,\n \"interruptionBackoffTimer\": 1.1,\n \"enableStyleGuide\": true,\n \"callDispositionConfig\": \"callDispositionConfig\",\n \"pronunciationDicts\": [\n {\n \"word\": \"word\",\n \"pronunciation\": \"pronunciation\"\n }\n ],\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ]\n },\n \"_resolvedConfig\": {\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ],\n \"postCallAnalyticsConfig\": {\n \"dispositionMetrics\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"Was the customer issue resolved by the end of the call?\",\n \"dispositionMetricType\": \"STRING\",\n \"choices\": [\n \"resolved\",\n \"escalated\",\n \"callback_scheduled\",\n \"no_action\"\n ]\n }\n ]\n }\n },\n \"_configSource\": \"active\",\n \"_versionedWorkflow\": {\n \"tools\": [\n {\n \"type\": \"end_call\",\n \"name\": \"end_call\",\n \"description\": \"End the conversation when the user is satisfied\",\n \"transferNumber\": \"+1234567890\",\n \"url\": \"https://api.example.com/orders\",\n \"headers\": {\n \"Authorization\": \"Bearer TOKEN\",\n \"Content-Type\": \"application/json\"\n },\n \"headersArray\": [\n {\n \"key\": \"Authorization\",\n \"value\": \"Bearer TOKEN\"\n }\n ],\n \"queryParams\": [\n {\n \"key\": \"id\",\n \"value\": \"{{order_id}}\"\n }\n ],\n \"requestBody\": \"{\\\"customer_id\\\": \\\"{{customer_id}}\\\"}\",\n \"responseVariables\": [\n {\n \"variableName\": \"orderStatus\",\n \"jsonPath\": \"$.data.status\"\n }\n ],\n \"variablesExtractionSchema\": [\n {\n \"name\": \"customer_name\",\n \"description\": \"The customer's full name\",\n \"type\": \"text\",\n \"values\": [\n \"satisfied\",\n \"unsatisfied\",\n \"neutral\"\n ]\n }\n ],\n \"knowledgeBaseId\": \"60d0fe4f5311236168a109ca\",\n \"fillerPhrases\": [\n \"Let me check that for you\",\n \"One moment please\"\n ]\n }\n ]\n }\n },\n \"status\": \"pending\",\n \"duration\": 300,\n \"recordingUrl\": \"recordingUrl\",\n \"recordingDualUrl\": \"recordingDualUrl\",\n \"from\": \"from\",\n \"to\": \"to\",\n \"type\": \"telephony_inbound\",\n \"transcript\": [\n {}\n ],\n \"variables\": {\n \"key\": \"value\"\n },\n \"events\": [\n {\n \"key\": \"value\"\n }\n ],\n \"callCost\": 1.1,\n \"callFailureReason\": \"callFailureReason\",\n \"retryCallId\": \"retryCallId\",\n \"postCallAnalytics\": {\n \"summary\": \"summary\",\n \"dispositionMetrics\": [\n {}\n ]\n },\n \"turnLatencyMetrics\": {\n \"turns\": 1.1,\n \"avgLatency\": 1.1,\n \"medianLatency\": 1.1,\n \"minLatency\": 1.1,\n \"maxLatency\": 1.1,\n \"latencies\": [\n 1.1\n ],\n \"transitions\": [\n {}\n ],\n \"processedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"voiceConfigUsed\": {\n \"model\": \"model\",\n \"voiceId\": \"voiceId\",\n \"gender\": \"gender\"\n },\n \"slmModelUsed\": \"slmModelUsed\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
@@ -1214,42 +1051,6 @@
}
}
},
- {
- "id": "8db7fb9b-e77b-4ae6-b013-87dbed43ffac",
- "name": "Export campaign call logs as a JSON file - default",
- "request": {
- "urlPathTemplate": "/campaign/{id}/logs/export",
- "method": "GET",
- "headers": {
- "Authorization": {
- "matches": "Bearer .+"
- }
- },
- "pathParameters": {
- "id": {
- "equalTo": "id"
- }
- }
- },
- "response": {
- "status": 200,
- "body": "[\n {\n \"_id\": \"_id\",\n \"callId\": \"callId\",\n \"status\": \"status\",\n \"duration\": 1.1,\n \"recordingUrl\": \"recordingUrl\",\n \"from\": \"from\",\n \"to\": \"to\",\n \"agentId\": \"agentId\",\n \"statusDetails\": [\n \"statusDetails\"\n ],\n \"events\": [\n {\n \"key\": \"value\"\n }\n ]\n }\n]",
- "headers": {
- "Content-Type": "application/json"
- }
- },
- "uuid": "8db7fb9b-e77b-4ae6-b013-87dbed43ffac",
- "persistent": true,
- "priority": 3,
- "metadata": {
- "mocklab": {
- "created": {
- "at": "2020-01-01T00:00:00.000Z",
- "via": "SYSTEM"
- }
- }
- }
- },
{
"id": "0f68c864-1595-4a06-a59d-172d7a5d924e",
"name": "Get all knowledge bases - default",
@@ -2730,7 +2531,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
}
},
@@ -2766,7 +2567,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
}
},
@@ -2802,7 +2603,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"draftId": {
"equalTo": "draftId"
@@ -2841,7 +2642,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"draftId": {
"equalTo": "draftId"
@@ -2880,7 +2681,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"draftId": {
"equalTo": "draftId"
@@ -2919,7 +2720,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"draftId": {
"equalTo": "draftId"
@@ -2928,7 +2729,7 @@
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"key\": \"value\"\n }\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"sections\": {\n \"key\": {}\n },\n \"hasChanges\": true\n }\n}",
"headers": {
"Content-Type": "application/json"
}
@@ -2958,7 +2759,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"draftId": {
"equalTo": "draftId"
@@ -2997,7 +2798,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"draftId": {
"equalTo": "draftId"
@@ -3036,7 +2837,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"draftId": {
"equalTo": "draftId"
@@ -3075,7 +2876,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
}
},
@@ -3111,7 +2912,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
}
},
"queryParameters": {
@@ -3142,56 +2943,6 @@
}
}
},
- {
- "id": "26c4525d-4fbe-4ec2-a2e3-c1a28c075982",
- "name": "Compare metrics between two versions - default",
- "request": {
- "urlPathTemplate": "/agent/{id}/versions/compare-metrics",
- "method": "GET",
- "headers": {
- "Authorization": {
- "matches": "Bearer .+"
- }
- },
- "pathParameters": {
- "id": {
- "equalTo": "60d0fe4f5311236168a109ca"
- }
- },
- "queryParameters": {
- "versionA": {
- "equalTo": "versionA"
- },
- "versionB": {
- "equalTo": "versionB"
- },
- "dateFrom": {
- "equalTo": "2026-05-01"
- },
- "dateTo": {
- "equalTo": "2026-05-31"
- }
- }
- },
- "response": {
- "status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"configDiff\": {\n \"unchangedSections\": [\n \"unchangedSections\"\n ],\n \"diffs\": [\n {}\n ]\n }\n }\n}",
- "headers": {
- "Content-Type": "application/json"
- }
- },
- "uuid": "26c4525d-4fbe-4ec2-a2e3-c1a28c075982",
- "persistent": true,
- "priority": 3,
- "metadata": {
- "mocklab": {
- "created": {
- "at": "2020-01-01T00:00:00.000Z",
- "via": "SYSTEM"
- }
- }
- }
- },
{
"id": "b355fc5e-fddf-4949-add8-e692d3aba429",
"name": "Get version detail - default",
@@ -3205,7 +2956,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"versionId": {
"equalTo": "versionId"
@@ -3244,7 +2995,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"versionId": {
"equalTo": "versionId"
@@ -3283,7 +3034,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"versionId": {
"equalTo": "versionId"
@@ -3322,7 +3073,7 @@
},
"pathParameters": {
"id": {
- "equalTo": "60d0fe4f5311236168a109ca"
+ "equalTo": "id"
},
"versionId": {
"equalTo": "versionId"
@@ -3349,25 +3100,30 @@
}
},
{
- "id": "a0c78fe6-f292-4dfb-8a8d-4adcd247f241",
- "name": "Score a prompt - default",
+ "id": "8db4663c-b278-4b3f-90c4-c9c2fd9ab7e7",
+ "name": "List branches - default",
"request": {
- "urlPathTemplate": "/prompt-scoring/score",
- "method": "POST",
+ "urlPathTemplate": "/agent/{id}/branches",
+ "method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"overall_score\": 56,\n \"overall_grade\": \"Needs Work\",\n \"band\": \"lean\",\n \"estimated_ttft_overhead_ms\": 12.9,\n \"dimensions\": [\n {\n \"tier\": 1,\n \"level\": \"Adequate\",\n \"evidence_span\": \"You are a friendly and helpful weather assistant. Your role is to provide accurate, real-time weather information to users.\",\n \"title\": \"Clear but basic role definition\",\n \"description\": \"The role is clearly defined but lacks specific success criteria or scope boundaries.\"\n },\n {\n \"tier\": 1,\n \"level\": \"Weak\",\n \"evidence_span\": \"Use the get_weather function to fetch real-time data\",\n \"title\": \"Undeclared tool reference\",\n \"description\": \"The 'get_weather' tool is referenced but not defined, and failure paths are missing.\"\n },\n {\n \"tier\": 2,\n \"level\": \"Missing\",\n \"evidence_span\": \"no relevant content found\",\n \"title\": \"No failure mode coverage\",\n \"description\": \"The prompt contains no instructions for handling errors, tool failures, or unclear user input.\"\n }\n ]\n }\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"branches\": [\n {}\n ]\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "a0c78fe6-f292-4dfb-8a8d-4adcd247f241",
+ "uuid": "8db4663c-b278-4b3f-90c4-c9c2fd9ab7e7",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3380,25 +3136,30 @@
}
},
{
- "id": "f4e9658c-d989-4ba4-b1c5-cadf2a257164",
- "name": "List DNC entries for the organization - default",
+ "id": "4296d204-bb40-47b5-85ef-509a030f7414",
+ "name": "Create a branch - default",
"request": {
- "urlPathTemplate": "/dnc",
- "method": "GET",
+ "urlPathTemplate": "/agent/{id}/branches",
+ "method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ }
}
},
"response": {
- "status": 200,
- "body": "{\n \"status\": true,\n \"data\": {\n \"entries\": [\n {}\n ],\n \"pagination\": {\n \"page\": 1,\n \"offset\": 1,\n \"total\": 1,\n \"totalPages\": 1,\n \"hasMore\": true\n }\n }\n}",
+ "status": 201,
+ "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"name\": \"name\",\n \"isDefault\": true,\n \"sourceBranchId\": \"sourceBranchId\",\n \"sourceRevisionId\": \"sourceRevisionId\",\n \"headRevisionId\": \"headRevisionId\",\n \"openDraftId\": \"openDraftId\",\n \"status\": \"active\",\n \"createdBy\": \"createdBy\",\n \"updatedBy\": \"updatedBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "f4e9658c-d989-4ba4-b1c5-cadf2a257164",
+ "uuid": "4296d204-bb40-47b5-85ef-509a030f7414",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3408,29 +3169,36 @@
"via": "SYSTEM"
}
}
- },
- "postServeActions": []
+ }
},
{
- "id": "23284c9e-d15e-4f3a-84b7-de9613ccf399",
- "name": "Get pronunciation dictionaries - default",
+ "id": "64d2622f-ec62-4dc1-bd39-94548f1617ab",
+ "name": "Get branch - default",
"request": {
- "urlPathTemplate": "/waves/v1/pronunciation-dicts",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}",
"method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "[\n {\n \"id\": \"64f1234567890abcdef12345\",\n \"items\": [\n {\n \"word\": \"mysql\",\n \"pronunciation\": \"my-sequel\"\n }\n ],\n \"createdAt\": \"2023-09-01T12:00:00Z\"\n }\n]",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"branch\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"name\": \"name\",\n \"isDefault\": true,\n \"sourceBranchId\": \"sourceBranchId\",\n \"sourceRevisionId\": \"sourceRevisionId\",\n \"headRevisionId\": \"headRevisionId\",\n \"openDraftId\": \"openDraftId\",\n \"status\": \"active\",\n \"createdBy\": \"createdBy\",\n \"updatedBy\": \"updatedBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"isLive\": true,\n \"hasOpenDraft\": true,\n \"revisionsCount\": 1,\n \"headRevisionNumber\": 1\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "23284c9e-d15e-4f3a-84b7-de9613ccf399",
+ "uuid": "64d2622f-ec62-4dc1-bd39-94548f1617ab",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3440,29 +3208,36 @@
"via": "SYSTEM"
}
}
- },
- "postServeActions": []
+ }
},
{
- "id": "2bda2b48-3814-4298-b92c-dad37f8c7a76",
- "name": "Create pronunciation dictionary - default",
+ "id": "be8b5ab2-c05e-41be-89a2-80773cc1a170",
+ "name": "Rename a branch - default",
"request": {
- "urlPathTemplate": "/waves/v1/pronunciation-dicts",
- "method": "POST",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}",
+ "method": "PATCH",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"id\": \"64f1234567890abcdef12345\",\n \"items\": [\n {\n \"word\": \"mysql\",\n \"pronunciation\": \"my-sequel\"\n }\n ],\n \"createdAt\": \"2023-09-01T12:00:00Z\"\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"name\": \"name\",\n \"isDefault\": true,\n \"sourceBranchId\": \"sourceBranchId\",\n \"sourceRevisionId\": \"sourceRevisionId\",\n \"headRevisionId\": \"headRevisionId\",\n \"openDraftId\": \"openDraftId\",\n \"status\": \"active\",\n \"createdBy\": \"createdBy\",\n \"updatedBy\": \"updatedBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "2bda2b48-3814-4298-b92c-dad37f8c7a76",
+ "uuid": "be8b5ab2-c05e-41be-89a2-80773cc1a170",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3475,25 +3250,33 @@
}
},
{
- "id": "d2a666c0-9796-4eeb-bcff-8a039794cdef",
- "name": "Update pronunciation dictionary - default",
+ "id": "84519979-c98a-4e67-8a0e-b587405878ac",
+ "name": "Archive a branch - default",
"request": {
- "urlPathTemplate": "/waves/v1/pronunciation-dicts",
- "method": "PUT",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/archive",
+ "method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"id\": \"64f1234567890abcdef12345\",\n \"items\": [\n {\n \"word\": \"mysql\",\n \"pronunciation\": \"my-sequel\"\n },\n {\n \"word\": \"goodbye\",\n \"pronunciation\": \"goodbai\"\n }\n ]\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"archived\": true\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "d2a666c0-9796-4eeb-bcff-8a039794cdef",
+ "uuid": "84519979-c98a-4e67-8a0e-b587405878ac",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3506,25 +3289,33 @@
}
},
{
- "id": "e8661638-733d-4a3c-9f3e-2653ca6aa6c2",
- "name": "Delete pronunciation dictionary - default",
+ "id": "b618d43f-f3ad-405b-90f0-bfa2f6dc7066",
+ "name": "Make a branch live - default",
"request": {
- "urlPathTemplate": "/waves/v1/pronunciation-dicts",
- "method": "DELETE",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/live",
+ "method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"id\": \"64f1234567890abcdef12345\",\n \"deleted\": true\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"branch\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"name\": \"name\",\n \"isDefault\": true,\n \"sourceBranchId\": \"sourceBranchId\",\n \"sourceRevisionId\": \"sourceRevisionId\",\n \"headRevisionId\": \"headRevisionId\",\n \"openDraftId\": \"openDraftId\",\n \"status\": \"active\",\n \"createdBy\": \"createdBy\",\n \"updatedBy\": \"updatedBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"isLive\": true,\n \"hasOpenDraft\": true,\n \"revisionsCount\": 1,\n \"headRevisionNumber\": 1\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "e8661638-733d-4a3c-9f3e-2653ca6aa6c2",
+ "uuid": "b618d43f-f3ad-405b-90f0-bfa2f6dc7066",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3537,25 +3328,33 @@
}
},
{
- "id": "5500f660-6aa4-448a-8957-ab600545e121",
- "name": "Text to speech - default",
+ "id": "22f7ca99-8694-40c1-9944-e27c16467190",
+ "name": "Get open draft - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning/get_speech",
- "method": "POST",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/draft",
+ "method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"key\": \"value\"\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"latest\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"status\": \"published\",\n \"branch\": \"branch\",\n \"revisionNumber\": 1,\n \"versionNumber\": 1,\n \"label\": \"label\",\n \"description\": \"description\",\n \"isPinned\": true,\n \"publishedBy\": \"publishedBy\",\n \"publishedAt\": \"2024-01-15T09:30:00Z\",\n \"publishedByName\": \"publishedByName\",\n \"restoredFromLabel\": \"restoredFromLabel\",\n \"draftId\": \"draftId\",\n \"draftName\": \"draftName\",\n \"draftRevision\": 1,\n \"sourceVersionId\": \"sourceVersionId\",\n \"workflowType\": \"workflowType\",\n \"parentVersion\": \"parentVersion\",\n \"isActive\": true,\n \"activatedBy\": \"activatedBy\",\n \"activatedAt\": \"2024-01-15T09:30:00Z\",\n \"restoredFromRevisionId\": \"restoredFromRevisionId\",\n \"sourceDraftId\": \"sourceDraftId\",\n \"promptScoreStale\": true,\n \"createdBy\": \"createdBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"editCount\": 1,\n \"editHistory\": [\n {}\n ]\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "5500f660-6aa4-448a-8957-ab600545e121",
+ "uuid": "22f7ca99-8694-40c1-9944-e27c16467190",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3568,25 +3367,33 @@
}
},
{
- "id": "5d2a1219-21eb-4b3a-93ed-adfb791795c4",
- "name": "Text to Speech (Deprecated) - default",
+ "id": "5cf384e2-c1c1-4197-accb-419080aebc67",
+ "name": "Write to draft - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-large/get_speech",
- "method": "POST",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/draft",
+ "method": "PUT",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"key\": \"value\"\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"status\": \"published\",\n \"branch\": \"branch\",\n \"revisionNumber\": 1,\n \"versionNumber\": 1,\n \"label\": \"label\",\n \"description\": \"description\",\n \"isPinned\": true,\n \"publishedBy\": \"publishedBy\",\n \"publishedAt\": \"2024-01-15T09:30:00Z\",\n \"publishedByName\": \"publishedByName\",\n \"restoredFromLabel\": \"restoredFromLabel\",\n \"draftId\": \"draftId\",\n \"draftName\": \"draftName\",\n \"draftRevision\": 1,\n \"sourceVersionId\": \"sourceVersionId\",\n \"blocks\": {\n \"workflow_prompt\": \"workflow_prompt\",\n \"workflow_tools\": \"workflow_tools\",\n \"workflow_graph\": \"workflow_graph\",\n \"llm\": \"llm\",\n \"voice\": \"voice\",\n \"language\": \"language\",\n \"call_handling\": \"call_handling\",\n \"detection\": \"detection\",\n \"analytics\": \"analytics\",\n \"timeouts\": \"timeouts\",\n \"audio\": \"audio\",\n \"privacy\": \"privacy\",\n \"widget\": \"widget\",\n \"playbooks\": \"playbooks\"\n },\n \"workflowType\": \"workflowType\",\n \"parentVersion\": \"parentVersion\",\n \"isActive\": true,\n \"activatedBy\": \"activatedBy\",\n \"activatedAt\": \"2024-01-15T09:30:00Z\",\n \"securityCheck\": {\n \"status\": \"status\",\n \"reason\": \"reason\",\n \"triggeredAt\": \"2024-01-15T09:30:00Z\",\n \"completedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"pendingPublish\": {\n \"state\": \"active\",\n \"startedBy\": \"startedBy\",\n \"startedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"restoredFromRevisionId\": \"restoredFromRevisionId\",\n \"sourceDraftId\": \"sourceDraftId\",\n \"promptScore\": {\n \"overall_score\": 1.1,\n \"overall_grade\": \"overall_grade\",\n \"band\": \"band\",\n \"estimated_ttft_overhead_ms\": 1.1,\n \"scoredAt\": \"2024-01-15T09:30:00Z\",\n \"dimensions\": [\n {}\n ]\n },\n \"promptScoreStale\": true,\n \"createdBy\": \"createdBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "5d2a1219-21eb-4b3a-93ed-adfb791795c4",
+ "uuid": "5cf384e2-c1c1-4197-accb-419080aebc67",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3599,25 +3406,33 @@
}
},
{
- "id": "cf20eee5-9ffc-407a-82e7-0fd54d9c7143",
- "name": "Text to Speech (Deprecated) - default",
+ "id": "2fd42108-766f-4d44-b25b-f3533d207961",
+ "name": "Discard open draft - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-v2/get_speech",
- "method": "POST",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/draft",
+ "method": "DELETE",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"key\": \"value\"\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"discarded\": true\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "cf20eee5-9ffc-407a-82e7-0fd54d9c7143",
+ "uuid": "2fd42108-766f-4d44-b25b-f3533d207961",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3630,25 +3445,33 @@
}
},
{
- "id": "f88779d9-9c06-4e6b-91c0-a066bcf5d281",
- "name": "Text to Speech (SSE) (Deprecated) - default",
+ "id": "61391cd6-5f1b-43c0-9a3c-73279e03f37f",
+ "name": "Publish draft - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-large/stream",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/draft/publish",
"method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "event: message\ndata: {\"data\":\"event: chunk\\ndata: \\ndone: false\\n\"}\n",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"state\": \"scanning\",\n \"revision\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"status\": \"published\",\n \"branch\": \"branch\",\n \"revisionNumber\": 1,\n \"versionNumber\": 1,\n \"label\": \"label\",\n \"description\": \"description\",\n \"isPinned\": true,\n \"publishedBy\": \"publishedBy\",\n \"publishedAt\": \"2024-01-15T09:30:00Z\",\n \"publishedByName\": \"publishedByName\",\n \"restoredFromLabel\": \"restoredFromLabel\",\n \"draftId\": \"draftId\",\n \"draftName\": \"draftName\",\n \"draftRevision\": 1,\n \"sourceVersionId\": \"sourceVersionId\",\n \"workflowType\": \"workflowType\",\n \"parentVersion\": \"parentVersion\",\n \"isActive\": true,\n \"activatedBy\": \"activatedBy\",\n \"activatedAt\": \"2024-01-15T09:30:00Z\",\n \"restoredFromRevisionId\": \"restoredFromRevisionId\",\n \"sourceDraftId\": \"sourceDraftId\",\n \"promptScoreStale\": true,\n \"createdBy\": \"createdBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n }\n}",
"headers": {
- "Content-Type": "text/event-stream"
+ "Content-Type": "application/json"
}
},
- "uuid": "f88779d9-9c06-4e6b-91c0-a066bcf5d281",
+ "uuid": "61391cd6-5f1b-43c0-9a3c-73279e03f37f",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3661,25 +3484,33 @@
}
},
{
- "id": "a9a97526-65c8-44f5-8bdf-7b346e329612",
- "name": "Text to Speech (SSE) (Deprecated) - default",
+ "id": "0854ebc2-2391-447e-ab99-9067191ca609",
+ "name": "Cancel in-flight publish - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-v2/stream",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/draft/publish/cancel",
"method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "event: message\ndata: {\"data\":\"event: chunk\\ndata: \\ndone: false\\n\"}\n",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"cancelled\": true\n }\n}",
"headers": {
- "Content-Type": "text/event-stream"
+ "Content-Type": "application/json"
}
},
- "uuid": "a9a97526-65c8-44f5-8bdf-7b346e329612",
+ "uuid": "0854ebc2-2391-447e-ab99-9067191ca609",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3692,30 +3523,33 @@
}
},
{
- "id": "d6ae9792-3284-4584-b16b-2fab02164aae",
- "name": "Get Voices - default",
+ "id": "83fb15ac-e235-47f5-9752-b1c3ef424a5a",
+ "name": "Start a test call against a branch - default",
"request": {
- "urlPathTemplate": "/waves/v1/{model}/get_voices",
- "method": "GET",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/test-call",
+ "method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
},
"pathParameters": {
- "model": {
- "equalTo": "lightning-v3.1"
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
}
}
},
"response": {
"status": 200,
- "body": "{\n \"voices\": [\n {\n \"voiceId\": \"voiceId\",\n \"displayName\": \"displayName\"\n }\n ]\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"conversationId\": \"conversationId\",\n \"callId\": \"callId\",\n \"token\": \"token\",\n \"roomName\": \"roomName\",\n \"host\": \"host\"\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "d6ae9792-3284-4584-b16b-2fab02164aae",
+ "uuid": "83fb15ac-e235-47f5-9752-b1c3ef424a5a",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3728,25 +3562,33 @@
}
},
{
- "id": "70067508-7499-413e-a2f8-f646931bc3e2",
- "name": "Add your Voice (Deprecated) - default",
+ "id": "340854e2-87f1-44c9-96a0-410ea3da2b2a",
+ "name": "List revisions on a branch - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-large/add_voice",
- "method": "POST",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/revisions",
+ "method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"message\": \"message\",\n \"data\": {\n \"voiceId\": \"voiceId\",\n \"model\": \"model\",\n \"status\": \"status\"\n }\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"revisions\": [\n {}\n ],\n \"total\": 1\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "70067508-7499-413e-a2f8-f646931bc3e2",
+ "uuid": "340854e2-87f1-44c9-96a0-410ea3da2b2a",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3759,25 +3601,36 @@
}
},
{
- "id": "fb9b09ba-fd73-4dc0-b53a-c9be394e795b",
- "name": "Get your cloned Voices (Deprecated) - default",
+ "id": "63471a9e-d72a-4d63-a4d3-fb7ccf8cb83d",
+ "name": "Get revision - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-large/get_cloned_voices",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/revisions/{revisionId}",
"method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ },
+ "revisionId": {
+ "equalTo": "revisionId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"voices\": [\n {\n \"displayName\": \"displayName\",\n \"accent\": \"accent\",\n \"tags\": [\n \"tags\"\n ],\n \"voiceId\": \"voiceId\",\n \"model\": \"model\",\n \"status\": \"status\",\n \"createdAt\": \"2024-01-15T09:30:00Z\"\n }\n ]\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"revision\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"status\": \"published\",\n \"branch\": \"branch\",\n \"revisionNumber\": 1,\n \"versionNumber\": 1,\n \"label\": \"label\",\n \"description\": \"description\",\n \"isPinned\": true,\n \"publishedBy\": \"publishedBy\",\n \"publishedAt\": \"2024-01-15T09:30:00Z\",\n \"publishedByName\": \"publishedByName\",\n \"restoredFromLabel\": \"restoredFromLabel\",\n \"draftId\": \"draftId\",\n \"draftName\": \"draftName\",\n \"draftRevision\": 1,\n \"sourceVersionId\": \"sourceVersionId\",\n \"workflowType\": \"workflowType\",\n \"parentVersion\": \"parentVersion\",\n \"isActive\": true,\n \"activatedBy\": \"activatedBy\",\n \"activatedAt\": \"2024-01-15T09:30:00Z\",\n \"restoredFromRevisionId\": \"restoredFromRevisionId\",\n \"sourceDraftId\": \"sourceDraftId\",\n \"promptScoreStale\": true,\n \"createdBy\": \"createdBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"resolvedConfig\": {\n \"key\": \"value\"\n }\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "fb9b09ba-fd73-4dc0-b53a-c9be394e795b",
+ "uuid": "63471a9e-d72a-4d63-a4d3-fb7ccf8cb83d",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3787,29 +3640,39 @@
"via": "SYSTEM"
}
}
- },
- "postServeActions": []
+ }
},
{
- "id": "48873c09-b04d-4590-a674-e14ae9e5a580",
- "name": "Delete a Voice Clone (Deprecated) - default",
+ "id": "ddf3db6f-7be7-41b6-a4c5-def01cd66158",
+ "name": "Get revision publish trail - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-large",
- "method": "DELETE",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/revisions/{revisionId}/history",
+ "method": "GET",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ },
+ "revisionId": {
+ "equalTo": "revisionId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"success\": true,\n \"voiceId\": \"voiceId\"\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"revision\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"status\": \"published\",\n \"branch\": \"branch\",\n \"revisionNumber\": 1,\n \"versionNumber\": 1,\n \"label\": \"label\",\n \"description\": \"description\",\n \"isPinned\": true,\n \"publishedBy\": \"publishedBy\",\n \"publishedAt\": \"2024-01-15T09:30:00Z\",\n \"publishedByName\": \"publishedByName\",\n \"restoredFromLabel\": \"restoredFromLabel\",\n \"draftId\": \"draftId\",\n \"draftName\": \"draftName\",\n \"draftRevision\": 1,\n \"sourceVersionId\": \"sourceVersionId\",\n \"workflowType\": \"workflowType\",\n \"parentVersion\": \"parentVersion\",\n \"isActive\": true,\n \"activatedBy\": \"activatedBy\",\n \"activatedAt\": \"2024-01-15T09:30:00Z\",\n \"restoredFromRevisionId\": \"restoredFromRevisionId\",\n \"sourceDraftId\": \"sourceDraftId\",\n \"promptScoreStale\": true,\n \"createdBy\": \"createdBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n },\n \"publishedBy\": \"publishedBy\",\n \"trail\": [\n {}\n ]\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "48873c09-b04d-4590-a674-e14ae9e5a580",
+ "uuid": "ddf3db6f-7be7-41b6-a4c5-def01cd66158",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3822,25 +3685,36 @@
}
},
{
- "id": "282d8128-9980-4589-bbd3-5ea80c0c20e1",
- "name": "Synthesize speech - default",
+ "id": "dcf5fea3-9a5b-400f-9f98-6b736ab69894",
+ "name": "Restore a revision - default",
"request": {
- "urlPathTemplate": "/waves/v1/tts",
+ "urlPathTemplate": "/agent/{id}/branches/{branchId}/revisions/{revisionId}/restore",
"method": "POST",
"headers": {
"Authorization": {
"matches": "Bearer .+"
}
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ },
+ "branchId": {
+ "equalTo": "branchId"
+ },
+ "revisionId": {
+ "equalTo": "revisionId"
+ }
}
},
"response": {
"status": 200,
- "body": "{\n \"key\": \"value\"\n}",
+ "body": "{\n \"status\": true,\n \"data\": {\n \"state\": \"scanning\",\n \"revision\": {\n \"_id\": \"_id\",\n \"agent\": \"agent\",\n \"status\": \"published\",\n \"branch\": \"branch\",\n \"revisionNumber\": 1,\n \"versionNumber\": 1,\n \"label\": \"label\",\n \"description\": \"description\",\n \"isPinned\": true,\n \"publishedBy\": \"publishedBy\",\n \"publishedAt\": \"2024-01-15T09:30:00Z\",\n \"publishedByName\": \"publishedByName\",\n \"restoredFromLabel\": \"restoredFromLabel\",\n \"draftId\": \"draftId\",\n \"draftName\": \"draftName\",\n \"draftRevision\": 1,\n \"sourceVersionId\": \"sourceVersionId\",\n \"workflowType\": \"workflowType\",\n \"parentVersion\": \"parentVersion\",\n \"isActive\": true,\n \"activatedBy\": \"activatedBy\",\n \"activatedAt\": \"2024-01-15T09:30:00Z\",\n \"restoredFromRevisionId\": \"restoredFromRevisionId\",\n \"sourceDraftId\": \"sourceDraftId\",\n \"promptScoreStale\": true,\n \"createdBy\": \"createdBy\",\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n }\n}",
"headers": {
"Content-Type": "application/json"
}
},
- "uuid": "282d8128-9980-4589-bbd3-5ea80c0c20e1",
+ "uuid": "dcf5fea3-9a5b-400f-9f98-6b736ab69894",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3853,11 +3727,1110 @@
}
},
{
- "id": "7f3f9d5d-9d57-42cf-801a-9188a8d88c1b",
- "name": "Stream speech (SSE) - default",
+ "id": "71ad59b3-a207-4405-81d2-5b7c6096e9c4",
+ "name": "Diff two revisions or drafts - default",
"request": {
- "urlPathTemplate": "/waves/v1/tts/live",
- "method": "POST",
+ "urlPathTemplate": "/agent/{id}/diff",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ }
+ },
+ "queryParameters": {
+ "a": {
+ "equalTo": "a"
+ },
+ "b": {
+ "equalTo": "b"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"status\": true,\n \"data\": {\n \"unchangedSections\": [\n \"unchangedSections\"\n ],\n \"diffs\": [\n {}\n ]\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "71ad59b3-a207-4405-81d2-5b7c6096e9c4",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "a0c78fe6-f292-4dfb-8a8d-4adcd247f241",
+ "name": "Score a prompt - default",
+ "request": {
+ "urlPathTemplate": "/prompt-scoring/score",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"status\": true,\n \"data\": {\n \"overall_score\": 56,\n \"overall_grade\": \"Needs Work\",\n \"band\": \"lean\",\n \"estimated_ttft_overhead_ms\": 12.9,\n \"dimensions\": [\n {\n \"tier\": 1,\n \"level\": \"Adequate\",\n \"evidence_span\": \"You are a friendly and helpful weather assistant. Your role is to provide accurate, real-time weather information to users.\",\n \"title\": \"Clear but basic role definition\",\n \"description\": \"The role is clearly defined but lacks specific success criteria or scope boundaries.\"\n },\n {\n \"tier\": 1,\n \"level\": \"Weak\",\n \"evidence_span\": \"Use the get_weather function to fetch real-time data\",\n \"title\": \"Undeclared tool reference\",\n \"description\": \"The 'get_weather' tool is referenced but not defined, and failure paths are missing.\"\n },\n {\n \"tier\": 2,\n \"level\": \"Missing\",\n \"evidence_span\": \"no relevant content found\",\n \"title\": \"No failure mode coverage\",\n \"description\": \"The prompt contains no instructions for handling errors, tool failures, or unclear user input.\"\n }\n ]\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "a0c78fe6-f292-4dfb-8a8d-4adcd247f241",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "17528ba0-8c57-4113-b903-e9562b906e3c",
+ "name": "List call records - default",
+ "request": {
+ "urlPathTemplate": "/analytics/call-counts-log",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"calls\": [\n {}\n ],\n \"totalCalls\": 1.1,\n \"totalPages\": 1.1,\n \"currentPage\": 1.1,\n \"limit\": 1.1\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "17528ba0-8c57-4113-b903-e9562b906e3c",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "44c3afc9-1bcb-4e37-872b-7b4ead15acc9",
+ "name": "Call counts by day - default",
+ "request": {
+ "urlPathTemplate": "/analytics/call-counts-by-day",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"callCountsByDay\": [\n {}\n ]\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "44c3afc9-1bcb-4e37-872b-7b4ead15acc9",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "16b34a40-1115-4568-a69e-add4d8c2ed4b",
+ "name": "Get conversation details - default",
+ "request": {
+ "urlPathTemplate": "/analytics/conversation-details/{callId}",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "pathParameters": {
+ "callId": {
+ "equalTo": "callId"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"callId\": \"callId\",\n \"agentName\": \"agentName\",\n \"workflowType\": \"workflowType\",\n \"transcript\": [\n {}\n ],\n \"events\": [\n {}\n ],\n \"fromNumber\": \"fromNumber\",\n \"toNumber\": \"toNumber\",\n \"callDurationMs\": 1.1\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "16b34a40-1115-4568-a69e-add4d8c2ed4b",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "01bb585e-df63-4031-939d-d85bd0a6b45f",
+ "name": "Credit usage timeseries - default",
+ "request": {
+ "urlPathTemplate": "/analytics/usage/timeseries",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"dateRange\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n },\n \"dayWiseCredits\": [\n {}\n ],\n \"totalCredits\": 1.1\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "01bb585e-df63-4031-939d-d85bd0a6b45f",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "af4367df-67de-4f64-8a22-85797af130cc",
+ "name": "Full dashboard data - default",
+ "request": {
+ "urlPathTemplate": "/analytics/dashboard",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"summary\": {\n \"key\": \"value\"\n },\n \"callVolumeTimeseries\": {\n \"key\": \"value\"\n },\n \"callOutcomesTimeseries\": {\n \"key\": \"value\"\n },\n \"pickupRateByNumber\": {\n \"key\": \"value\"\n },\n \"hourlyPerformance\": {\n \"key\": \"value\"\n },\n \"durationStats\": {\n \"key\": \"value\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "af4367df-67de-4f64-8a22-85797af130cc",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "cd7a6dc3-a47a-4687-b5e8-5085df2aaeb6",
+ "name": "Dashboard summary KPIs - default",
+ "request": {
+ "urlPathTemplate": "/analytics/summary",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"totalCalls\": {\n \"current\": 1.1,\n \"previous\": 1.1,\n \"percentChange\": 1.1\n },\n \"pickupRate\": {\n \"current\": 1.1,\n \"previous\": 1.1,\n \"percentChange\": 1.1\n },\n \"avgDurationMs\": {\n \"current\": 1.1,\n \"previous\": 1.1,\n \"percentChange\": 1.1\n },\n \"medianDurationMs\": 1.1,\n \"totalDurationMs\": {\n \"current\": 1.1,\n \"previous\": 1.1,\n \"percentChange\": 1.1\n },\n \"uniqueUsersReached\": {\n \"current\": 1.1,\n \"previous\": 1.1,\n \"percentChange\": 1.1\n },\n \"totalCost\": {\n \"current\": 1.1,\n \"previous\": 1.1,\n \"percentChange\": 1.1\n },\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n },\n \"previousPeriod\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "cd7a6dc3-a47a-4687-b5e8-5085df2aaeb6",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "87bfa6c6-d269-43f4-910d-b8daacfe2309",
+ "name": "Call volume timeseries - default",
+ "request": {
+ "urlPathTemplate": "/analytics/call-volume-timeseries",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"dataPoints\": [\n {}\n ],\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "87bfa6c6-d269-43f4-910d-b8daacfe2309",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "cabf5bda-63a4-4055-be75-974a5c176cdb",
+ "name": "Pickup rate by phone number - default",
+ "request": {
+ "urlPathTemplate": "/analytics/pickup-rate-by-number",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"numbers\": [\n {}\n ],\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "cabf5bda-63a4-4055-be75-974a5c176cdb",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "88508c66-a9be-4e84-8178-60c6dd93f6dc",
+ "name": "Phone number trends - default",
+ "request": {
+ "urlPathTemplate": "/analytics/phone-number-trends",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"trends\": [\n {}\n ],\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "88508c66-a9be-4e84-8178-60c6dd93f6dc",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "a760a1f7-167d-4010-8361-bc0a6bf0c8ae",
+ "name": "Hourly performance - default",
+ "request": {
+ "urlPathTemplate": "/analytics/hourly-performance",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"hours\": [\n {}\n ],\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "a760a1f7-167d-4010-8361-bc0a6bf0c8ae",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "95d92802-41db-4330-a96c-bcbcbec057cd",
+ "name": "Call outcomes timeseries - default",
+ "request": {
+ "urlPathTemplate": "/analytics/call-outcomes-timeseries",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"dataPoints\": [\n {}\n ],\n \"totals\": {\n \"answered\": 1.1,\n \"noAnswer\": 1.1,\n \"failed\": 1.1,\n \"cancelled\": 1.1,\n \"total\": 1.1\n },\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "95d92802-41db-4330-a96c-bcbcbec057cd",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "23d56d91-cb25-4509-a411-323c9ce8797d",
+ "name": "Call duration statistics - default",
+ "request": {
+ "urlPathTemplate": "/analytics/duration-stats",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"avgDurationMs\": 1.1,\n \"medianDurationMs\": 1.1,\n \"p90DurationMs\": 1.1,\n \"p95DurationMs\": 1.1,\n \"shortCallsPercent\": 1.1,\n \"longCallsPercent\": 1.1,\n \"totalCalls\": 1.1,\n \"totalDurationMs\": 1.1,\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "23d56d91-cb25-4509-a411-323c9ce8797d",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "8abd879f-8d27-466d-8d1a-7bd8011c22bb",
+ "name": "Weekly call trends - default",
+ "request": {
+ "urlPathTemplate": "/analytics/weekly-trends",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"weeks\": [\n {}\n ],\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "8abd879f-8d27-466d-8d1a-7bd8011c22bb",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "05f5fb00-5f49-48dd-9026-05c7492c6716",
+ "name": "Agent performance - default",
+ "request": {
+ "urlPathTemplate": "/analytics/agent-performance",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"agents\": [\n {}\n ],\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "05f5fb00-5f49-48dd-9026-05c7492c6716",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "3bb1e74a-a753-43e7-be62-66a906316a68",
+ "name": "Concurrent call counts - default",
+ "request": {
+ "urlPathTemplate": "/analytics/concurrency",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "queryParameters": {
+ "date": {
+ "equalTo": "2023-01-15"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"date\": \"2023-01-15\",\n \"dataPoints\": [\n {}\n ],\n \"peakConcurrent\": 1.1,\n \"avgConcurrent\": 1.1,\n \"totalMinutes\": 1.1,\n \"byAgent\": [\n {}\n ]\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "3bb1e74a-a753-43e7-be62-66a906316a68",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "ea5c6908-8ddf-4ff7-954e-3fa39881c045",
+ "name": "Call start distribution - default",
+ "request": {
+ "urlPathTemplate": "/analytics/call-start-distribution",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "queryParameters": {
+ "date": {
+ "equalTo": "2023-01-15"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"date\": \"2023-01-15\",\n \"dataPoints\": [\n {}\n ],\n \"totalCalls\": 1.1\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "ea5c6908-8ddf-4ff7-954e-3fa39881c045",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "e60a862a-e4f7-4734-b450-701ed196edd9",
+ "name": "Daily call summary - default",
+ "request": {
+ "urlPathTemplate": "/analytics/daily-call-summary",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "queryParameters": {
+ "date": {
+ "equalTo": "2023-01-15"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"date\": \"2023-01-15\",\n \"totalCalls\": 1.1,\n \"answeredCalls\": 1.1,\n \"unansweredCalls\": 1.1,\n \"failedCalls\": 1.1,\n \"pickupRate\": 1.1,\n \"inProgressCalls\": 1.1,\n \"inQueueCalls\": 1.1,\n \"totalDurationMs\": 1.1\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "e60a862a-e4f7-4734-b450-701ed196edd9",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "802a40f0-2fd5-4a9f-b0f3-dc30603aeee2",
+ "name": "Attempt cohort analysis - default",
+ "request": {
+ "urlPathTemplate": "/analytics/attempt-cohort",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgId\": \"orgId\",\n \"cohorts\": [\n {}\n ],\n \"totalUniqueNumbers\": 1.1,\n \"maxAttemptsSeen\": 1.1,\n \"avgAttemptsToPickup\": 1.1,\n \"period\": {\n \"from\": \"2023-01-15\",\n \"to\": \"2023-01-15\"\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "802a40f0-2fd5-4a9f-b0f3-dc30603aeee2",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "ed84a9b0-9db6-4150-ab53-3c7f20e022af",
+ "name": "List call actions - default",
+ "request": {
+ "urlPathTemplate": "/call-actions",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "queryParameters": {
+ "agentId": {
+ "equalTo": "agentId"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"callActions\": [\n {}\n ],\n \"totalCount\": 1.1,\n \"page\": 1.1,\n \"limit\": 1.1\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "ed84a9b0-9db6-4150-ab53-3c7f20e022af",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "7a565f20-c86d-4f37-b81b-cd880b01ef4a",
+ "name": "Create a call action - default",
+ "request": {
+ "urlPathTemplate": "/call-actions",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 201,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"organization\": \"organization\",\n \"agentId\": \"agentId\",\n \"category\": \"trigger\",\n \"provider\": \"provider\",\n \"action_type\": \"create\",\n \"object\": \"object\",\n \"config\": {\n \"phoneNumberFieldName\": \"phoneNumberFieldName\",\n \"conditions\": [\n {\n \"field\": \"field\",\n \"operator\": \"eq\",\n \"value\": \"value\"\n }\n ],\n \"body\": \"body\"\n },\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "7a565f20-c86d-4f37-b81b-cd880b01ef4a",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "e83872f8-3e8b-4ccb-a64c-5657aa60633d",
+ "name": "Get a call action - default",
+ "request": {
+ "urlPathTemplate": "/call-actions/{id}",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"organization\": \"organization\",\n \"agentId\": \"agentId\",\n \"category\": \"trigger\",\n \"provider\": \"provider\",\n \"action_type\": \"create\",\n \"object\": \"object\",\n \"config\": {\n \"phoneNumberFieldName\": \"phoneNumberFieldName\",\n \"conditions\": [\n {\n \"field\": \"field\",\n \"operator\": \"eq\",\n \"value\": \"value\"\n }\n ],\n \"body\": \"body\"\n },\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "e83872f8-3e8b-4ccb-a64c-5657aa60633d",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "f6025a6e-aa60-4430-b4cd-d2c3621bef20",
+ "name": "Update a call action - default",
+ "request": {
+ "urlPathTemplate": "/call-actions/{id}",
+ "method": "PUT",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"_id\": \"_id\",\n \"organization\": \"organization\",\n \"agentId\": \"agentId\",\n \"category\": \"trigger\",\n \"provider\": \"provider\",\n \"action_type\": \"create\",\n \"object\": \"object\",\n \"config\": {\n \"phoneNumberFieldName\": \"phoneNumberFieldName\",\n \"conditions\": [\n {\n \"field\": \"field\",\n \"operator\": \"eq\",\n \"value\": \"value\"\n }\n ],\n \"body\": \"body\"\n },\n \"createdAt\": \"2024-01-15T09:30:00Z\",\n \"updatedAt\": \"2024-01-15T09:30:00Z\"\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "f6025a6e-aa60-4430-b4cd-d2c3621bef20",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "163f7034-7312-4049-9872-2bc209d34f76",
+ "name": "Delete a call action - default",
+ "request": {
+ "urlPathTemplate": "/call-actions/{id}",
+ "method": "DELETE",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "pathParameters": {
+ "id": {
+ "equalTo": "id"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"message\": \"Call action deleted successfully\"\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "163f7034-7312-4049-9872-2bc209d34f76",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "8902d6c5-e12e-4c9f-ac6d-e6ac9c95c7a8",
+ "name": "Create or update WebEngage integration - default",
+ "request": {
+ "urlPathTemplate": "/integration/modify-webengage-integration",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"message\": \"Integration created successfully\",\n \"data\": {\n \"key\": \"value\"\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "8902d6c5-e12e-4c9f-ac6d-e6ac9c95c7a8",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "1d880580-66fd-41c1-9b8d-9c96a8d6ccc8",
+ "name": "Get WebEngage integration details - default",
+ "request": {
+ "urlPathTemplate": "/integration/get-webengage-details",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"message\": \"Integration details retrieved successfully\",\n \"data\": {\n \"key\": \"value\"\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "1d880580-66fd-41c1-9b8d-9c96a8d6ccc8",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "d679ddd0-68da-4eb1-8df8-b4a43acc63a7",
+ "name": "Get concurrency limits - default",
+ "request": {
+ "urlPathTemplate": "/concurrency",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"orgLimit\": 1.1,\n \"totalReserved\": 1.1,\n \"unreservedPool\": 1.1,\n \"agents\": [\n {}\n ]\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "d679ddd0-68da-4eb1-8df8-b4a43acc63a7",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "7aaeab46-63ab-4926-8a0b-e04f6e2c68d8",
+ "name": "Update concurrency reservations - default",
+ "request": {
+ "urlPathTemplate": "/concurrency/reservations",
+ "method": "PUT",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": {\n \"updated\": 1.1\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "7aaeab46-63ab-4926-8a0b-e04f6e2c68d8",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "ca88fcaf-e959-4688-b984-ac97564ae4b3",
+ "name": "List disposition metric templates - default",
+ "request": {
+ "urlPathTemplate": "/disposition-metric-templates",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"success\": true,\n \"data\": [\n {\n \"identifier\": \"call_resolved\",\n \"dispositionMetricPrompt\": \"dispositionMetricPrompt\",\n \"dispositionMetricType\": \"STRING\"\n }\n ]\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "ca88fcaf-e959-4688-b984-ac97564ae4b3",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "f4e9658c-d989-4ba4-b1c5-cadf2a257164",
+ "name": "List DNC entries for the organization - default",
+ "request": {
+ "urlPathTemplate": "/dnc",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"status\": true,\n \"data\": {\n \"entries\": [\n {}\n ],\n \"pagination\": {\n \"page\": 1,\n \"offset\": 1,\n \"total\": 1,\n \"totalPages\": 1,\n \"hasMore\": true\n }\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "f4e9658c-d989-4ba4-b1c5-cadf2a257164",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "23284c9e-d15e-4f3a-84b7-de9613ccf399",
+ "name": "Get pronunciation dictionaries - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/pronunciation-dicts",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "[\n {\n \"id\": \"64f1234567890abcdef12345\",\n \"items\": [\n {\n \"word\": \"mysql\",\n \"pronunciation\": \"my-sequel\"\n }\n ],\n \"createdAt\": \"2023-09-01T12:00:00Z\"\n }\n]",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "23284c9e-d15e-4f3a-84b7-de9613ccf399",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ },
+ "postServeActions": []
+ },
+ {
+ "id": "2bda2b48-3814-4298-b92c-dad37f8c7a76",
+ "name": "Create pronunciation dictionary - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/pronunciation-dicts",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"id\": \"64f1234567890abcdef12345\",\n \"items\": [\n {\n \"word\": \"mysql\",\n \"pronunciation\": \"my-sequel\"\n }\n ],\n \"createdAt\": \"2023-09-01T12:00:00Z\"\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "2bda2b48-3814-4298-b92c-dad37f8c7a76",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "d2a666c0-9796-4eeb-bcff-8a039794cdef",
+ "name": "Update pronunciation dictionary - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/pronunciation-dicts",
+ "method": "PUT",
"headers": {
"Authorization": {
"matches": "Bearer .+"
@@ -3866,12 +4839,234 @@
},
"response": {
"status": 200,
- "body": "event: message\ndata: \"event: audio\\ndata: {\\\"audio\\\": \\\"\\\"}\\n\\ndata: {\\\"done\\\": true}\\n\"\n",
+ "body": "{\n \"id\": \"64f1234567890abcdef12345\",\n \"items\": [\n {\n \"word\": \"mysql\",\n \"pronunciation\": \"my-sequel\"\n },\n {\n \"word\": \"goodbye\",\n \"pronunciation\": \"goodbai\"\n }\n ]\n}",
"headers": {
- "Content-Type": "text/event-stream"
+ "Content-Type": "application/json"
}
},
- "uuid": "7f3f9d5d-9d57-42cf-801a-9188a8d88c1b",
+ "uuid": "d2a666c0-9796-4eeb-bcff-8a039794cdef",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "e8661638-733d-4a3c-9f3e-2653ca6aa6c2",
+ "name": "Delete pronunciation dictionary - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/pronunciation-dicts",
+ "method": "DELETE",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"id\": \"64f1234567890abcdef12345\",\n \"deleted\": true\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "e8661638-733d-4a3c-9f3e-2653ca6aa6c2",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "5500f660-6aa4-448a-8957-ab600545e121",
+ "name": "Text to speech - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/lightning/get_speech",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "\"\"",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "5500f660-6aa4-448a-8957-ab600545e121",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "5d2a1219-21eb-4b3a-93ed-adfb791795c4",
+ "name": "Text to Speech (Deprecated) - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/lightning-large/get_speech",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "\"\"",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "5d2a1219-21eb-4b3a-93ed-adfb791795c4",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "f88779d9-9c06-4e6b-91c0-a066bcf5d281",
+ "name": "Text to Speech (SSE) (Deprecated) - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/lightning-large/stream",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "\"\"",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "f88779d9-9c06-4e6b-91c0-a066bcf5d281",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "cf20eee5-9ffc-407a-82e7-0fd54d9c7143",
+ "name": "Text to Speech (Deprecated) - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/lightning-v2/get_speech",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "\"\"",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "cf20eee5-9ffc-407a-82e7-0fd54d9c7143",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "a9a97526-65c8-44f5-8bdf-7b346e329612",
+ "name": "Text to Speech (SSE) (Deprecated) - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/lightning-v2/stream",
+ "method": "POST",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "\"\"",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "a9a97526-65c8-44f5-8bdf-7b346e329612",
+ "persistent": true,
+ "priority": 3,
+ "metadata": {
+ "mocklab": {
+ "created": {
+ "at": "2020-01-01T00:00:00.000Z",
+ "via": "SYSTEM"
+ }
+ }
+ }
+ },
+ {
+ "id": "d6ae9792-3284-4584-b16b-2fab02164aae",
+ "name": "Get Voices - default",
+ "request": {
+ "urlPathTemplate": "/waves/v1/{model}/get_voices",
+ "method": "GET",
+ "headers": {
+ "Authorization": {
+ "matches": "Bearer .+"
+ }
+ },
+ "pathParameters": {
+ "model": {
+ "equalTo": "lightning-v3.1"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"voices\": [\n {\n \"voiceId\": \"avery\",\n \"displayName\": \"Avery\",\n \"tags\": {\n \"language\": [\n \"english\"\n ],\n \"accent\": \"american\",\n \"gender\": \"female\",\n \"age\": \"young\",\n \"emotions\": [\n \"emotions\"\n ],\n \"usecases\": [\n \"conversational\"\n ]\n }\n },\n {\n \"voiceId\": \"liam\",\n \"displayName\": \"Liam\",\n \"tags\": {\n \"language\": [\n \"english\"\n ],\n \"accent\": \"british\",\n \"gender\": \"male\",\n \"age\": \"young\",\n \"emotions\": [\n \"emotions\"\n ],\n \"usecases\": [\n \"conversational\"\n ]\n }\n },\n {\n \"voiceId\": \"devansh\",\n \"displayName\": \"Devansh\",\n \"tags\": {\n \"language\": [\n \"hindi\",\n \"english\"\n ],\n \"accent\": \"indian\",\n \"gender\": \"male\",\n \"age\": \"young\",\n \"emotions\": [\n \"emotions\"\n ],\n \"usecases\": [\n \"conversational\"\n ]\n }\n }\n ]\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ },
+ "uuid": "d6ae9792-3284-4584-b16b-2fab02164aae",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3884,10 +5079,10 @@
}
},
{
- "id": "d81cf6f0-55d5-4181-8a33-79bb51c9ea42",
- "name": "Generate speech (Lightning v3.1, Endpoint Deprecated) - default",
+ "id": "282d8128-9980-4589-bbd3-5ea80c0c20e1",
+ "name": "Synthesize speech - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-v3.1/get_speech",
+ "urlPathTemplate": "/waves/v1/tts",
"method": "POST",
"headers": {
"Authorization": {
@@ -3902,7 +5097,7 @@
"Content-Type": "application/json"
}
},
- "uuid": "d81cf6f0-55d5-4181-8a33-79bb51c9ea42",
+ "uuid": "282d8128-9980-4589-bbd3-5ea80c0c20e1",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3915,10 +5110,10 @@
}
},
{
- "id": "6d332fd5-07f6-40ed-81e7-5650ec38135e",
- "name": "Stream speech (Lightning v3.1 SSE, Endpoint Deprecated) - default",
+ "id": "7f3f9d5d-9d57-42cf-801a-9188a8d88c1b",
+ "name": "Stream speech (SSE) - default",
"request": {
- "urlPathTemplate": "/waves/v1/lightning-v3.1/stream",
+ "urlPathTemplate": "/waves/v1/tts/live",
"method": "POST",
"headers": {
"Authorization": {
@@ -3928,12 +5123,12 @@
},
"response": {
"status": 200,
- "body": "event: message\ndata: {\"data\":\"event: chunk\\ndata: \\ndone: false\\n\"}\n",
+ "body": "event: message\ndata: \"event: audio\\ndata: {\\\"audio\\\": \\\"\\\"}\\n\\ndata: {\\\"done\\\": true}\\n\"\n",
"headers": {
"Content-Type": "text/event-stream"
}
},
- "uuid": "6d332fd5-07f6-40ed-81e7-5650ec38135e",
+ "uuid": "7f3f9d5d-9d57-42cf-801a-9188a8d88c1b",
"persistent": true,
"priority": 3,
"metadata": {
@@ -3947,6 +5142,6 @@
}
],
"meta": {
- "total": 113
+ "total": 146
}
}
\ No newline at end of file
From 0e215314e700266fb0c807a50d819147f20f26eb Mon Sep 17 00:00:00 2001
From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com>
Date: Fri, 24 Jul 2026 03:08:05 +0000
Subject: [PATCH 2/3] [fern-replay] Applied customizations
Patches applied (1):
- patch-0c9e9b4e: refactor: rename agent-swarm -> agent-crew across SDK + CLI (4.4.2)
---
.fern/replay.lock | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/.fern/replay.lock b/.fern/replay.lock
index 6808e07a..991221ad 100644
--- a/.fern/replay.lock
+++ b/.fern/replay.lock
@@ -12,14 +12,20 @@ generations:
cli_version: unknown
generator_versions:
fernapi/fern-python-sdk: 5.12.12
-current_generation: e6df8b56a0372564733a88670e0762c88504e06f
+ - commit_sha: ae36558fb1b1fc953760c7d5064d4c7e523c6113
+ tree_hash: e37787dfe12036547d6ecdf15c8c222a137422c1
+ timestamp: 2026-07-24T03:08:01.112Z
+ cli_version: unknown
+ generator_versions:
+ fernapi/fern-python-sdk: 5.12.12
+current_generation: ae36558fb1b1fc953760c7d5064d4c7e523c6113
patches:
- id: patch-0c9e9b4e
content_hash: sha256:43cbb1c853a3de0a20a4d1e151169f4039eec0f4d998b32978cb5038f2a3b665
original_commit: 0c9e9b4e2286bd64653a1bdeb91a2b871a56668e
original_message: "refactor: rename agent-swarm -> agent-crew across SDK + CLI (4.4.2)"
original_author: Maharshi Chattopadhyay
- base_generation: e6df8b56a0372564733a88670e0762c88504e06f
+ base_generation: ae36558fb1b1fc953760c7d5064d4c7e523c6113
files:
- src/smallestai/atoms/swarm/__init__.py
- src/smallestai/atoms/swarm/clients/__init__.py
From 5fa3e5ed797c19c4efd1be15ec4b2071991afcba Mon Sep 17 00:00:00 2001
From: Abhishek Mishra
Date: Fri, 24 Jul 2026 08:46:02 +0530
Subject: [PATCH 3/3] feat(5.2.0): agent versioning v2 helper + waves
unified-tts + [8] init cleanup
On top of the Fern regen (dnc restored via docs #315):
- Versioning helper (smallestai.atoms.helpers.Versioning): publish_and_wait / wait_for_commit
(publish runs an async security scan -> list-then-poll), edit_and_publish (one-call config edit),
update_draft with a typed 409 discriminator -> MigrationRequiredError / DraftConflictError
(handles dev-rich data.conflict AND prod-lean errors[]) / BaseRevisionUnavailableError.
- Wire Versioning into atoms.helpers; add unit test + live E2E + verbose API probe + cookbook example.
- Waves: adopt the unified-tts client (regen-owned again; the old streaming/SampleRate override is
obsolete since the unified tts client streams bytes natively). Drop removed lightning_v2tts/v31tts
refs from the waves shim __init__.
- [8]: exclude_types_from_init_exports=false -> remove the __init__ re-export stopgaps, use native exports.
- Bump 5.1.1 -> 5.2.0 + changelog.
Verified live on api.smallest.ai: versioning e2e 15/15, dnc.list, waves synth; mypy clean, verify passed, pytest 245 passed.
---
.fernignore | 18 +-
changelog.md | 22 +
examples/agent_versioning_lifecycle.py | 83 +
pyproject.toml | 2 +-
src/smallestai/atoms/__init__.py | 1775 ++++++-
src/smallestai/atoms/helpers/__init__.py | 25 +-
src/smallestai/atoms/helpers/versioning.py | 234 +
.../atoms/prompt_scoring/__init__.py | 63 +-
src/smallestai/atoms/webhooks/__init__.py | 71 +-
src/smallestai/waves/__init__.py | 10 +-
src/smallestai/waves/client.py | 3082 ++++--------
src/smallestai/waves/raw_client.py | 4273 ++++-------------
tests/custom/test_versioning_helper.py | 74 +
tests/velocity/v2_versioning_api_probe.py | 190 +
tests/velocity/v2_versioning_e2e.py | 153 +
15 files changed, 4484 insertions(+), 5591 deletions(-)
create mode 100644 examples/agent_versioning_lifecycle.py
create mode 100644 src/smallestai/atoms/helpers/versioning.py
create mode 100644 tests/custom/test_versioning_helper.py
create mode 100644 tests/velocity/v2_versioning_api_probe.py
create mode 100644 tests/velocity/v2_versioning_e2e.py
diff --git a/.fernignore b/.fernignore
index 2368fec8..ca415b58 100644
--- a/.fernignore
+++ b/.fernignore
@@ -13,17 +13,15 @@ src/smallestai/waves/stream_tts.py
src/smallestai/waves/types/sample_rate.py
# Files with non-IR-expressible customizations
-# (shim block in waves/__init__.py; SampleRate type override in client.py/raw_client.py)
+# (shim block in waves/__init__.py lazy-loads the WavesStreamingTTS compat shim).
+# client.py/raw_client.py are now regen-owned: the unified `tts` client streams bytes
+# natively (verified 282KB audio on prod), so the old SampleRate/streaming override is
+# obsolete. Dropped from .fernignore in 5.2.0 so future regens keep them current.
src/smallestai/waves/__init__.py
-src/smallestai/waves/client.py
-src/smallestai/waves/raw_client.py
-
-# Sub-package __init__ type re-exports: generator 5.12.12 strips sub-package type
-# exports (exclude_types_from_init_exports), but generated wire tests import these
-# types from the package root. Preserve the re-exports. See SDK_ESCALATIONS.log [1].
-src/smallestai/atoms/webhooks/__init__.py
-src/smallestai/atoms/prompt_scoring/__init__.py
-src/smallestai/atoms/__init__.py
+
+# (Sub-package __init__ stopgaps removed in 5.2.0: exclude_types_from_init_exports
+# was flipped to false, so the generator now exports these types natively and the
+# hand-written re-export stopgaps are obsolete. See SDK_ESCALATIONS.log [8].)
# Customer-owned CI / release automation
.github/workflows/**
diff --git a/changelog.md b/changelog.md
index c10d02e2..20d865d4 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,25 @@
+## 5.2.0 - 2026-07-24
+
+Agent Versioning v2 (branch/revision API) + new namespaces, plus an ergonomic versioning helper.
+Additive; generator pinned to 5.12.12.
+
+* **Agent Versioning v2** โ new `agent_versioning_branches` (`create_branch`, `list`, `get`,
+ `rename`, `archive`, `make_live`, `get_draft`, `update_draft`, `discard_draft`, `publish_draft`,
+ `cancel_publish`, `test_call`) and `agent_versioning_revisions` (`list`, `get`, `get_history`,
+ `restore`, `diff`) clients. `update_draft` takes typed config kwargs (`global_prompt`,
+ `first_message`, `synthesizer`, โฆ). The v1 `agent_versioning_drafts`/`agent_versioning_versions`
+ write endpoints are deprecated under the branch model (they return `409
+ versioning_v2_migration_required`).
+* **Versioning helper** (`from smallestai.atoms.helpers import Versioning`): `publish_and_wait` /
+ `wait_for_commit` (publishing runs a security scan asynchronously โ this polls until the revision
+ is `published`), `edit_and_publish` (one-call config edit), and typed conflict handling
+ (`DraftConflictError`, `MigrationRequiredError`, `BaseRevisionUnavailableError`).
+* **New namespaces**: `call_actions`, `integrations`, `concurrency`, `disposition_metric_templates`,
+ `realtime`.
+* Generator: `exclude_types_from_init_exports` flipped to `false`.
+* Verified live end-to-end against `api.smallest.ai` (fork โ edit โ publish โ scan โ test-call โ
+ make-live โ restore), via both raw endpoints and the generated SDK.
+
## 5.1.1 - 2026-07-15
Patch โ fix the streaming-TTS compat shim (`WavesStreamingTTS`/`TTSConfig`).
diff --git a/examples/agent_versioning_lifecycle.py b/examples/agent_versioning_lifecycle.py
new file mode 100644
index 00000000..0d04c33b
--- /dev/null
+++ b/examples/agent_versioning_lifecycle.py
@@ -0,0 +1,83 @@
+"""Build a voice agent and iterate on it safely, with the smallestai SDK.
+
+The 90% path is one call: create_agent(...) stands up a fully-configured, LIVE
+agent. You do NOT need the versioning API to create or run an agent.
+
+Versioning (branches / drafts / revisions) is the layer for CHANGING an agent's
+config safely after it exists: edits are staged as a draft, published as an
+immutable revision (with a security scan), and can be staged on a branch or
+rolled back. Under the branch model, runtime-config edits go through this flow;
+`update_agent` only changes metadata (name, description, inbound toggle, ...).
+
+ pip install smallestai
+ export SMALLEST_API_KEY=sk_...
+ python examples/agent_versioning_lifecycle.py
+"""
+import os
+
+from smallestai import SmallestAI
+from smallestai.atoms.helpers.versioning import Versioning, DraftConflictError
+
+client = SmallestAI(api_key=os.environ["SMALLEST_API_KEY"])
+
+# 1. CREATE โ one call, fully configured, live immediately. No versioning needed.
+agent_id = client.atoms.agents.create_agent(
+ name="receptionist",
+ global_prompt="You are a friendly receptionist. Book appointments and answer questions.",
+ first_message="Hi, how can I help?",
+ language={"switching": {"isEnabled": False}},
+).data
+print("agent (live):", agent_id)
+
+# You can place calls right now โ the agent is serving the config above.
+# client.atoms.calls.start_an_outbound_call(agent_id=agent_id, to_phone="+15551234567")
+
+# ------------------------------------------------------------------------------
+# 2. EDIT the config later โ this is where versioning comes in.
+# ------------------------------------------------------------------------------
+v = Versioning(client)
+
+# Find the live Main branch (id + isDefault live on the inner .branch).
+branches = v.branches.list(id=agent_id).data.branches
+main_id = next(b for b in branches if b.branch.is_default).branch.id
+
+# Quick edit: change the config and publish in one call (draft -> publish -> live,
+# security scan handled). This is the simple "update my agent" path.
+revision = v.edit_and_publish(
+ agent_id, main_id,
+ global_prompt="You are a warm, concise receptionist. Confirm details before booking.",
+ label="tone tweak",
+)
+print("published revision:", revision.id, "status:", revision.status)
+
+# Test the branch before/after going live.
+test = v.branches.test_call(id=agent_id, branch_id=main_id, mode="webcall")
+print("test call id:", test.data.call_id)
+
+# ------------------------------------------------------------------------------
+# 3. ADVANCED โ stage a big change on a fork, promote it, roll back.
+# ------------------------------------------------------------------------------
+# Fork Main so real traffic keeps hitting the live config while you experiment.
+staging_id = v.branches.create_branch(id=agent_id, source_branch_id=main_id, name="staging").data.id
+v.edit_and_publish(agent_id, staging_id, global_prompt="New experimental prompt.", label="experiment")
+v.branches.make_live(id=agent_id, branch_id=staging_id) # staging live, Main flips to not-live
+
+# Roll back: restore an older revision as a new head revision on the live branch.
+older = v.revisions.list(id=agent_id, branch_id=staging_id).data.revisions[-1].id
+v.revisions.restore(id=agent_id, branch_id=staging_id, revision_id=older)
+
+# ------------------------------------------------------------------------------
+# 4. Safe concurrent edits โ optimistic concurrency.
+# ------------------------------------------------------------------------------
+draft = v.branches.get_draft(id=agent_id, branch_id=main_id).data
+try:
+ v.edit_and_publish(
+ agent_id, main_id,
+ expected_revision=draft.latest.draft_revision, # only apply if nobody edited since
+ global_prompt="Applied only if no conflicting edit landed first.",
+ )
+except DraftConflictError as e:
+ print(f"conflict: based on r{e.expected_revision}, latest is r{e.latest_revision}")
+ for d in e.diffs:
+ print(" changed section:", d["section"])
+ # Rebase on e.latest_revision, or retry without expected_revision to force-overwrite.
diff --git a/pyproject.toml b/pyproject.toml
index 04ff8802..b536aeee 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ dynamic = ["version"]
[tool.poetry]
name = "smallestai"
-version = "5.1.1"
+version = "5.2.0"
description = ""
readme = "README.md"
authors = []
diff --git a/src/smallestai/atoms/__init__.py b/src/smallestai/atoms/__init__.py
index 0503118e..762819af 100644
--- a/src/smallestai/atoms/__init__.py
+++ b/src/smallestai/atoms/__init__.py
@@ -6,47 +6,1237 @@
from importlib import import_module
if typing.TYPE_CHECKING:
+ from .types import (
+ AgentDto,
+ AgentDtoBackgroundSound,
+ AgentDtoConfigSource,
+ AgentDtoDenoisingConfig,
+ AgentDtoLanguage,
+ AgentDtoLanguageDefault,
+ AgentDtoLanguageSwitching,
+ AgentDtoLlmIdleTimeoutConfig,
+ AgentDtoPreCallApi,
+ AgentDtoPreCallApiMethod,
+ AgentDtoPreCallApiResponseVariablesItem,
+ AgentDtoPronunciationDictsItem,
+ AgentDtoRedactionConfig,
+ AgentDtoResolvedConfig,
+ AgentDtoResolvedConfigDefaultLanguage,
+ AgentDtoSessionTimeoutConfig,
+ AgentDtoSlmModel,
+ AgentDtoSmartTurnConfig,
+ AgentDtoSynthesizer,
+ AgentDtoSynthesizerVoiceConfig,
+ AgentDtoSynthesizerVoiceConfigGender,
+ AgentDtoSynthesizerVoiceConfigModel,
+ AgentDtoTimezone,
+ AgentDtoVersionedWorkflow,
+ AgentDtoVoiceDetectionConfig,
+ AgentDtoVoiceMailDetectionConfig,
+ AgentDtoWidgetConfig,
+ AgentDtoWidgetConfigMode,
+ AgentDtoWidgetConfigPosition,
+ AgentDtoWidgetConfigSize,
+ AgentDtoWidgetConfigTheme,
+ AgentVersion,
+ AgentVersionBlocks,
+ AgentVersionDiff,
+ AgentVersionDiffDiffsItem,
+ AgentVersionDiffDiffsItemChangesItem,
+ AgentVersionDiffDiffsItemChangesItemNewValue,
+ AgentVersionDiffDiffsItemChangesItemOldValue,
+ AgentVersionMetrics,
+ AgentVersionMetricsComparison,
+ AgentVersionMetricsComparisonMetrics,
+ AgentVersionMetricsComparisonMetricsDeltas,
+ AgentVersionStatus,
+ AnalyticsDateRange,
+ AnalyticsTrendMetric,
+ ApiResponse,
+ BadRequestErrorBody,
+ BadRequestErrorResponse,
+ Branch,
+ BranchStatus,
+ BranchSummary,
+ CallAction,
+ CallActionActionType,
+ CallActionCategory,
+ CallActionCondition,
+ CallActionConditionOperator,
+ CallActionConfig,
+ ComplianceApplication,
+ ComplianceApplicationNumberType,
+ ComplianceApplicationStatus,
+ ComplianceApplicationUserType,
+ ComplianceRequirement,
+ ConflictErrorResponse,
+ DiffChange,
+ DiffResult,
+ DiffSection,
+ DispositionMetric,
+ DispositionMetricDispositionMetricType,
+ DraftConflictError,
+ DraftConflictErrorData,
+ DraftConflictErrorDataConflict,
+ DraftDetail,
+ DraftDetailEditHistoryItem,
+ DraftEditHistoryEntry,
+ ForbiddenErrorBody,
+ InternalServerErrorBody,
+ InternalServerErrorResponse,
+ KnowledgeBase,
+ KnowledgeBaseItem,
+ KnowledgeBaseItemItemType,
+ KnowledgeBaseItemProcessingStatus,
+ KnowledgeBaseProcessingStatus,
+ LockedErrorResponse,
+ LockedErrorResponseErrorType,
+ NotFoundErrorBody,
+ PostCallAnalyticsConfig,
+ PostCallAnalyticsConfigSuccessMetricsItem,
+ PostCallAnalyticsConfigSuccessMetricsItemSuccessMetricType,
+ Product,
+ ProductAttributes,
+ ProductAttributesProvider,
+ ProductProductType,
+ PromptScore,
+ PromptScoreDimensionsItem,
+ PublishResult,
+ PublishResultState,
+ RequiredDocumentType,
+ RequiredDocumentTypeRequiredFieldsItem,
+ Revision,
+ RevisionPendingPublish,
+ RevisionPendingPublishState,
+ RevisionSecurityCheck,
+ RevisionStatus,
+ SinglePromptConfig,
+ SinglePromptData,
+ TestCallRequest,
+ TestCallRequestMode,
+ TestCallResult,
+ TooManyRequestsErrorBody,
+ TooManyRequestsErrorBodyRateLimit,
+ Tool,
+ ToolHeadersArrayItem,
+ ToolLlmParametersItem,
+ ToolLlmParametersItemType,
+ ToolMethod,
+ ToolOnHoldMusic,
+ ToolQueryParamsItem,
+ ToolResponseVariablesItem,
+ ToolTransferOption,
+ ToolTransferOptionPrivateHandoffOption,
+ ToolTransferOptionPrivateHandoffOptionType,
+ ToolTransferOptionPublicHandoffOption,
+ ToolTransferOptionPublicHandoffOptionType,
+ ToolTransferOptionType,
+ ToolType,
+ ToolVariablesExtractionSchemaItem,
+ ToolVariablesExtractionSchemaItemType,
+ UnauthorizedErrorResponse,
+ VersionBlocks,
+ VersioningV2MigrationRequiredResponse,
+ VersioningV2MigrationRequiredResponseErrorType,
+ WebEngageIntegrationSet,
+ Webhook,
+ WebhookAgent,
+ WebhookEvent,
+ WebhookEventAnalyticsCompleted,
+ WebhookEventAnalyticsCompletedMetadata,
+ WebhookEventAnalyticsCompletedMetadataAnalytics,
+ WebhookEventAnalyticsCompletedMetadataEventType,
+ WebhookEventAnalyticsMetric,
+ WebhookEventAnalyticsMetricDispositionMetricType,
+ WebhookEventAnalyticsMetricValue,
+ WebhookEventCallData,
+ WebhookEventCallDataCallDirection,
+ WebhookEventCallDataCallStatus,
+ WebhookEventEnvelope,
+ WebhookEventPostConversation,
+ WebhookEventPostConversationMetadata,
+ WebhookEventPostConversationMetadataEventType,
+ WebhookEventPreConversation,
+ WebhookEventPreConversationMetadata,
+ WebhookEventPreConversationMetadataEventType,
+ WebhookEventTranscriptTurn,
+ WebhookEventTranscriptTurnRole,
+ WebhookStatus,
+ WebhookSubscription,
+ WebhookSubscriptionEventType,
+ WebhookSubscriptionPopulated,
+ WebhookSubscriptionPopulatedEventType,
+ WidgetConfig,
+ WidgetConfigMode,
+ WidgetConfigPosition,
+ WidgetConfigSize,
+ WidgetConfigTheme,
+ WorkflowGraphData,
+ WorkflowGraphDataEdgesItem,
+ WorkflowGraphDataNodesItem,
+ WorkflowGraphDataNodesItemPosition,
+ WorkflowType,
+ )
+ from .errors import (
+ BadGatewayError,
+ BadRequestError,
+ ConflictError,
+ ForbiddenError,
+ InternalServerError,
+ LockedError,
+ NotFoundError,
+ TooManyRequestsError,
+ UnauthorizedError,
+ UnprocessableEntityError,
+ )
from . import (
agent_templates,
+ agent_versioning_branches,
agent_versioning_drafts,
+ agent_versioning_revisions,
agent_versioning_versions,
agents,
+ analytics,
audience,
+ call_actions,
calls,
campaigns,
compliance,
+ concurrency,
conversations,
+ disposition_metric_templates,
dnc,
+ integrations,
knowledge_base,
live_transcripts,
organization,
phone_numbers,
prompt_scoring,
+ realtime,
user,
webhooks,
)
-
- # .fernignore-preserved: generator 5.12.12 strips this atoms-root type export
- # (exclude_types_from_init_exports), but the agents wire test imports it from
- # `smallestai.atoms`. Re-export so the import resolves. See SDK_ESCALATIONS.log [1].
- from .types.widget_config import WidgetConfig
+ from .agent_templates import (
+ ListAgentTemplatesAgentTemplatesRequestRegion,
+ ListAgentTemplatesAgentTemplatesResponse,
+ ListAgentTemplatesAgentTemplatesResponseDataItem,
+ ListAgentTemplatesAgentTemplatesResponseDataItemCallType,
+ ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage,
+ ListAgentTemplatesAgentTemplatesResponseDataItemRegion,
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig,
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem,
+ ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType,
+ ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType,
+ PostAgentFromTemplateResponse,
+ )
+ from .agent_versioning_branches import (
+ ArchiveAgentVersioningBranchesResponse,
+ ArchiveAgentVersioningBranchesResponseData,
+ CancelPublishAgentVersioningBranchesResponse,
+ CancelPublishAgentVersioningBranchesResponseData,
+ CreateBranchAgentVersioningBranchesResponse,
+ DiscardDraftAgentVersioningBranchesResponse,
+ DiscardDraftAgentVersioningBranchesResponseData,
+ GetAgentVersioningBranchesResponse,
+ GetDraftAgentVersioningBranchesResponse,
+ ListAgentVersioningBranchesResponse,
+ ListAgentVersioningBranchesResponseData,
+ MakeLiveAgentVersioningBranchesResponse,
+ PublishDraftAgentVersioningBranchesResponse,
+ RenameAgentVersioningBranchesResponse,
+ TestCallAgentVersioningBranchesResponse,
+ TestCallV2RequestMode,
+ UpdateBranchDraftRequestBackgroundSound,
+ UpdateBranchDraftRequestSlmModel,
+ UpdateDraftAgentVersioningBranchesResponse,
+ )
+ from .agent_versioning_drafts import (
+ CreateDraftAgentVersioningDraftsResponse,
+ DiscardDraftAgentVersioningDraftsResponse,
+ DraftConfigRequestBackgroundSound,
+ DraftConfigRequestSlmModel,
+ GetAgentIdDraftsDraftIdDiffResponse,
+ GetAgentIdDraftsDraftIdDiffResponseData,
+ GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue,
+ GetAgentIdDraftsDraftIdResponse,
+ GetAgentIdDraftsDraftIdResponseData,
+ GetAgentIdDraftsResponse,
+ GetAgentIdDraftsResponseDataItem,
+ PostAgentIdDraftsDraftIdTestCallResponse,
+ PostAgentIdDraftsDraftIdTestCallResponseData,
+ PublishDraftAgentVersioningDraftsResponse,
+ RenameDraftAgentVersioningDraftsResponse,
+ RenameDraftAgentVersioningDraftsResponseData,
+ UpdateDraftConfigAgentVersioningDraftsResponse,
+ )
+ from .agent_versioning_revisions import (
+ DiffAgentVersioningRevisionsResponse,
+ GetAgentVersioningRevisionsResponse,
+ GetAgentVersioningRevisionsResponseData,
+ GetHistoryAgentVersioningRevisionsResponse,
+ GetHistoryAgentVersioningRevisionsResponseData,
+ GetHistoryAgentVersioningRevisionsResponseDataTrailItem,
+ ListAgentVersioningRevisionsResponse,
+ ListAgentVersioningRevisionsResponseData,
+ RestoreAgentVersioningRevisionsResponse,
+ )
+ from .agent_versioning_versions import (
+ ActivateVersionAgentVersioningVersionsResponse,
+ GetAgentIdVersionsDiffResponse,
+ GetAgentIdVersionsResponse,
+ GetAgentIdVersionsResponseData,
+ GetAgentIdVersionsResponseDataVersionsItem,
+ GetAgentIdVersionsVersionIdResponse,
+ GetAgentIdVersionsVersionIdResponseData,
+ PostAgentIdVersionsVersionIdTestCallResponse,
+ PostAgentIdVersionsVersionIdTestCallResponseData,
+ UpdateVersionMetadataAgentVersioningVersionsResponse,
+ )
+ from .agents import (
+ ArchiveAgentAgentsResponse,
+ ArchiveAgentAgentsResponseData,
+ ArchiveAgentAgentsResponseDataMessage,
+ CreateAgentAgentsResponse,
+ CreateAgentRequestBackgroundSound,
+ CreateAgentRequestDenoisingConfig,
+ CreateAgentRequestLanguage,
+ CreateAgentRequestLanguageDefault,
+ CreateAgentRequestLanguageSupportedItem,
+ CreateAgentRequestLanguageSwitching,
+ CreateAgentRequestLlmIdleTimeoutConfig,
+ CreateAgentRequestPreCallApi,
+ CreateAgentRequestPreCallApiMethod,
+ CreateAgentRequestPreCallApiResponseVariablesItem,
+ CreateAgentRequestPronunciationDictsItem,
+ CreateAgentRequestRedactionConfig,
+ CreateAgentRequestSessionTimeoutConfig,
+ CreateAgentRequestSlmModel,
+ CreateAgentRequestSmartTurnConfig,
+ CreateAgentRequestSynthesizer,
+ CreateAgentRequestSynthesizerVoiceConfig,
+ CreateAgentRequestSynthesizerVoiceConfigGender,
+ CreateAgentRequestSynthesizerVoiceConfigModel,
+ CreateAgentRequestTimezone,
+ CreateAgentRequestVoiceDetectionConfig,
+ CreateAgentRequestVoiceMailDetectionConfig,
+ DuplicateAgentAgentsResponse,
+ DuplicateAgentAgentsResponseData,
+ GetAgentAgentsResponse,
+ GetAgentAvatarPresignedUrlResponse,
+ GetAgentAvatarPresignedUrlResponseData,
+ GetAgentCallLogsResponse,
+ GetAgentCallLogsResponseData,
+ GetAgentCallLogsResponseDataCallLogsItem,
+ GetAgentWidgetConfigResponse,
+ GetAgentWidgetConfigResponseData,
+ GetAgentWidgetConfigResponseDataMode,
+ GetAgentWidgetConfigResponseDataPosition,
+ GetAgentWidgetConfigResponseDataSize,
+ GetAgentWidgetConfigResponseDataTheme,
+ ListAgentsAgentsRequestSortField,
+ ListAgentsAgentsRequestSortOrder,
+ ListAgentsAgentsRequestType,
+ ListAgentsAgentsResponse,
+ ListAgentsAgentsResponseData,
+ UpdateAgentAgentsResponse,
+ UpdateAgentWidgetConfigRequestWidgetConfig,
+ UpdateAgentWidgetConfigRequestWidgetConfigMode,
+ UpdateAgentWidgetConfigRequestWidgetConfigPosition,
+ UpdateAgentWidgetConfigRequestWidgetConfigSize,
+ UpdateAgentWidgetConfigRequestWidgetConfigTheme,
+ UpdateAgentWidgetConfigResponse,
+ )
+ from .analytics import (
+ GetAgentPerformanceRequestSortOrder,
+ GetAgentPerformanceResponse,
+ GetAgentPerformanceResponseData,
+ GetAgentPerformanceResponseDataAgentsItem,
+ GetAnalyticsConcurrencyRequestIncludeAgents,
+ GetAnalyticsConcurrencyResponse,
+ GetAnalyticsConcurrencyResponseData,
+ GetAnalyticsConcurrencyResponseDataByAgentItem,
+ GetAnalyticsConcurrencyResponseDataDataPointsItem,
+ GetAnalyticsSummaryResponse,
+ GetAnalyticsSummaryResponseData,
+ GetAttemptCohortResponse,
+ GetAttemptCohortResponseData,
+ GetAttemptCohortResponseDataCohortsItem,
+ GetCallCountsByDayResponse,
+ GetCallCountsByDayResponseData,
+ GetCallCountsByDayResponseDataCallCountsByDayItem,
+ GetCallCountsLogResponse,
+ GetCallCountsLogResponseData,
+ GetCallCountsLogResponseDataCallsItem,
+ GetCallOutcomesTimeseriesResponse,
+ GetCallOutcomesTimeseriesResponseData,
+ GetCallOutcomesTimeseriesResponseDataDataPointsItem,
+ GetCallOutcomesTimeseriesResponseDataTotals,
+ GetCallStartDistributionResponse,
+ GetCallStartDistributionResponseData,
+ GetCallStartDistributionResponseDataDataPointsItem,
+ GetCallVolumeTimeseriesResponse,
+ GetCallVolumeTimeseriesResponseData,
+ GetCallVolumeTimeseriesResponseDataDataPointsItem,
+ GetConversationDetailsResponse,
+ GetConversationDetailsResponseData,
+ GetConversationDetailsResponseDataEventsItem,
+ GetConversationDetailsResponseDataTranscriptItem,
+ GetDailyCallSummaryResponse,
+ GetDailyCallSummaryResponseData,
+ GetDashboardResponse,
+ GetDashboardResponseData,
+ GetDurationStatsResponse,
+ GetDurationStatsResponseData,
+ GetHourlyPerformanceResponse,
+ GetHourlyPerformanceResponseData,
+ GetHourlyPerformanceResponseDataHoursItem,
+ GetPhoneNumberTrendsResponse,
+ GetPhoneNumberTrendsResponseData,
+ GetPhoneNumberTrendsResponseDataTrendsItem,
+ GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem,
+ GetPickupRateByNumberResponse,
+ GetPickupRateByNumberResponseData,
+ GetPickupRateByNumberResponseDataNumbersItem,
+ GetUsageTimeseriesResponse,
+ GetUsageTimeseriesResponseData,
+ GetUsageTimeseriesResponseDataDateRange,
+ GetUsageTimeseriesResponseDataDayWiseCreditsItem,
+ GetWeeklyTrendsResponse,
+ GetWeeklyTrendsResponseData,
+ GetWeeklyTrendsResponseDataWeeksItem,
+ )
+ from .audience import (
+ DeleteAudienceIdMembersResponse,
+ DeleteAudienceIdMembersResponseData,
+ DeleteAudienceIdResponse,
+ GetAudienceIdMembersResponse,
+ GetAudienceIdMembersResponseData,
+ GetAudienceIdMembersResponseDataMembersItem,
+ GetAudienceIdMembersSearchResponse,
+ GetAudienceIdMembersSearchResponseData,
+ GetAudienceIdMembersSearchResponseDataMembersItem,
+ GetAudienceIdMembersSearchResponseDataSearchInfo,
+ GetAudienceIdMembersSearchResponseDataSearchInfoSearchType,
+ GetAudienceResponse,
+ GetAudienceResponseData,
+ ListAudienceResponse,
+ ListAudienceResponseDataItem,
+ ListAudienceResponseDataItemCampaignsItem,
+ PostAudienceIdMembersResponse,
+ PostAudienceIdMembersResponseDataItem,
+ PostAudienceIdMembersResponseDataItemData,
+ PostAudienceResponse,
+ PostAudienceResponseData,
+ )
+ from .call_actions import (
+ CreateCallActionRequestActionType,
+ CreateCallActionRequestCategory,
+ CreateCallActionRequestConfig,
+ CreateCallActionResponse,
+ DeleteCallActionResponse,
+ DeleteCallActionResponseData,
+ GetCallActionResponse,
+ ListCallActionsRequestCategory,
+ ListCallActionsResponse,
+ ListCallActionsResponseData,
+ UpdateCallActionRequestActionType,
+ UpdateCallActionRequestCategory,
+ UpdateCallActionRequestConfig,
+ UpdateCallActionResponse,
+ )
+ from .calls import (
+ GetCallsResponse,
+ GetCallsResponseData,
+ GetCallsResponseDataPostCallAnalytics,
+ GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem,
+ GetCallsResponseDataStatus,
+ GetCallsResponseDataTranscriptItem,
+ GetCallsResponseDataTranscriptItemRole,
+ GetCallsResponseDataTurnLatencyMetrics,
+ GetCallsResponseDataTurnLatencyMetricsTransitionsItem,
+ GetCallsResponseDataType,
+ GetCallsResponseDataVoiceConfigUsed,
+ ListCallsRequestCallTypes,
+ ListCallsRequestSortBy,
+ ListCallsRequestSortOrder,
+ ListCallsResponse,
+ ListCallsResponseData,
+ ListCallsResponseDataDispositionMetricsConfigItem,
+ ListCallsResponseDataLogsItem,
+ ListCallsResponseDataLogsItemAgentDispositionConfigItem,
+ ListCallsResponseDataLogsItemPostCallAnalytics,
+ ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem,
+ ListCallsResponseDataLogsItemStatus,
+ ListCallsResponseDataLogsItemTurnLatencyMetrics,
+ ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem,
+ ListCallsResponseDataLogsItemType,
+ ListCallsResponseDataPagination,
+ SearchCallsResponse,
+ SearchCallsResponseData,
+ SearchCallsResponseDataLogsItem,
+ SearchCallsResponseDataLogsItemPostCallAnalytics,
+ SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem,
+ SearchCallsResponseDataLogsItemStatus,
+ SearchCallsResponseDataLogsItemTurnLatencyMetrics,
+ SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem,
+ SearchCallsResponseDataLogsItemType,
+ StartOutboundCallCallsRequestVariablesValue,
+ StartOutboundCallCallsRequestXTestCall,
+ StartOutboundCallCallsResponse,
+ StartOutboundCallCallsResponseData,
+ )
+ from .campaigns import (
+ CreateCampaignsResponse,
+ CreateCampaignsResponseData,
+ CreateCampaignsResponseDataStatus,
+ DeleteCampaignsResponse,
+ GetCampaignsResponse,
+ GetCampaignsResponseData,
+ GetCampaignsResponseDataCampaign,
+ GetCampaignsResponseDataCampaignAgent,
+ GetCampaignsResponseDataCampaignAudience,
+ GetCampaignsResponseDataCampaignStatus,
+ GetCampaignsResponseDataEventsItem,
+ GetCampaignsResponseDataMetrics,
+ ListCampaignsRequestSortField,
+ ListCampaignsRequestSortOrder,
+ ListCampaignsRequestStatus,
+ ListCampaignsResponse,
+ ListCampaignsResponseData,
+ ListCampaignsResponseDataCampaignsItem,
+ ListCampaignsResponseDataCampaignsItemAgent,
+ ListCampaignsResponseDataCampaignsItemAudience,
+ ListCampaignsResponseDataCampaignsItemStatus,
+ ListCampaignsResponseDataPagination,
+ PauseCampaignsResponse,
+ StartOrResumeCampaignsResponse,
+ StartOrResumeCampaignsResponseData,
+ )
+ from .compliance import (
+ GetComplianceRequirementsRequestNumberType,
+ GetComplianceRequirementsRequestUserType,
+ GetComplianceRequirementsResponse,
+ GetComplianceStatusRequestNumberType,
+ GetComplianceStatusRequestUserType,
+ GetComplianceStatusResponse,
+ GetComplianceStatusResponseData,
+ GetComplianceStatusResponseDataStep,
+ PostComplianceApplicationsIdRefreshResponse,
+ ResubmitComplianceResponse,
+ SubmitComplianceRequestNumberType,
+ SubmitComplianceRequestUserType,
+ SubmitComplianceResponse,
+ )
+ from .concurrency import (
+ GetConcurrencyResponse,
+ GetConcurrencyResponseData,
+ GetConcurrencyResponseDataAgentsItem,
+ UpdateConcurrencyReservationsRequestReservationsItem,
+ UpdateConcurrencyReservationsResponse,
+ UpdateConcurrencyReservationsResponseData,
+ )
+ from .conversations import (
+ CancelConversationsResponse,
+ CancelConversationsResponseData,
+ CancelQueuedConversationsResponse,
+ CancelQueuedConversationsResponseData,
+ GetConversationCallIdRecordingDownloadUrlResponse,
+ GetConversationCallIdRecordingDownloadUrlResponseData,
+ ListRetryAttemptsConversationsResponse,
+ ListRetryAttemptsConversationsResponseData,
+ )
+ from .disposition_metric_templates import (
+ ListDispositionMetricTemplatesResponse,
+ ListDispositionMetricTemplatesResponseDataItem,
+ ListDispositionMetricTemplatesResponseDataItemDispositionMetricType,
+ )
+ from .dnc import (
+ ListDncRequestSortField,
+ ListDncRequestSortOrder,
+ ListDncResponse,
+ ListDncResponseData,
+ ListDncResponseDataEntriesItem,
+ ListDncResponseDataPagination,
+ )
+ from .integrations import GetWebEngageDetailsResponse, ModifyWebEngageIntegrationResponse
+ from .knowledge_base import (
+ CreateKnowledgeBaseResponse,
+ DeleteKnowledgeBaseResponse,
+ DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse,
+ DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse,
+ ExtractSitemapUrlsKnowledgeBaseResponse,
+ ExtractSitemapUrlsKnowledgeBaseResponseData,
+ GetKnowledgeBaseResponse,
+ GetKnowledgebaseIdItemsResponse,
+ GetKnowledgebaseIdScrapedUrlsResponse,
+ GetKnowledgebaseIdScrapedUrlsResponseDataItem,
+ ListKnowledgeBaseResponse,
+ PostKnowledgebaseCompeleteFileUploadResponse,
+ PostKnowledgebaseGetPresignedUrlResponse,
+ PostKnowledgebaseGetPresignedUrlResponseData,
+ PostKnowledgebaseIdItemsUploadMediaResponse,
+ PostKnowledgebaseIdResponse,
+ ScrapeUrlsKnowledgeBaseResponse,
+ )
+ from .live_transcripts import (
+ SubscribeToLiveEventsLiveTranscriptsResponse,
+ SubscribeToLiveEventsLiveTranscriptsResponseEventType,
+ SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem,
+ )
+ from .organization import (
+ GetOrganizationResponse,
+ GetOrganizationResponseData,
+ GetOrganizationResponseDataMembersItem,
+ GetOrganizationResponseDataSubscription,
+ )
+ from .phone_numbers import (
+ GetProductAllNumbersResponse,
+ GetProductAllNumbersResponseData,
+ GetProductManageSubscriptionResponse,
+ GetProductManageSubscriptionResponseData,
+ GetProductProrationAmountResponse,
+ GetProductProrationAmountResponseData,
+ GetProductUnpaidInvoicesResponse,
+ GetProductUnpaidInvoicesResponseData,
+ ImportSipPhoneNumbersResponse,
+ ImportSipPhoneNumbersResponseData,
+ ImportSipPhoneNumbersResponseDataAttributes,
+ ImportSipPhoneNumbersResponseDataProductType,
+ ListPhoneNumbersResponse,
+ ListPhoneNumbersResponseDataItem,
+ ListPhoneNumbersResponseDataItemAttributes,
+ ListPhoneNumbersResponseDataItemAttributesProvider,
+ ListPhoneNumbersResponseDataItemProductType,
+ ReleasePhoneNumbersResponse,
+ ReleasePhoneNumbersResponseData,
+ RentPhoneNumbersRequestProvider,
+ RentPhoneNumbersResponse,
+ RentPhoneNumbersResponseData,
+ SearchRentablePhoneNumbersRequestProvider,
+ SearchRentablePhoneNumbersResponse,
+ SearchRentablePhoneNumbersResponseDataItem,
+ SearchRentablePhoneNumbersResponseDataItemProvider,
+ )
+ from .prompt_scoring import (
+ PostPromptScoringScoreRequest,
+ PostPromptScoringScoreRequestDraftId,
+ PostPromptScoringScoreRequestVersionId,
+ PostPromptScoringScoreResponse,
+ PostPromptScoringScoreResponseData,
+ PostPromptScoringScoreResponseDataBand,
+ PostPromptScoringScoreResponseDataDimensionsItem,
+ PostPromptScoringScoreResponseDataDimensionsItemLevel,
+ PostPromptScoringScoreResponseDataOverallGrade,
+ )
+ from .realtime import (
+ RegisterCallRealtimeRequestMode,
+ RegisterCallRealtimeRequestVariablesValue,
+ RegisterCallRealtimeResponse,
+ RegisterCallRealtimeResponseData,
+ )
+ from .user import GetUserResponse, GetUserResponseData
+ from .webhooks import (
+ CreateWebhooksRequestEventsItem,
+ CreateWebhooksRequestEventsItemEventType,
+ CreateWebhooksResponse,
+ DeleteAgentAgentIdWebhookSubscriptionsResponse,
+ DeleteWebhooksResponse,
+ GetAgentAgentIdWebhookSubscriptionsResponse,
+ GetWebhookResponse,
+ GetWebhookResponseData,
+ PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem,
+ PostAgentAgentIdWebhookSubscriptionsResponse,
+ )
_dynamic_imports: typing.Dict[str, str] = {
- "WidgetConfig": ".types.widget_config",
+ "ActivateVersionAgentVersioningVersionsResponse": ".agent_versioning_versions",
+ "AgentDto": ".types",
+ "AgentDtoBackgroundSound": ".types",
+ "AgentDtoConfigSource": ".types",
+ "AgentDtoDenoisingConfig": ".types",
+ "AgentDtoLanguage": ".types",
+ "AgentDtoLanguageDefault": ".types",
+ "AgentDtoLanguageSwitching": ".types",
+ "AgentDtoLlmIdleTimeoutConfig": ".types",
+ "AgentDtoPreCallApi": ".types",
+ "AgentDtoPreCallApiMethod": ".types",
+ "AgentDtoPreCallApiResponseVariablesItem": ".types",
+ "AgentDtoPronunciationDictsItem": ".types",
+ "AgentDtoRedactionConfig": ".types",
+ "AgentDtoResolvedConfig": ".types",
+ "AgentDtoResolvedConfigDefaultLanguage": ".types",
+ "AgentDtoSessionTimeoutConfig": ".types",
+ "AgentDtoSlmModel": ".types",
+ "AgentDtoSmartTurnConfig": ".types",
+ "AgentDtoSynthesizer": ".types",
+ "AgentDtoSynthesizerVoiceConfig": ".types",
+ "AgentDtoSynthesizerVoiceConfigGender": ".types",
+ "AgentDtoSynthesizerVoiceConfigModel": ".types",
+ "AgentDtoTimezone": ".types",
+ "AgentDtoVersionedWorkflow": ".types",
+ "AgentDtoVoiceDetectionConfig": ".types",
+ "AgentDtoVoiceMailDetectionConfig": ".types",
+ "AgentDtoWidgetConfig": ".types",
+ "AgentDtoWidgetConfigMode": ".types",
+ "AgentDtoWidgetConfigPosition": ".types",
+ "AgentDtoWidgetConfigSize": ".types",
+ "AgentDtoWidgetConfigTheme": ".types",
+ "AgentVersion": ".types",
+ "AgentVersionBlocks": ".types",
+ "AgentVersionDiff": ".types",
+ "AgentVersionDiffDiffsItem": ".types",
+ "AgentVersionDiffDiffsItemChangesItem": ".types",
+ "AgentVersionDiffDiffsItemChangesItemNewValue": ".types",
+ "AgentVersionDiffDiffsItemChangesItemOldValue": ".types",
+ "AgentVersionMetrics": ".types",
+ "AgentVersionMetricsComparison": ".types",
+ "AgentVersionMetricsComparisonMetrics": ".types",
+ "AgentVersionMetricsComparisonMetricsDeltas": ".types",
+ "AgentVersionStatus": ".types",
+ "AnalyticsDateRange": ".types",
+ "AnalyticsTrendMetric": ".types",
+ "ApiResponse": ".types",
+ "ArchiveAgentAgentsResponse": ".agents",
+ "ArchiveAgentAgentsResponseData": ".agents",
+ "ArchiveAgentAgentsResponseDataMessage": ".agents",
+ "ArchiveAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "ArchiveAgentVersioningBranchesResponseData": ".agent_versioning_branches",
+ "BadGatewayError": ".errors",
+ "BadRequestError": ".errors",
+ "BadRequestErrorBody": ".types",
+ "BadRequestErrorResponse": ".types",
+ "Branch": ".types",
+ "BranchStatus": ".types",
+ "BranchSummary": ".types",
+ "CallAction": ".types",
+ "CallActionActionType": ".types",
+ "CallActionCategory": ".types",
+ "CallActionCondition": ".types",
+ "CallActionConditionOperator": ".types",
+ "CallActionConfig": ".types",
+ "CancelConversationsResponse": ".conversations",
+ "CancelConversationsResponseData": ".conversations",
+ "CancelPublishAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "CancelPublishAgentVersioningBranchesResponseData": ".agent_versioning_branches",
+ "CancelQueuedConversationsResponse": ".conversations",
+ "CancelQueuedConversationsResponseData": ".conversations",
+ "ComplianceApplication": ".types",
+ "ComplianceApplicationNumberType": ".types",
+ "ComplianceApplicationStatus": ".types",
+ "ComplianceApplicationUserType": ".types",
+ "ComplianceRequirement": ".types",
+ "ConflictError": ".errors",
+ "ConflictErrorResponse": ".types",
+ "CreateAgentAgentsResponse": ".agents",
+ "CreateAgentRequestBackgroundSound": ".agents",
+ "CreateAgentRequestDenoisingConfig": ".agents",
+ "CreateAgentRequestLanguage": ".agents",
+ "CreateAgentRequestLanguageDefault": ".agents",
+ "CreateAgentRequestLanguageSupportedItem": ".agents",
+ "CreateAgentRequestLanguageSwitching": ".agents",
+ "CreateAgentRequestLlmIdleTimeoutConfig": ".agents",
+ "CreateAgentRequestPreCallApi": ".agents",
+ "CreateAgentRequestPreCallApiMethod": ".agents",
+ "CreateAgentRequestPreCallApiResponseVariablesItem": ".agents",
+ "CreateAgentRequestPronunciationDictsItem": ".agents",
+ "CreateAgentRequestRedactionConfig": ".agents",
+ "CreateAgentRequestSessionTimeoutConfig": ".agents",
+ "CreateAgentRequestSlmModel": ".agents",
+ "CreateAgentRequestSmartTurnConfig": ".agents",
+ "CreateAgentRequestSynthesizer": ".agents",
+ "CreateAgentRequestSynthesizerVoiceConfig": ".agents",
+ "CreateAgentRequestSynthesizerVoiceConfigGender": ".agents",
+ "CreateAgentRequestSynthesizerVoiceConfigModel": ".agents",
+ "CreateAgentRequestTimezone": ".agents",
+ "CreateAgentRequestVoiceDetectionConfig": ".agents",
+ "CreateAgentRequestVoiceMailDetectionConfig": ".agents",
+ "CreateBranchAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "CreateCallActionRequestActionType": ".call_actions",
+ "CreateCallActionRequestCategory": ".call_actions",
+ "CreateCallActionRequestConfig": ".call_actions",
+ "CreateCallActionResponse": ".call_actions",
+ "CreateCampaignsResponse": ".campaigns",
+ "CreateCampaignsResponseData": ".campaigns",
+ "CreateCampaignsResponseDataStatus": ".campaigns",
+ "CreateDraftAgentVersioningDraftsResponse": ".agent_versioning_drafts",
+ "CreateKnowledgeBaseResponse": ".knowledge_base",
+ "CreateWebhooksRequestEventsItem": ".webhooks",
+ "CreateWebhooksRequestEventsItemEventType": ".webhooks",
+ "CreateWebhooksResponse": ".webhooks",
+ "DeleteAgentAgentIdWebhookSubscriptionsResponse": ".webhooks",
+ "DeleteAudienceIdMembersResponse": ".audience",
+ "DeleteAudienceIdMembersResponseData": ".audience",
+ "DeleteAudienceIdResponse": ".audience",
+ "DeleteCallActionResponse": ".call_actions",
+ "DeleteCallActionResponseData": ".call_actions",
+ "DeleteCampaignsResponse": ".campaigns",
+ "DeleteKnowledgeBaseResponse": ".knowledge_base",
+ "DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse": ".knowledge_base",
+ "DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse": ".knowledge_base",
+ "DeleteWebhooksResponse": ".webhooks",
+ "DiffAgentVersioningRevisionsResponse": ".agent_versioning_revisions",
+ "DiffChange": ".types",
+ "DiffResult": ".types",
+ "DiffSection": ".types",
+ "DiscardDraftAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "DiscardDraftAgentVersioningBranchesResponseData": ".agent_versioning_branches",
+ "DiscardDraftAgentVersioningDraftsResponse": ".agent_versioning_drafts",
+ "DispositionMetric": ".types",
+ "DispositionMetricDispositionMetricType": ".types",
+ "DraftConfigRequestBackgroundSound": ".agent_versioning_drafts",
+ "DraftConfigRequestSlmModel": ".agent_versioning_drafts",
+ "DraftConflictError": ".types",
+ "DraftConflictErrorData": ".types",
+ "DraftConflictErrorDataConflict": ".types",
+ "DraftDetail": ".types",
+ "DraftDetailEditHistoryItem": ".types",
+ "DraftEditHistoryEntry": ".types",
+ "DuplicateAgentAgentsResponse": ".agents",
+ "DuplicateAgentAgentsResponseData": ".agents",
+ "ExtractSitemapUrlsKnowledgeBaseResponse": ".knowledge_base",
+ "ExtractSitemapUrlsKnowledgeBaseResponseData": ".knowledge_base",
+ "ForbiddenError": ".errors",
+ "ForbiddenErrorBody": ".types",
+ "GetAgentAgentIdWebhookSubscriptionsResponse": ".webhooks",
+ "GetAgentAgentsResponse": ".agents",
+ "GetAgentAvatarPresignedUrlResponse": ".agents",
+ "GetAgentAvatarPresignedUrlResponseData": ".agents",
+ "GetAgentCallLogsResponse": ".agents",
+ "GetAgentCallLogsResponseData": ".agents",
+ "GetAgentCallLogsResponseDataCallLogsItem": ".agents",
+ "GetAgentIdDraftsDraftIdDiffResponse": ".agent_versioning_drafts",
+ "GetAgentIdDraftsDraftIdDiffResponseData": ".agent_versioning_drafts",
+ "GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue": ".agent_versioning_drafts",
+ "GetAgentIdDraftsDraftIdResponse": ".agent_versioning_drafts",
+ "GetAgentIdDraftsDraftIdResponseData": ".agent_versioning_drafts",
+ "GetAgentIdDraftsResponse": ".agent_versioning_drafts",
+ "GetAgentIdDraftsResponseDataItem": ".agent_versioning_drafts",
+ "GetAgentIdVersionsDiffResponse": ".agent_versioning_versions",
+ "GetAgentIdVersionsResponse": ".agent_versioning_versions",
+ "GetAgentIdVersionsResponseData": ".agent_versioning_versions",
+ "GetAgentIdVersionsResponseDataVersionsItem": ".agent_versioning_versions",
+ "GetAgentIdVersionsVersionIdResponse": ".agent_versioning_versions",
+ "GetAgentIdVersionsVersionIdResponseData": ".agent_versioning_versions",
+ "GetAgentPerformanceRequestSortOrder": ".analytics",
+ "GetAgentPerformanceResponse": ".analytics",
+ "GetAgentPerformanceResponseData": ".analytics",
+ "GetAgentPerformanceResponseDataAgentsItem": ".analytics",
+ "GetAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "GetAgentVersioningRevisionsResponse": ".agent_versioning_revisions",
+ "GetAgentVersioningRevisionsResponseData": ".agent_versioning_revisions",
+ "GetAgentWidgetConfigResponse": ".agents",
+ "GetAgentWidgetConfigResponseData": ".agents",
+ "GetAgentWidgetConfigResponseDataMode": ".agents",
+ "GetAgentWidgetConfigResponseDataPosition": ".agents",
+ "GetAgentWidgetConfigResponseDataSize": ".agents",
+ "GetAgentWidgetConfigResponseDataTheme": ".agents",
+ "GetAnalyticsConcurrencyRequestIncludeAgents": ".analytics",
+ "GetAnalyticsConcurrencyResponse": ".analytics",
+ "GetAnalyticsConcurrencyResponseData": ".analytics",
+ "GetAnalyticsConcurrencyResponseDataByAgentItem": ".analytics",
+ "GetAnalyticsConcurrencyResponseDataDataPointsItem": ".analytics",
+ "GetAnalyticsSummaryResponse": ".analytics",
+ "GetAnalyticsSummaryResponseData": ".analytics",
+ "GetAttemptCohortResponse": ".analytics",
+ "GetAttemptCohortResponseData": ".analytics",
+ "GetAttemptCohortResponseDataCohortsItem": ".analytics",
+ "GetAudienceIdMembersResponse": ".audience",
+ "GetAudienceIdMembersResponseData": ".audience",
+ "GetAudienceIdMembersResponseDataMembersItem": ".audience",
+ "GetAudienceIdMembersSearchResponse": ".audience",
+ "GetAudienceIdMembersSearchResponseData": ".audience",
+ "GetAudienceIdMembersSearchResponseDataMembersItem": ".audience",
+ "GetAudienceIdMembersSearchResponseDataSearchInfo": ".audience",
+ "GetAudienceIdMembersSearchResponseDataSearchInfoSearchType": ".audience",
+ "GetAudienceResponse": ".audience",
+ "GetAudienceResponseData": ".audience",
+ "GetCallActionResponse": ".call_actions",
+ "GetCallCountsByDayResponse": ".analytics",
+ "GetCallCountsByDayResponseData": ".analytics",
+ "GetCallCountsByDayResponseDataCallCountsByDayItem": ".analytics",
+ "GetCallCountsLogResponse": ".analytics",
+ "GetCallCountsLogResponseData": ".analytics",
+ "GetCallCountsLogResponseDataCallsItem": ".analytics",
+ "GetCallOutcomesTimeseriesResponse": ".analytics",
+ "GetCallOutcomesTimeseriesResponseData": ".analytics",
+ "GetCallOutcomesTimeseriesResponseDataDataPointsItem": ".analytics",
+ "GetCallOutcomesTimeseriesResponseDataTotals": ".analytics",
+ "GetCallStartDistributionResponse": ".analytics",
+ "GetCallStartDistributionResponseData": ".analytics",
+ "GetCallStartDistributionResponseDataDataPointsItem": ".analytics",
+ "GetCallVolumeTimeseriesResponse": ".analytics",
+ "GetCallVolumeTimeseriesResponseData": ".analytics",
+ "GetCallVolumeTimeseriesResponseDataDataPointsItem": ".analytics",
+ "GetCallsResponse": ".calls",
+ "GetCallsResponseData": ".calls",
+ "GetCallsResponseDataPostCallAnalytics": ".calls",
+ "GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem": ".calls",
+ "GetCallsResponseDataStatus": ".calls",
+ "GetCallsResponseDataTranscriptItem": ".calls",
+ "GetCallsResponseDataTranscriptItemRole": ".calls",
+ "GetCallsResponseDataTurnLatencyMetrics": ".calls",
+ "GetCallsResponseDataTurnLatencyMetricsTransitionsItem": ".calls",
+ "GetCallsResponseDataType": ".calls",
+ "GetCallsResponseDataVoiceConfigUsed": ".calls",
+ "GetCampaignsResponse": ".campaigns",
+ "GetCampaignsResponseData": ".campaigns",
+ "GetCampaignsResponseDataCampaign": ".campaigns",
+ "GetCampaignsResponseDataCampaignAgent": ".campaigns",
+ "GetCampaignsResponseDataCampaignAudience": ".campaigns",
+ "GetCampaignsResponseDataCampaignStatus": ".campaigns",
+ "GetCampaignsResponseDataEventsItem": ".campaigns",
+ "GetCampaignsResponseDataMetrics": ".campaigns",
+ "GetComplianceRequirementsRequestNumberType": ".compliance",
+ "GetComplianceRequirementsRequestUserType": ".compliance",
+ "GetComplianceRequirementsResponse": ".compliance",
+ "GetComplianceStatusRequestNumberType": ".compliance",
+ "GetComplianceStatusRequestUserType": ".compliance",
+ "GetComplianceStatusResponse": ".compliance",
+ "GetComplianceStatusResponseData": ".compliance",
+ "GetComplianceStatusResponseDataStep": ".compliance",
+ "GetConcurrencyResponse": ".concurrency",
+ "GetConcurrencyResponseData": ".concurrency",
+ "GetConcurrencyResponseDataAgentsItem": ".concurrency",
+ "GetConversationCallIdRecordingDownloadUrlResponse": ".conversations",
+ "GetConversationCallIdRecordingDownloadUrlResponseData": ".conversations",
+ "GetConversationDetailsResponse": ".analytics",
+ "GetConversationDetailsResponseData": ".analytics",
+ "GetConversationDetailsResponseDataEventsItem": ".analytics",
+ "GetConversationDetailsResponseDataTranscriptItem": ".analytics",
+ "GetDailyCallSummaryResponse": ".analytics",
+ "GetDailyCallSummaryResponseData": ".analytics",
+ "GetDashboardResponse": ".analytics",
+ "GetDashboardResponseData": ".analytics",
+ "GetDraftAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "GetDurationStatsResponse": ".analytics",
+ "GetDurationStatsResponseData": ".analytics",
+ "GetHistoryAgentVersioningRevisionsResponse": ".agent_versioning_revisions",
+ "GetHistoryAgentVersioningRevisionsResponseData": ".agent_versioning_revisions",
+ "GetHistoryAgentVersioningRevisionsResponseDataTrailItem": ".agent_versioning_revisions",
+ "GetHourlyPerformanceResponse": ".analytics",
+ "GetHourlyPerformanceResponseData": ".analytics",
+ "GetHourlyPerformanceResponseDataHoursItem": ".analytics",
+ "GetKnowledgeBaseResponse": ".knowledge_base",
+ "GetKnowledgebaseIdItemsResponse": ".knowledge_base",
+ "GetKnowledgebaseIdScrapedUrlsResponse": ".knowledge_base",
+ "GetKnowledgebaseIdScrapedUrlsResponseDataItem": ".knowledge_base",
+ "GetOrganizationResponse": ".organization",
+ "GetOrganizationResponseData": ".organization",
+ "GetOrganizationResponseDataMembersItem": ".organization",
+ "GetOrganizationResponseDataSubscription": ".organization",
+ "GetPhoneNumberTrendsResponse": ".analytics",
+ "GetPhoneNumberTrendsResponseData": ".analytics",
+ "GetPhoneNumberTrendsResponseDataTrendsItem": ".analytics",
+ "GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem": ".analytics",
+ "GetPickupRateByNumberResponse": ".analytics",
+ "GetPickupRateByNumberResponseData": ".analytics",
+ "GetPickupRateByNumberResponseDataNumbersItem": ".analytics",
+ "GetProductAllNumbersResponse": ".phone_numbers",
+ "GetProductAllNumbersResponseData": ".phone_numbers",
+ "GetProductManageSubscriptionResponse": ".phone_numbers",
+ "GetProductManageSubscriptionResponseData": ".phone_numbers",
+ "GetProductProrationAmountResponse": ".phone_numbers",
+ "GetProductProrationAmountResponseData": ".phone_numbers",
+ "GetProductUnpaidInvoicesResponse": ".phone_numbers",
+ "GetProductUnpaidInvoicesResponseData": ".phone_numbers",
+ "GetUsageTimeseriesResponse": ".analytics",
+ "GetUsageTimeseriesResponseData": ".analytics",
+ "GetUsageTimeseriesResponseDataDateRange": ".analytics",
+ "GetUsageTimeseriesResponseDataDayWiseCreditsItem": ".analytics",
+ "GetUserResponse": ".user",
+ "GetUserResponseData": ".user",
+ "GetWebEngageDetailsResponse": ".integrations",
+ "GetWebhookResponse": ".webhooks",
+ "GetWebhookResponseData": ".webhooks",
+ "GetWeeklyTrendsResponse": ".analytics",
+ "GetWeeklyTrendsResponseData": ".analytics",
+ "GetWeeklyTrendsResponseDataWeeksItem": ".analytics",
+ "ImportSipPhoneNumbersResponse": ".phone_numbers",
+ "ImportSipPhoneNumbersResponseData": ".phone_numbers",
+ "ImportSipPhoneNumbersResponseDataAttributes": ".phone_numbers",
+ "ImportSipPhoneNumbersResponseDataProductType": ".phone_numbers",
+ "InternalServerError": ".errors",
+ "InternalServerErrorBody": ".types",
+ "InternalServerErrorResponse": ".types",
+ "KnowledgeBase": ".types",
+ "KnowledgeBaseItem": ".types",
+ "KnowledgeBaseItemItemType": ".types",
+ "KnowledgeBaseItemProcessingStatus": ".types",
+ "KnowledgeBaseProcessingStatus": ".types",
+ "ListAgentTemplatesAgentTemplatesRequestRegion": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponse": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItem": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemCallType": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemRegion": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType": ".agent_templates",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType": ".agent_templates",
+ "ListAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "ListAgentVersioningBranchesResponseData": ".agent_versioning_branches",
+ "ListAgentVersioningRevisionsResponse": ".agent_versioning_revisions",
+ "ListAgentVersioningRevisionsResponseData": ".agent_versioning_revisions",
+ "ListAgentsAgentsRequestSortField": ".agents",
+ "ListAgentsAgentsRequestSortOrder": ".agents",
+ "ListAgentsAgentsRequestType": ".agents",
+ "ListAgentsAgentsResponse": ".agents",
+ "ListAgentsAgentsResponseData": ".agents",
+ "ListAudienceResponse": ".audience",
+ "ListAudienceResponseDataItem": ".audience",
+ "ListAudienceResponseDataItemCampaignsItem": ".audience",
+ "ListCallActionsRequestCategory": ".call_actions",
+ "ListCallActionsResponse": ".call_actions",
+ "ListCallActionsResponseData": ".call_actions",
+ "ListCallsRequestCallTypes": ".calls",
+ "ListCallsRequestSortBy": ".calls",
+ "ListCallsRequestSortOrder": ".calls",
+ "ListCallsResponse": ".calls",
+ "ListCallsResponseData": ".calls",
+ "ListCallsResponseDataDispositionMetricsConfigItem": ".calls",
+ "ListCallsResponseDataLogsItem": ".calls",
+ "ListCallsResponseDataLogsItemAgentDispositionConfigItem": ".calls",
+ "ListCallsResponseDataLogsItemPostCallAnalytics": ".calls",
+ "ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem": ".calls",
+ "ListCallsResponseDataLogsItemStatus": ".calls",
+ "ListCallsResponseDataLogsItemTurnLatencyMetrics": ".calls",
+ "ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem": ".calls",
+ "ListCallsResponseDataLogsItemType": ".calls",
+ "ListCallsResponseDataPagination": ".calls",
+ "ListCampaignsRequestSortField": ".campaigns",
+ "ListCampaignsRequestSortOrder": ".campaigns",
+ "ListCampaignsRequestStatus": ".campaigns",
+ "ListCampaignsResponse": ".campaigns",
+ "ListCampaignsResponseData": ".campaigns",
+ "ListCampaignsResponseDataCampaignsItem": ".campaigns",
+ "ListCampaignsResponseDataCampaignsItemAgent": ".campaigns",
+ "ListCampaignsResponseDataCampaignsItemAudience": ".campaigns",
+ "ListCampaignsResponseDataCampaignsItemStatus": ".campaigns",
+ "ListCampaignsResponseDataPagination": ".campaigns",
+ "ListDispositionMetricTemplatesResponse": ".disposition_metric_templates",
+ "ListDispositionMetricTemplatesResponseDataItem": ".disposition_metric_templates",
+ "ListDispositionMetricTemplatesResponseDataItemDispositionMetricType": ".disposition_metric_templates",
+ "ListDncRequestSortField": ".dnc",
+ "ListDncRequestSortOrder": ".dnc",
+ "ListDncResponse": ".dnc",
+ "ListDncResponseData": ".dnc",
+ "ListDncResponseDataEntriesItem": ".dnc",
+ "ListDncResponseDataPagination": ".dnc",
+ "ListKnowledgeBaseResponse": ".knowledge_base",
+ "ListPhoneNumbersResponse": ".phone_numbers",
+ "ListPhoneNumbersResponseDataItem": ".phone_numbers",
+ "ListPhoneNumbersResponseDataItemAttributes": ".phone_numbers",
+ "ListPhoneNumbersResponseDataItemAttributesProvider": ".phone_numbers",
+ "ListPhoneNumbersResponseDataItemProductType": ".phone_numbers",
+ "ListRetryAttemptsConversationsResponse": ".conversations",
+ "ListRetryAttemptsConversationsResponseData": ".conversations",
+ "LockedError": ".errors",
+ "LockedErrorResponse": ".types",
+ "LockedErrorResponseErrorType": ".types",
+ "MakeLiveAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "ModifyWebEngageIntegrationResponse": ".integrations",
+ "NotFoundError": ".errors",
+ "NotFoundErrorBody": ".types",
+ "PauseCampaignsResponse": ".campaigns",
+ "PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem": ".webhooks",
+ "PostAgentAgentIdWebhookSubscriptionsResponse": ".webhooks",
+ "PostAgentFromTemplateResponse": ".agent_templates",
+ "PostAgentIdDraftsDraftIdTestCallResponse": ".agent_versioning_drafts",
+ "PostAgentIdDraftsDraftIdTestCallResponseData": ".agent_versioning_drafts",
+ "PostAgentIdVersionsVersionIdTestCallResponse": ".agent_versioning_versions",
+ "PostAgentIdVersionsVersionIdTestCallResponseData": ".agent_versioning_versions",
+ "PostAudienceIdMembersResponse": ".audience",
+ "PostAudienceIdMembersResponseDataItem": ".audience",
+ "PostAudienceIdMembersResponseDataItemData": ".audience",
+ "PostAudienceResponse": ".audience",
+ "PostAudienceResponseData": ".audience",
+ "PostCallAnalyticsConfig": ".types",
+ "PostCallAnalyticsConfigSuccessMetricsItem": ".types",
+ "PostCallAnalyticsConfigSuccessMetricsItemSuccessMetricType": ".types",
+ "PostComplianceApplicationsIdRefreshResponse": ".compliance",
+ "PostKnowledgebaseCompeleteFileUploadResponse": ".knowledge_base",
+ "PostKnowledgebaseGetPresignedUrlResponse": ".knowledge_base",
+ "PostKnowledgebaseGetPresignedUrlResponseData": ".knowledge_base",
+ "PostKnowledgebaseIdItemsUploadMediaResponse": ".knowledge_base",
+ "PostKnowledgebaseIdResponse": ".knowledge_base",
+ "PostPromptScoringScoreRequest": ".prompt_scoring",
+ "PostPromptScoringScoreRequestDraftId": ".prompt_scoring",
+ "PostPromptScoringScoreRequestVersionId": ".prompt_scoring",
+ "PostPromptScoringScoreResponse": ".prompt_scoring",
+ "PostPromptScoringScoreResponseData": ".prompt_scoring",
+ "PostPromptScoringScoreResponseDataBand": ".prompt_scoring",
+ "PostPromptScoringScoreResponseDataDimensionsItem": ".prompt_scoring",
+ "PostPromptScoringScoreResponseDataDimensionsItemLevel": ".prompt_scoring",
+ "PostPromptScoringScoreResponseDataOverallGrade": ".prompt_scoring",
+ "Product": ".types",
+ "ProductAttributes": ".types",
+ "ProductAttributesProvider": ".types",
+ "ProductProductType": ".types",
+ "PromptScore": ".types",
+ "PromptScoreDimensionsItem": ".types",
+ "PublishDraftAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "PublishDraftAgentVersioningDraftsResponse": ".agent_versioning_drafts",
+ "PublishResult": ".types",
+ "PublishResultState": ".types",
+ "RegisterCallRealtimeRequestMode": ".realtime",
+ "RegisterCallRealtimeRequestVariablesValue": ".realtime",
+ "RegisterCallRealtimeResponse": ".realtime",
+ "RegisterCallRealtimeResponseData": ".realtime",
+ "ReleasePhoneNumbersResponse": ".phone_numbers",
+ "ReleasePhoneNumbersResponseData": ".phone_numbers",
+ "RenameAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "RenameDraftAgentVersioningDraftsResponse": ".agent_versioning_drafts",
+ "RenameDraftAgentVersioningDraftsResponseData": ".agent_versioning_drafts",
+ "RentPhoneNumbersRequestProvider": ".phone_numbers",
+ "RentPhoneNumbersResponse": ".phone_numbers",
+ "RentPhoneNumbersResponseData": ".phone_numbers",
+ "RequiredDocumentType": ".types",
+ "RequiredDocumentTypeRequiredFieldsItem": ".types",
+ "RestoreAgentVersioningRevisionsResponse": ".agent_versioning_revisions",
+ "ResubmitComplianceResponse": ".compliance",
+ "Revision": ".types",
+ "RevisionPendingPublish": ".types",
+ "RevisionPendingPublishState": ".types",
+ "RevisionSecurityCheck": ".types",
+ "RevisionStatus": ".types",
+ "ScrapeUrlsKnowledgeBaseResponse": ".knowledge_base",
+ "SearchCallsResponse": ".calls",
+ "SearchCallsResponseData": ".calls",
+ "SearchCallsResponseDataLogsItem": ".calls",
+ "SearchCallsResponseDataLogsItemPostCallAnalytics": ".calls",
+ "SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem": ".calls",
+ "SearchCallsResponseDataLogsItemStatus": ".calls",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetrics": ".calls",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem": ".calls",
+ "SearchCallsResponseDataLogsItemType": ".calls",
+ "SearchRentablePhoneNumbersRequestProvider": ".phone_numbers",
+ "SearchRentablePhoneNumbersResponse": ".phone_numbers",
+ "SearchRentablePhoneNumbersResponseDataItem": ".phone_numbers",
+ "SearchRentablePhoneNumbersResponseDataItemProvider": ".phone_numbers",
+ "SinglePromptConfig": ".types",
+ "SinglePromptData": ".types",
+ "StartOrResumeCampaignsResponse": ".campaigns",
+ "StartOrResumeCampaignsResponseData": ".campaigns",
+ "StartOutboundCallCallsRequestVariablesValue": ".calls",
+ "StartOutboundCallCallsRequestXTestCall": ".calls",
+ "StartOutboundCallCallsResponse": ".calls",
+ "StartOutboundCallCallsResponseData": ".calls",
+ "SubmitComplianceRequestNumberType": ".compliance",
+ "SubmitComplianceRequestUserType": ".compliance",
+ "SubmitComplianceResponse": ".compliance",
+ "SubscribeToLiveEventsLiveTranscriptsResponse": ".live_transcripts",
+ "SubscribeToLiveEventsLiveTranscriptsResponseEventType": ".live_transcripts",
+ "SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem": ".live_transcripts",
+ "TestCallAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "TestCallRequest": ".types",
+ "TestCallRequestMode": ".types",
+ "TestCallResult": ".types",
+ "TestCallV2RequestMode": ".agent_versioning_branches",
+ "TooManyRequestsError": ".errors",
+ "TooManyRequestsErrorBody": ".types",
+ "TooManyRequestsErrorBodyRateLimit": ".types",
+ "Tool": ".types",
+ "ToolHeadersArrayItem": ".types",
+ "ToolLlmParametersItem": ".types",
+ "ToolLlmParametersItemType": ".types",
+ "ToolMethod": ".types",
+ "ToolOnHoldMusic": ".types",
+ "ToolQueryParamsItem": ".types",
+ "ToolResponseVariablesItem": ".types",
+ "ToolTransferOption": ".types",
+ "ToolTransferOptionPrivateHandoffOption": ".types",
+ "ToolTransferOptionPrivateHandoffOptionType": ".types",
+ "ToolTransferOptionPublicHandoffOption": ".types",
+ "ToolTransferOptionPublicHandoffOptionType": ".types",
+ "ToolTransferOptionType": ".types",
+ "ToolType": ".types",
+ "ToolVariablesExtractionSchemaItem": ".types",
+ "ToolVariablesExtractionSchemaItemType": ".types",
+ "UnauthorizedError": ".errors",
+ "UnauthorizedErrorResponse": ".types",
+ "UnprocessableEntityError": ".errors",
+ "UpdateAgentAgentsResponse": ".agents",
+ "UpdateAgentWidgetConfigRequestWidgetConfig": ".agents",
+ "UpdateAgentWidgetConfigRequestWidgetConfigMode": ".agents",
+ "UpdateAgentWidgetConfigRequestWidgetConfigPosition": ".agents",
+ "UpdateAgentWidgetConfigRequestWidgetConfigSize": ".agents",
+ "UpdateAgentWidgetConfigRequestWidgetConfigTheme": ".agents",
+ "UpdateAgentWidgetConfigResponse": ".agents",
+ "UpdateBranchDraftRequestBackgroundSound": ".agent_versioning_branches",
+ "UpdateBranchDraftRequestSlmModel": ".agent_versioning_branches",
+ "UpdateCallActionRequestActionType": ".call_actions",
+ "UpdateCallActionRequestCategory": ".call_actions",
+ "UpdateCallActionRequestConfig": ".call_actions",
+ "UpdateCallActionResponse": ".call_actions",
+ "UpdateConcurrencyReservationsRequestReservationsItem": ".concurrency",
+ "UpdateConcurrencyReservationsResponse": ".concurrency",
+ "UpdateConcurrencyReservationsResponseData": ".concurrency",
+ "UpdateDraftAgentVersioningBranchesResponse": ".agent_versioning_branches",
+ "UpdateDraftConfigAgentVersioningDraftsResponse": ".agent_versioning_drafts",
+ "UpdateVersionMetadataAgentVersioningVersionsResponse": ".agent_versioning_versions",
+ "VersionBlocks": ".types",
+ "VersioningV2MigrationRequiredResponse": ".types",
+ "VersioningV2MigrationRequiredResponseErrorType": ".types",
+ "WebEngageIntegrationSet": ".types",
+ "Webhook": ".types",
+ "WebhookAgent": ".types",
+ "WebhookEvent": ".types",
+ "WebhookEventAnalyticsCompleted": ".types",
+ "WebhookEventAnalyticsCompletedMetadata": ".types",
+ "WebhookEventAnalyticsCompletedMetadataAnalytics": ".types",
+ "WebhookEventAnalyticsCompletedMetadataEventType": ".types",
+ "WebhookEventAnalyticsMetric": ".types",
+ "WebhookEventAnalyticsMetricDispositionMetricType": ".types",
+ "WebhookEventAnalyticsMetricValue": ".types",
+ "WebhookEventCallData": ".types",
+ "WebhookEventCallDataCallDirection": ".types",
+ "WebhookEventCallDataCallStatus": ".types",
+ "WebhookEventEnvelope": ".types",
+ "WebhookEventPostConversation": ".types",
+ "WebhookEventPostConversationMetadata": ".types",
+ "WebhookEventPostConversationMetadataEventType": ".types",
+ "WebhookEventPreConversation": ".types",
+ "WebhookEventPreConversationMetadata": ".types",
+ "WebhookEventPreConversationMetadataEventType": ".types",
+ "WebhookEventTranscriptTurn": ".types",
+ "WebhookEventTranscriptTurnRole": ".types",
+ "WebhookStatus": ".types",
+ "WebhookSubscription": ".types",
+ "WebhookSubscriptionEventType": ".types",
+ "WebhookSubscriptionPopulated": ".types",
+ "WebhookSubscriptionPopulatedEventType": ".types",
+ "WidgetConfig": ".types",
+ "WidgetConfigMode": ".types",
+ "WidgetConfigPosition": ".types",
+ "WidgetConfigSize": ".types",
+ "WidgetConfigTheme": ".types",
+ "WorkflowGraphData": ".types",
+ "WorkflowGraphDataEdgesItem": ".types",
+ "WorkflowGraphDataNodesItem": ".types",
+ "WorkflowGraphDataNodesItemPosition": ".types",
+ "WorkflowType": ".types",
"agent_templates": ".agent_templates",
+ "agent_versioning_branches": ".agent_versioning_branches",
"agent_versioning_drafts": ".agent_versioning_drafts",
+ "agent_versioning_revisions": ".agent_versioning_revisions",
"agent_versioning_versions": ".agent_versioning_versions",
"agents": ".agents",
+ "analytics": ".analytics",
"audience": ".audience",
+ "call_actions": ".call_actions",
"calls": ".calls",
"campaigns": ".campaigns",
"compliance": ".compliance",
+ "concurrency": ".concurrency",
"conversations": ".conversations",
+ "disposition_metric_templates": ".disposition_metric_templates",
"dnc": ".dnc",
+ "integrations": ".integrations",
"knowledge_base": ".knowledge_base",
"live_transcripts": ".live_transcripts",
"organization": ".organization",
"phone_numbers": ".phone_numbers",
"prompt_scoring": ".prompt_scoring",
+ "realtime": ".realtime",
"user": ".user",
"webhooks": ".webhooks",
}
@@ -74,22 +1264,595 @@ def __dir__():
__all__ = [
+ "ActivateVersionAgentVersioningVersionsResponse",
+ "AgentDto",
+ "AgentDtoBackgroundSound",
+ "AgentDtoConfigSource",
+ "AgentDtoDenoisingConfig",
+ "AgentDtoLanguage",
+ "AgentDtoLanguageDefault",
+ "AgentDtoLanguageSwitching",
+ "AgentDtoLlmIdleTimeoutConfig",
+ "AgentDtoPreCallApi",
+ "AgentDtoPreCallApiMethod",
+ "AgentDtoPreCallApiResponseVariablesItem",
+ "AgentDtoPronunciationDictsItem",
+ "AgentDtoRedactionConfig",
+ "AgentDtoResolvedConfig",
+ "AgentDtoResolvedConfigDefaultLanguage",
+ "AgentDtoSessionTimeoutConfig",
+ "AgentDtoSlmModel",
+ "AgentDtoSmartTurnConfig",
+ "AgentDtoSynthesizer",
+ "AgentDtoSynthesizerVoiceConfig",
+ "AgentDtoSynthesizerVoiceConfigGender",
+ "AgentDtoSynthesizerVoiceConfigModel",
+ "AgentDtoTimezone",
+ "AgentDtoVersionedWorkflow",
+ "AgentDtoVoiceDetectionConfig",
+ "AgentDtoVoiceMailDetectionConfig",
+ "AgentDtoWidgetConfig",
+ "AgentDtoWidgetConfigMode",
+ "AgentDtoWidgetConfigPosition",
+ "AgentDtoWidgetConfigSize",
+ "AgentDtoWidgetConfigTheme",
+ "AgentVersion",
+ "AgentVersionBlocks",
+ "AgentVersionDiff",
+ "AgentVersionDiffDiffsItem",
+ "AgentVersionDiffDiffsItemChangesItem",
+ "AgentVersionDiffDiffsItemChangesItemNewValue",
+ "AgentVersionDiffDiffsItemChangesItemOldValue",
+ "AgentVersionMetrics",
+ "AgentVersionMetricsComparison",
+ "AgentVersionMetricsComparisonMetrics",
+ "AgentVersionMetricsComparisonMetricsDeltas",
+ "AgentVersionStatus",
+ "AnalyticsDateRange",
+ "AnalyticsTrendMetric",
+ "ApiResponse",
+ "ArchiveAgentAgentsResponse",
+ "ArchiveAgentAgentsResponseData",
+ "ArchiveAgentAgentsResponseDataMessage",
+ "ArchiveAgentVersioningBranchesResponse",
+ "ArchiveAgentVersioningBranchesResponseData",
+ "BadGatewayError",
+ "BadRequestError",
+ "BadRequestErrorBody",
+ "BadRequestErrorResponse",
+ "Branch",
+ "BranchStatus",
+ "BranchSummary",
+ "CallAction",
+ "CallActionActionType",
+ "CallActionCategory",
+ "CallActionCondition",
+ "CallActionConditionOperator",
+ "CallActionConfig",
+ "CancelConversationsResponse",
+ "CancelConversationsResponseData",
+ "CancelPublishAgentVersioningBranchesResponse",
+ "CancelPublishAgentVersioningBranchesResponseData",
+ "CancelQueuedConversationsResponse",
+ "CancelQueuedConversationsResponseData",
+ "ComplianceApplication",
+ "ComplianceApplicationNumberType",
+ "ComplianceApplicationStatus",
+ "ComplianceApplicationUserType",
+ "ComplianceRequirement",
+ "ConflictError",
+ "ConflictErrorResponse",
+ "CreateAgentAgentsResponse",
+ "CreateAgentRequestBackgroundSound",
+ "CreateAgentRequestDenoisingConfig",
+ "CreateAgentRequestLanguage",
+ "CreateAgentRequestLanguageDefault",
+ "CreateAgentRequestLanguageSupportedItem",
+ "CreateAgentRequestLanguageSwitching",
+ "CreateAgentRequestLlmIdleTimeoutConfig",
+ "CreateAgentRequestPreCallApi",
+ "CreateAgentRequestPreCallApiMethod",
+ "CreateAgentRequestPreCallApiResponseVariablesItem",
+ "CreateAgentRequestPronunciationDictsItem",
+ "CreateAgentRequestRedactionConfig",
+ "CreateAgentRequestSessionTimeoutConfig",
+ "CreateAgentRequestSlmModel",
+ "CreateAgentRequestSmartTurnConfig",
+ "CreateAgentRequestSynthesizer",
+ "CreateAgentRequestSynthesizerVoiceConfig",
+ "CreateAgentRequestSynthesizerVoiceConfigGender",
+ "CreateAgentRequestSynthesizerVoiceConfigModel",
+ "CreateAgentRequestTimezone",
+ "CreateAgentRequestVoiceDetectionConfig",
+ "CreateAgentRequestVoiceMailDetectionConfig",
+ "CreateBranchAgentVersioningBranchesResponse",
+ "CreateCallActionRequestActionType",
+ "CreateCallActionRequestCategory",
+ "CreateCallActionRequestConfig",
+ "CreateCallActionResponse",
+ "CreateCampaignsResponse",
+ "CreateCampaignsResponseData",
+ "CreateCampaignsResponseDataStatus",
+ "CreateDraftAgentVersioningDraftsResponse",
+ "CreateKnowledgeBaseResponse",
+ "CreateWebhooksRequestEventsItem",
+ "CreateWebhooksRequestEventsItemEventType",
+ "CreateWebhooksResponse",
+ "DeleteAgentAgentIdWebhookSubscriptionsResponse",
+ "DeleteAudienceIdMembersResponse",
+ "DeleteAudienceIdMembersResponseData",
+ "DeleteAudienceIdResponse",
+ "DeleteCallActionResponse",
+ "DeleteCallActionResponseData",
+ "DeleteCampaignsResponse",
+ "DeleteKnowledgeBaseResponse",
+ "DeleteKnowledgebaseKnowledgeBaseIdItemsKnowledgeBaseItemIdResponse",
+ "DeleteKnowledgebaseKnowledgeBaseIdScrapedUrlsKnowledgeBaseScrapedUrlsIdResponse",
+ "DeleteWebhooksResponse",
+ "DiffAgentVersioningRevisionsResponse",
+ "DiffChange",
+ "DiffResult",
+ "DiffSection",
+ "DiscardDraftAgentVersioningBranchesResponse",
+ "DiscardDraftAgentVersioningBranchesResponseData",
+ "DiscardDraftAgentVersioningDraftsResponse",
+ "DispositionMetric",
+ "DispositionMetricDispositionMetricType",
+ "DraftConfigRequestBackgroundSound",
+ "DraftConfigRequestSlmModel",
+ "DraftConflictError",
+ "DraftConflictErrorData",
+ "DraftConflictErrorDataConflict",
+ "DraftDetail",
+ "DraftDetailEditHistoryItem",
+ "DraftEditHistoryEntry",
+ "DuplicateAgentAgentsResponse",
+ "DuplicateAgentAgentsResponseData",
+ "ExtractSitemapUrlsKnowledgeBaseResponse",
+ "ExtractSitemapUrlsKnowledgeBaseResponseData",
+ "ForbiddenError",
+ "ForbiddenErrorBody",
+ "GetAgentAgentIdWebhookSubscriptionsResponse",
+ "GetAgentAgentsResponse",
+ "GetAgentAvatarPresignedUrlResponse",
+ "GetAgentAvatarPresignedUrlResponseData",
+ "GetAgentCallLogsResponse",
+ "GetAgentCallLogsResponseData",
+ "GetAgentCallLogsResponseDataCallLogsItem",
+ "GetAgentIdDraftsDraftIdDiffResponse",
+ "GetAgentIdDraftsDraftIdDiffResponseData",
+ "GetAgentIdDraftsDraftIdDiffResponseDataSectionsValue",
+ "GetAgentIdDraftsDraftIdResponse",
+ "GetAgentIdDraftsDraftIdResponseData",
+ "GetAgentIdDraftsResponse",
+ "GetAgentIdDraftsResponseDataItem",
+ "GetAgentIdVersionsDiffResponse",
+ "GetAgentIdVersionsResponse",
+ "GetAgentIdVersionsResponseData",
+ "GetAgentIdVersionsResponseDataVersionsItem",
+ "GetAgentIdVersionsVersionIdResponse",
+ "GetAgentIdVersionsVersionIdResponseData",
+ "GetAgentPerformanceRequestSortOrder",
+ "GetAgentPerformanceResponse",
+ "GetAgentPerformanceResponseData",
+ "GetAgentPerformanceResponseDataAgentsItem",
+ "GetAgentVersioningBranchesResponse",
+ "GetAgentVersioningRevisionsResponse",
+ "GetAgentVersioningRevisionsResponseData",
+ "GetAgentWidgetConfigResponse",
+ "GetAgentWidgetConfigResponseData",
+ "GetAgentWidgetConfigResponseDataMode",
+ "GetAgentWidgetConfigResponseDataPosition",
+ "GetAgentWidgetConfigResponseDataSize",
+ "GetAgentWidgetConfigResponseDataTheme",
+ "GetAnalyticsConcurrencyRequestIncludeAgents",
+ "GetAnalyticsConcurrencyResponse",
+ "GetAnalyticsConcurrencyResponseData",
+ "GetAnalyticsConcurrencyResponseDataByAgentItem",
+ "GetAnalyticsConcurrencyResponseDataDataPointsItem",
+ "GetAnalyticsSummaryResponse",
+ "GetAnalyticsSummaryResponseData",
+ "GetAttemptCohortResponse",
+ "GetAttemptCohortResponseData",
+ "GetAttemptCohortResponseDataCohortsItem",
+ "GetAudienceIdMembersResponse",
+ "GetAudienceIdMembersResponseData",
+ "GetAudienceIdMembersResponseDataMembersItem",
+ "GetAudienceIdMembersSearchResponse",
+ "GetAudienceIdMembersSearchResponseData",
+ "GetAudienceIdMembersSearchResponseDataMembersItem",
+ "GetAudienceIdMembersSearchResponseDataSearchInfo",
+ "GetAudienceIdMembersSearchResponseDataSearchInfoSearchType",
+ "GetAudienceResponse",
+ "GetAudienceResponseData",
+ "GetCallActionResponse",
+ "GetCallCountsByDayResponse",
+ "GetCallCountsByDayResponseData",
+ "GetCallCountsByDayResponseDataCallCountsByDayItem",
+ "GetCallCountsLogResponse",
+ "GetCallCountsLogResponseData",
+ "GetCallCountsLogResponseDataCallsItem",
+ "GetCallOutcomesTimeseriesResponse",
+ "GetCallOutcomesTimeseriesResponseData",
+ "GetCallOutcomesTimeseriesResponseDataDataPointsItem",
+ "GetCallOutcomesTimeseriesResponseDataTotals",
+ "GetCallStartDistributionResponse",
+ "GetCallStartDistributionResponseData",
+ "GetCallStartDistributionResponseDataDataPointsItem",
+ "GetCallVolumeTimeseriesResponse",
+ "GetCallVolumeTimeseriesResponseData",
+ "GetCallVolumeTimeseriesResponseDataDataPointsItem",
+ "GetCallsResponse",
+ "GetCallsResponseData",
+ "GetCallsResponseDataPostCallAnalytics",
+ "GetCallsResponseDataPostCallAnalyticsDispositionMetricsItem",
+ "GetCallsResponseDataStatus",
+ "GetCallsResponseDataTranscriptItem",
+ "GetCallsResponseDataTranscriptItemRole",
+ "GetCallsResponseDataTurnLatencyMetrics",
+ "GetCallsResponseDataTurnLatencyMetricsTransitionsItem",
+ "GetCallsResponseDataType",
+ "GetCallsResponseDataVoiceConfigUsed",
+ "GetCampaignsResponse",
+ "GetCampaignsResponseData",
+ "GetCampaignsResponseDataCampaign",
+ "GetCampaignsResponseDataCampaignAgent",
+ "GetCampaignsResponseDataCampaignAudience",
+ "GetCampaignsResponseDataCampaignStatus",
+ "GetCampaignsResponseDataEventsItem",
+ "GetCampaignsResponseDataMetrics",
+ "GetComplianceRequirementsRequestNumberType",
+ "GetComplianceRequirementsRequestUserType",
+ "GetComplianceRequirementsResponse",
+ "GetComplianceStatusRequestNumberType",
+ "GetComplianceStatusRequestUserType",
+ "GetComplianceStatusResponse",
+ "GetComplianceStatusResponseData",
+ "GetComplianceStatusResponseDataStep",
+ "GetConcurrencyResponse",
+ "GetConcurrencyResponseData",
+ "GetConcurrencyResponseDataAgentsItem",
+ "GetConversationCallIdRecordingDownloadUrlResponse",
+ "GetConversationCallIdRecordingDownloadUrlResponseData",
+ "GetConversationDetailsResponse",
+ "GetConversationDetailsResponseData",
+ "GetConversationDetailsResponseDataEventsItem",
+ "GetConversationDetailsResponseDataTranscriptItem",
+ "GetDailyCallSummaryResponse",
+ "GetDailyCallSummaryResponseData",
+ "GetDashboardResponse",
+ "GetDashboardResponseData",
+ "GetDraftAgentVersioningBranchesResponse",
+ "GetDurationStatsResponse",
+ "GetDurationStatsResponseData",
+ "GetHistoryAgentVersioningRevisionsResponse",
+ "GetHistoryAgentVersioningRevisionsResponseData",
+ "GetHistoryAgentVersioningRevisionsResponseDataTrailItem",
+ "GetHourlyPerformanceResponse",
+ "GetHourlyPerformanceResponseData",
+ "GetHourlyPerformanceResponseDataHoursItem",
+ "GetKnowledgeBaseResponse",
+ "GetKnowledgebaseIdItemsResponse",
+ "GetKnowledgebaseIdScrapedUrlsResponse",
+ "GetKnowledgebaseIdScrapedUrlsResponseDataItem",
+ "GetOrganizationResponse",
+ "GetOrganizationResponseData",
+ "GetOrganizationResponseDataMembersItem",
+ "GetOrganizationResponseDataSubscription",
+ "GetPhoneNumberTrendsResponse",
+ "GetPhoneNumberTrendsResponseData",
+ "GetPhoneNumberTrendsResponseDataTrendsItem",
+ "GetPhoneNumberTrendsResponseDataTrendsItemDataPointsItem",
+ "GetPickupRateByNumberResponse",
+ "GetPickupRateByNumberResponseData",
+ "GetPickupRateByNumberResponseDataNumbersItem",
+ "GetProductAllNumbersResponse",
+ "GetProductAllNumbersResponseData",
+ "GetProductManageSubscriptionResponse",
+ "GetProductManageSubscriptionResponseData",
+ "GetProductProrationAmountResponse",
+ "GetProductProrationAmountResponseData",
+ "GetProductUnpaidInvoicesResponse",
+ "GetProductUnpaidInvoicesResponseData",
+ "GetUsageTimeseriesResponse",
+ "GetUsageTimeseriesResponseData",
+ "GetUsageTimeseriesResponseDataDateRange",
+ "GetUsageTimeseriesResponseDataDayWiseCreditsItem",
+ "GetUserResponse",
+ "GetUserResponseData",
+ "GetWebEngageDetailsResponse",
+ "GetWebhookResponse",
+ "GetWebhookResponseData",
+ "GetWeeklyTrendsResponse",
+ "GetWeeklyTrendsResponseData",
+ "GetWeeklyTrendsResponseDataWeeksItem",
+ "ImportSipPhoneNumbersResponse",
+ "ImportSipPhoneNumbersResponseData",
+ "ImportSipPhoneNumbersResponseDataAttributes",
+ "ImportSipPhoneNumbersResponseDataProductType",
+ "InternalServerError",
+ "InternalServerErrorBody",
+ "InternalServerErrorResponse",
+ "KnowledgeBase",
+ "KnowledgeBaseItem",
+ "KnowledgeBaseItemItemType",
+ "KnowledgeBaseItemProcessingStatus",
+ "KnowledgeBaseProcessingStatus",
+ "ListAgentTemplatesAgentTemplatesRequestRegion",
+ "ListAgentTemplatesAgentTemplatesResponse",
+ "ListAgentTemplatesAgentTemplatesResponseDataItem",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemCallType",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemDefaultLanguage",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemRegion",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfig",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItem",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemSinglePromptConfigToolsItemType",
+ "ListAgentTemplatesAgentTemplatesResponseDataItemWorkflowType",
+ "ListAgentVersioningBranchesResponse",
+ "ListAgentVersioningBranchesResponseData",
+ "ListAgentVersioningRevisionsResponse",
+ "ListAgentVersioningRevisionsResponseData",
+ "ListAgentsAgentsRequestSortField",
+ "ListAgentsAgentsRequestSortOrder",
+ "ListAgentsAgentsRequestType",
+ "ListAgentsAgentsResponse",
+ "ListAgentsAgentsResponseData",
+ "ListAudienceResponse",
+ "ListAudienceResponseDataItem",
+ "ListAudienceResponseDataItemCampaignsItem",
+ "ListCallActionsRequestCategory",
+ "ListCallActionsResponse",
+ "ListCallActionsResponseData",
+ "ListCallsRequestCallTypes",
+ "ListCallsRequestSortBy",
+ "ListCallsRequestSortOrder",
+ "ListCallsResponse",
+ "ListCallsResponseData",
+ "ListCallsResponseDataDispositionMetricsConfigItem",
+ "ListCallsResponseDataLogsItem",
+ "ListCallsResponseDataLogsItemAgentDispositionConfigItem",
+ "ListCallsResponseDataLogsItemPostCallAnalytics",
+ "ListCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem",
+ "ListCallsResponseDataLogsItemStatus",
+ "ListCallsResponseDataLogsItemTurnLatencyMetrics",
+ "ListCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem",
+ "ListCallsResponseDataLogsItemType",
+ "ListCallsResponseDataPagination",
+ "ListCampaignsRequestSortField",
+ "ListCampaignsRequestSortOrder",
+ "ListCampaignsRequestStatus",
+ "ListCampaignsResponse",
+ "ListCampaignsResponseData",
+ "ListCampaignsResponseDataCampaignsItem",
+ "ListCampaignsResponseDataCampaignsItemAgent",
+ "ListCampaignsResponseDataCampaignsItemAudience",
+ "ListCampaignsResponseDataCampaignsItemStatus",
+ "ListCampaignsResponseDataPagination",
+ "ListDispositionMetricTemplatesResponse",
+ "ListDispositionMetricTemplatesResponseDataItem",
+ "ListDispositionMetricTemplatesResponseDataItemDispositionMetricType",
+ "ListDncRequestSortField",
+ "ListDncRequestSortOrder",
+ "ListDncResponse",
+ "ListDncResponseData",
+ "ListDncResponseDataEntriesItem",
+ "ListDncResponseDataPagination",
+ "ListKnowledgeBaseResponse",
+ "ListPhoneNumbersResponse",
+ "ListPhoneNumbersResponseDataItem",
+ "ListPhoneNumbersResponseDataItemAttributes",
+ "ListPhoneNumbersResponseDataItemAttributesProvider",
+ "ListPhoneNumbersResponseDataItemProductType",
+ "ListRetryAttemptsConversationsResponse",
+ "ListRetryAttemptsConversationsResponseData",
+ "LockedError",
+ "LockedErrorResponse",
+ "LockedErrorResponseErrorType",
+ "MakeLiveAgentVersioningBranchesResponse",
+ "ModifyWebEngageIntegrationResponse",
+ "NotFoundError",
+ "NotFoundErrorBody",
+ "PauseCampaignsResponse",
+ "PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem",
+ "PostAgentAgentIdWebhookSubscriptionsResponse",
+ "PostAgentFromTemplateResponse",
+ "PostAgentIdDraftsDraftIdTestCallResponse",
+ "PostAgentIdDraftsDraftIdTestCallResponseData",
+ "PostAgentIdVersionsVersionIdTestCallResponse",
+ "PostAgentIdVersionsVersionIdTestCallResponseData",
+ "PostAudienceIdMembersResponse",
+ "PostAudienceIdMembersResponseDataItem",
+ "PostAudienceIdMembersResponseDataItemData",
+ "PostAudienceResponse",
+ "PostAudienceResponseData",
+ "PostCallAnalyticsConfig",
+ "PostCallAnalyticsConfigSuccessMetricsItem",
+ "PostCallAnalyticsConfigSuccessMetricsItemSuccessMetricType",
+ "PostComplianceApplicationsIdRefreshResponse",
+ "PostKnowledgebaseCompeleteFileUploadResponse",
+ "PostKnowledgebaseGetPresignedUrlResponse",
+ "PostKnowledgebaseGetPresignedUrlResponseData",
+ "PostKnowledgebaseIdItemsUploadMediaResponse",
+ "PostKnowledgebaseIdResponse",
+ "PostPromptScoringScoreRequest",
+ "PostPromptScoringScoreRequestDraftId",
+ "PostPromptScoringScoreRequestVersionId",
+ "PostPromptScoringScoreResponse",
+ "PostPromptScoringScoreResponseData",
+ "PostPromptScoringScoreResponseDataBand",
+ "PostPromptScoringScoreResponseDataDimensionsItem",
+ "PostPromptScoringScoreResponseDataDimensionsItemLevel",
+ "PostPromptScoringScoreResponseDataOverallGrade",
+ "Product",
+ "ProductAttributes",
+ "ProductAttributesProvider",
+ "ProductProductType",
+ "PromptScore",
+ "PromptScoreDimensionsItem",
+ "PublishDraftAgentVersioningBranchesResponse",
+ "PublishDraftAgentVersioningDraftsResponse",
+ "PublishResult",
+ "PublishResultState",
+ "RegisterCallRealtimeRequestMode",
+ "RegisterCallRealtimeRequestVariablesValue",
+ "RegisterCallRealtimeResponse",
+ "RegisterCallRealtimeResponseData",
+ "ReleasePhoneNumbersResponse",
+ "ReleasePhoneNumbersResponseData",
+ "RenameAgentVersioningBranchesResponse",
+ "RenameDraftAgentVersioningDraftsResponse",
+ "RenameDraftAgentVersioningDraftsResponseData",
+ "RentPhoneNumbersRequestProvider",
+ "RentPhoneNumbersResponse",
+ "RentPhoneNumbersResponseData",
+ "RequiredDocumentType",
+ "RequiredDocumentTypeRequiredFieldsItem",
+ "RestoreAgentVersioningRevisionsResponse",
+ "ResubmitComplianceResponse",
+ "Revision",
+ "RevisionPendingPublish",
+ "RevisionPendingPublishState",
+ "RevisionSecurityCheck",
+ "RevisionStatus",
+ "ScrapeUrlsKnowledgeBaseResponse",
+ "SearchCallsResponse",
+ "SearchCallsResponseData",
+ "SearchCallsResponseDataLogsItem",
+ "SearchCallsResponseDataLogsItemPostCallAnalytics",
+ "SearchCallsResponseDataLogsItemPostCallAnalyticsDispositionMetricsItem",
+ "SearchCallsResponseDataLogsItemStatus",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetrics",
+ "SearchCallsResponseDataLogsItemTurnLatencyMetricsTransitionsItem",
+ "SearchCallsResponseDataLogsItemType",
+ "SearchRentablePhoneNumbersRequestProvider",
+ "SearchRentablePhoneNumbersResponse",
+ "SearchRentablePhoneNumbersResponseDataItem",
+ "SearchRentablePhoneNumbersResponseDataItemProvider",
+ "SinglePromptConfig",
+ "SinglePromptData",
+ "StartOrResumeCampaignsResponse",
+ "StartOrResumeCampaignsResponseData",
+ "StartOutboundCallCallsRequestVariablesValue",
+ "StartOutboundCallCallsRequestXTestCall",
+ "StartOutboundCallCallsResponse",
+ "StartOutboundCallCallsResponseData",
+ "SubmitComplianceRequestNumberType",
+ "SubmitComplianceRequestUserType",
+ "SubmitComplianceResponse",
+ "SubscribeToLiveEventsLiveTranscriptsResponse",
+ "SubscribeToLiveEventsLiveTranscriptsResponseEventType",
+ "SubscribeToLiveEventsLiveTranscriptsResponseMetricsItem",
+ "TestCallAgentVersioningBranchesResponse",
+ "TestCallRequest",
+ "TestCallRequestMode",
+ "TestCallResult",
+ "TestCallV2RequestMode",
+ "TooManyRequestsError",
+ "TooManyRequestsErrorBody",
+ "TooManyRequestsErrorBodyRateLimit",
+ "Tool",
+ "ToolHeadersArrayItem",
+ "ToolLlmParametersItem",
+ "ToolLlmParametersItemType",
+ "ToolMethod",
+ "ToolOnHoldMusic",
+ "ToolQueryParamsItem",
+ "ToolResponseVariablesItem",
+ "ToolTransferOption",
+ "ToolTransferOptionPrivateHandoffOption",
+ "ToolTransferOptionPrivateHandoffOptionType",
+ "ToolTransferOptionPublicHandoffOption",
+ "ToolTransferOptionPublicHandoffOptionType",
+ "ToolTransferOptionType",
+ "ToolType",
+ "ToolVariablesExtractionSchemaItem",
+ "ToolVariablesExtractionSchemaItemType",
+ "UnauthorizedError",
+ "UnauthorizedErrorResponse",
+ "UnprocessableEntityError",
+ "UpdateAgentAgentsResponse",
+ "UpdateAgentWidgetConfigRequestWidgetConfig",
+ "UpdateAgentWidgetConfigRequestWidgetConfigMode",
+ "UpdateAgentWidgetConfigRequestWidgetConfigPosition",
+ "UpdateAgentWidgetConfigRequestWidgetConfigSize",
+ "UpdateAgentWidgetConfigRequestWidgetConfigTheme",
+ "UpdateAgentWidgetConfigResponse",
+ "UpdateBranchDraftRequestBackgroundSound",
+ "UpdateBranchDraftRequestSlmModel",
+ "UpdateCallActionRequestActionType",
+ "UpdateCallActionRequestCategory",
+ "UpdateCallActionRequestConfig",
+ "UpdateCallActionResponse",
+ "UpdateConcurrencyReservationsRequestReservationsItem",
+ "UpdateConcurrencyReservationsResponse",
+ "UpdateConcurrencyReservationsResponseData",
+ "UpdateDraftAgentVersioningBranchesResponse",
+ "UpdateDraftConfigAgentVersioningDraftsResponse",
+ "UpdateVersionMetadataAgentVersioningVersionsResponse",
+ "VersionBlocks",
+ "VersioningV2MigrationRequiredResponse",
+ "VersioningV2MigrationRequiredResponseErrorType",
+ "WebEngageIntegrationSet",
+ "Webhook",
+ "WebhookAgent",
+ "WebhookEvent",
+ "WebhookEventAnalyticsCompleted",
+ "WebhookEventAnalyticsCompletedMetadata",
+ "WebhookEventAnalyticsCompletedMetadataAnalytics",
+ "WebhookEventAnalyticsCompletedMetadataEventType",
+ "WebhookEventAnalyticsMetric",
+ "WebhookEventAnalyticsMetricDispositionMetricType",
+ "WebhookEventAnalyticsMetricValue",
+ "WebhookEventCallData",
+ "WebhookEventCallDataCallDirection",
+ "WebhookEventCallDataCallStatus",
+ "WebhookEventEnvelope",
+ "WebhookEventPostConversation",
+ "WebhookEventPostConversationMetadata",
+ "WebhookEventPostConversationMetadataEventType",
+ "WebhookEventPreConversation",
+ "WebhookEventPreConversationMetadata",
+ "WebhookEventPreConversationMetadataEventType",
+ "WebhookEventTranscriptTurn",
+ "WebhookEventTranscriptTurnRole",
+ "WebhookStatus",
+ "WebhookSubscription",
+ "WebhookSubscriptionEventType",
+ "WebhookSubscriptionPopulated",
+ "WebhookSubscriptionPopulatedEventType",
"WidgetConfig",
+ "WidgetConfigMode",
+ "WidgetConfigPosition",
+ "WidgetConfigSize",
+ "WidgetConfigTheme",
+ "WorkflowGraphData",
+ "WorkflowGraphDataEdgesItem",
+ "WorkflowGraphDataNodesItem",
+ "WorkflowGraphDataNodesItemPosition",
+ "WorkflowType",
"agent_templates",
+ "agent_versioning_branches",
"agent_versioning_drafts",
+ "agent_versioning_revisions",
"agent_versioning_versions",
"agents",
+ "analytics",
"audience",
+ "call_actions",
"calls",
"campaigns",
"compliance",
+ "concurrency",
"conversations",
+ "disposition_metric_templates",
"dnc",
+ "integrations",
"knowledge_base",
"live_transcripts",
"organization",
"phone_numbers",
"prompt_scoring",
+ "realtime",
"user",
"webhooks",
]
diff --git a/src/smallestai/atoms/helpers/__init__.py b/src/smallestai/atoms/helpers/__init__.py
index a52f341f..80b89ebb 100644
--- a/src/smallestai/atoms/helpers/__init__.py
+++ b/src/smallestai/atoms/helpers/__init__.py
@@ -5,5 +5,28 @@
from smallestai.atoms.helpers.campaign import Campaign
from smallestai.atoms.helpers.kb import KB
from smallestai.atoms.helpers._envelope import Page, as_page, require_id
+from smallestai.atoms.helpers.versioning import (
+ Versioning,
+ VersioningError,
+ MigrationRequiredError,
+ DraftConflictError,
+ BaseRevisionUnavailableError,
+ SecurityCheckFailedError,
+)
-__all__ = ["Audience", "CallAnalytics", "Call", "Campaign", "KB", "Page", "as_page", "require_id"]
+__all__ = [
+ "Audience",
+ "CallAnalytics",
+ "Call",
+ "Campaign",
+ "KB",
+ "Page",
+ "as_page",
+ "require_id",
+ "Versioning",
+ "VersioningError",
+ "MigrationRequiredError",
+ "DraftConflictError",
+ "BaseRevisionUnavailableError",
+ "SecurityCheckFailedError",
+]
diff --git a/src/smallestai/atoms/helpers/versioning.py b/src/smallestai/atoms/helpers/versioning.py
new file mode 100644
index 00000000..1bd584d8
--- /dev/null
+++ b/src/smallestai/atoms/helpers/versioning.py
@@ -0,0 +1,234 @@
+"""Ergonomic helpers over the generated Agent Versioning v2 (branch/revision) client.
+
+Fern generates the raw branch/revision methods and the typed models. This module
+adds the two things Fern cannot generate for any SDK:
+
+ 1. `wait_for_commit` / `publish_and_wait` โ publishing a draft returns only
+ `{state: "scanning"}` (HTTP 202) while a security scan runs; there is no
+ revision id in that response. The caller has to list the branch's newest
+ revision and poll it until `status == "published"`. This wraps that loop.
+
+ 2. A discriminator for the three 409 flavors on draft writes, which the raw
+ client all surfaces as a single `ConflictError`:
+ - error_type == "versioning_v2_migration_required" -> MigrationRequiredError
+ - data.conflict{expectedRevision, latestRevision, diffs} -> DraftConflictError
+ - errors == ["base_revision_unavailable"] -> BaseRevisionUnavailableError
+
+This is a thin wrapper over `client.atoms.agent_versioning_branches` /
+`.agent_versioning_revisions`, so it rides future regens without changes.
+"""
+from __future__ import annotations
+
+import time
+import typing
+
+from ...core.api_error import ApiError
+
+
+class VersioningError(Exception):
+ """Base class for versioning helper errors."""
+
+
+class MigrationRequiredError(VersioningError):
+ """A v1 versioning endpoint was called while the branch model is enabled.
+
+ Migrate to the branch/revision API (client.atoms.agent_versioning_branches).
+ """
+
+
+class DraftConflictError(VersioningError):
+ """Optimistic-concurrency conflict on update_draft.
+
+ `expectedRevision` did not match the branch's current draft revision because
+ another edit landed first. Inspect `.diffs`, then either rebase on
+ `.latest_revision` or force-overwrite by resending without expectedRevision.
+ """
+
+ def __init__(self, conflict: typing.Dict[str, typing.Any]):
+ self.conflict = conflict
+ self.expected_revision = conflict.get("expectedRevision")
+ self.latest_revision = conflict.get("latestRevision")
+ self.diffs = conflict.get("diffs") or []
+ # `fields` is the lean prod variant: a list of "." paths
+ # that conflicted, without the structured expected/latest/diffs detail.
+ self.fields = conflict.get("fields") or []
+ if self.expected_revision is not None or self.latest_revision is not None:
+ detail = (
+ f"based on r{self.expected_revision}, latest is r{self.latest_revision} "
+ f"({len(self.diffs)} section(s) changed)"
+ )
+ else:
+ detail = "conflicting fields: " + (", ".join(self.fields) or "unknown")
+ super().__init__(f"draft conflict: {detail}")
+
+
+class BaseRevisionUnavailableError(VersioningError):
+ """`expectedRevision` referenced a base revision that does not exist / is unavailable."""
+
+
+class SecurityCheckFailedError(VersioningError):
+ """The published revision's security scan came back failed."""
+
+
+def _body_dict(err: ApiError) -> typing.Dict[str, typing.Any]:
+ """Best-effort extraction of the raw JSON body from an ApiError.
+
+ `body` is typed `Any`; depending on whether the generator emitted a typed
+ error class or fell through to the base ApiError, it may be a dict or a
+ pydantic model (with extras preserved). Handle both.
+ """
+ b = getattr(err, "body", None)
+ if isinstance(b, dict):
+ return b
+ for meth in ("model_dump", "dict"):
+ fn = getattr(b, meth, None)
+ if callable(fn):
+ try:
+ out = fn()
+ if isinstance(out, dict):
+ return out
+ except Exception:
+ pass
+ return {}
+
+
+def _discriminate_conflict(err: ApiError) -> Exception:
+ body = _body_dict(err)
+ if body.get("error_type") == "versioning_v2_migration_required":
+ return MigrationRequiredError(str(body.get("message") or body))
+ data = body.get("data")
+ if isinstance(data, dict) and isinstance(data.get("conflict"), dict):
+ return DraftConflictError(data["conflict"]) # dev/rich variant
+ errors = body.get("errors") or []
+ if isinstance(errors, list):
+ if "base_revision_unavailable" in errors:
+ return BaseRevisionUnavailableError(str(body))
+ # Prod/lean variant: 409 whose `errors` are "." conflict
+ # paths, without the structured data.conflict block. Still a draft conflict.
+ if errors:
+ return DraftConflictError({"fields": errors})
+ return err # unknown 409 shape: let the original propagate
+
+
+def _state(obj: typing.Any) -> typing.Optional[str]:
+ s = getattr(obj, "state", None)
+ return str(s) if s is not None else None
+
+
+class Versioning:
+ """Thin ergonomic facade over the generated v2 versioning client.
+
+ Usage:
+ from smallestai import SmallestAI
+ from smallestai.atoms.helpers.versioning import Versioning
+
+ client = SmallestAI(api_key="...")
+ v = Versioning(client)
+
+ branch = v.branches.create_branch(id=agent_id, source_branch_id=main_id, name="feature-x")
+ v.update_draft(agent_id, branch.data.id, global_prompt="You are ...", first_message="Hi!")
+ revision = v.publish_and_wait(agent_id, branch.data.id) # blocks until scan completes
+ v.branches.make_live(id=agent_id, branch_id=branch.data.id)
+ """
+
+ def __init__(self, client: typing.Any):
+ self._client = client
+ self.branches = client.atoms.agent_versioning_branches
+ self.revisions = client.atoms.agent_versioning_revisions
+
+ def update_draft(self, agent_id: str, branch_id: str, **fields: typing.Any) -> typing.Any:
+ """update_draft with the three 409 flavors mapped to typed exceptions.
+
+ Pass the typed config kwargs (global_prompt=, first_message=, synthesizer=, ...)
+ and optionally expected_revision= for optimistic concurrency.
+ """
+ try:
+ return self.branches.update_draft(id=agent_id, branch_id=branch_id, **fields)
+ except ApiError as e:
+ # The 409s (migration / draft-conflict / base-revision) may surface as a
+ # typed ConflictError or, if the spec omits the 409 response declaration,
+ # as a bare ApiError. ConflictError subclasses ApiError, so this catches both.
+ if getattr(e, "status_code", None) == 409:
+ raise _discriminate_conflict(e) from e
+ raise
+
+ def wait_for_commit(
+ self,
+ agent_id: str,
+ branch_id: str,
+ *,
+ timeout: float = 120.0,
+ poll_interval: float = 2.0,
+ ) -> typing.Any:
+ """Poll the branch's newest revision until it reaches `status == "published"`.
+
+ Returns the published Revision. Raises SecurityCheckFailedError if the scan
+ fails, or TimeoutError if it does not finish within `timeout` seconds.
+ """
+ deadline = time.monotonic() + timeout
+ last_status = None
+ while time.monotonic() < deadline:
+ listed = self.revisions.list(id=agent_id, branch_id=branch_id, limit=1)
+ revs = getattr(listed.data, "revisions", None) or []
+ if revs:
+ rev_id = revs[0].id
+ got = self.revisions.get(id=agent_id, branch_id=branch_id, revision_id=rev_id)
+ rev = getattr(got.data, "revision", None) or got.data
+ last_status = getattr(rev, "status", None)
+ sec = getattr(rev, "security_check", None)
+ sec_status = getattr(sec, "status", None) if sec is not None else None
+ if last_status == "published":
+ return rev
+ if sec_status == "failed":
+ raise SecurityCheckFailedError(f"security check failed for revision {rev_id}")
+ time.sleep(poll_interval)
+ raise TimeoutError(
+ f"revision did not reach 'published' within {timeout}s (last status={last_status})"
+ )
+
+ def publish_and_wait(
+ self,
+ agent_id: str,
+ branch_id: str,
+ *,
+ label: typing.Optional[str] = None,
+ timeout: float = 120.0,
+ poll_interval: float = 2.0,
+ ) -> typing.Any:
+ """Publish the branch's open draft and block until the revision is published.
+
+ Handles both publish outcomes: sync `committed` (revision inline) and async
+ `scanning` (poll until published).
+ """
+ res = self.branches.publish_draft(id=agent_id, branch_id=branch_id, label=label)
+ data = res.data
+ if _state(data) == "committed" and getattr(data, "revision", None) is not None:
+ return data.revision
+ return self.wait_for_commit(
+ agent_id, branch_id, timeout=timeout, poll_interval=poll_interval
+ )
+
+ def edit_and_publish(
+ self,
+ agent_id: str,
+ branch_id: str,
+ *,
+ label: typing.Optional[str] = None,
+ expected_revision: typing.Optional[int] = None,
+ timeout: float = 120.0,
+ poll_interval: float = 2.0,
+ **fields: typing.Any,
+ ) -> typing.Any:
+ """One-call config edit: update the draft with `fields`, then publish and wait.
+
+ The simple "change the agent's config" path over the versioned draft->publish
+ flow. Pass the typed config kwargs (global_prompt=, synthesizer=, ...). Returns
+ the published Revision. Raises DraftConflictError if `expected_revision` is stale.
+ """
+ kwargs = dict(fields)
+ if expected_revision is not None:
+ kwargs["expected_revision"] = expected_revision
+ self.update_draft(agent_id, branch_id, **kwargs)
+ return self.publish_and_wait(
+ agent_id, branch_id, label=label, timeout=timeout, poll_interval=poll_interval
+ )
diff --git a/src/smallestai/atoms/prompt_scoring/__init__.py b/src/smallestai/atoms/prompt_scoring/__init__.py
index ffa8dae8..48c58c52 100644
--- a/src/smallestai/atoms/prompt_scoring/__init__.py
+++ b/src/smallestai/atoms/prompt_scoring/__init__.py
@@ -2,8 +2,63 @@
# isort: skip_file
-# .fernignore-preserved: see webhooks/__init__.py โ generator 5.12.12 strips sub-package
-# type exports, but the generated wire test imports this from the package root.
-from .types.post_prompt_scoring_score_request_version_id import PostPromptScoringScoreRequestVersionId
+import typing
+from importlib import import_module
-__all__ = ["PostPromptScoringScoreRequestVersionId"]
+if typing.TYPE_CHECKING:
+ from .types import (
+ PostPromptScoringScoreRequest,
+ PostPromptScoringScoreRequestDraftId,
+ PostPromptScoringScoreRequestVersionId,
+ PostPromptScoringScoreResponse,
+ PostPromptScoringScoreResponseData,
+ PostPromptScoringScoreResponseDataBand,
+ PostPromptScoringScoreResponseDataDimensionsItem,
+ PostPromptScoringScoreResponseDataDimensionsItemLevel,
+ PostPromptScoringScoreResponseDataOverallGrade,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "PostPromptScoringScoreRequest": ".types",
+ "PostPromptScoringScoreRequestDraftId": ".types",
+ "PostPromptScoringScoreRequestVersionId": ".types",
+ "PostPromptScoringScoreResponse": ".types",
+ "PostPromptScoringScoreResponseData": ".types",
+ "PostPromptScoringScoreResponseDataBand": ".types",
+ "PostPromptScoringScoreResponseDataDimensionsItem": ".types",
+ "PostPromptScoringScoreResponseDataDimensionsItemLevel": ".types",
+ "PostPromptScoringScoreResponseDataOverallGrade": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "PostPromptScoringScoreRequest",
+ "PostPromptScoringScoreRequestDraftId",
+ "PostPromptScoringScoreRequestVersionId",
+ "PostPromptScoringScoreResponse",
+ "PostPromptScoringScoreResponseData",
+ "PostPromptScoringScoreResponseDataBand",
+ "PostPromptScoringScoreResponseDataDimensionsItem",
+ "PostPromptScoringScoreResponseDataDimensionsItemLevel",
+ "PostPromptScoringScoreResponseDataOverallGrade",
+]
diff --git a/src/smallestai/atoms/webhooks/__init__.py b/src/smallestai/atoms/webhooks/__init__.py
index 5cc0f92e..308f6a7b 100644
--- a/src/smallestai/atoms/webhooks/__init__.py
+++ b/src/smallestai/atoms/webhooks/__init__.py
@@ -2,11 +2,66 @@
# isort: skip_file
-# .fernignore-preserved: generator 5.12.12 strips sub-package type exports
-# (exclude_types_from_init_exports), but the generated wire test imports this type
-# from the package root. Re-export it so `from smallestai.atoms.webhooks import
-# CreateWebhooksRequestEventsItem` works and the wire test passes.
-# See SDK_ESCALATIONS.log [1] + memory generator-config-needs-regen-proof.
-from .types.create_webhooks_request_events_item import CreateWebhooksRequestEventsItem
-
-__all__ = ["CreateWebhooksRequestEventsItem"]
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ CreateWebhooksRequestEventsItem,
+ CreateWebhooksRequestEventsItemEventType,
+ CreateWebhooksResponse,
+ DeleteAgentAgentIdWebhookSubscriptionsResponse,
+ DeleteWebhooksResponse,
+ GetAgentAgentIdWebhookSubscriptionsResponse,
+ GetWebhookResponse,
+ GetWebhookResponseData,
+ PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem,
+ PostAgentAgentIdWebhookSubscriptionsResponse,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "CreateWebhooksRequestEventsItem": ".types",
+ "CreateWebhooksRequestEventsItemEventType": ".types",
+ "CreateWebhooksResponse": ".types",
+ "DeleteAgentAgentIdWebhookSubscriptionsResponse": ".types",
+ "DeleteWebhooksResponse": ".types",
+ "GetAgentAgentIdWebhookSubscriptionsResponse": ".types",
+ "GetWebhookResponse": ".types",
+ "GetWebhookResponseData": ".types",
+ "PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem": ".types",
+ "PostAgentAgentIdWebhookSubscriptionsResponse": ".types",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "CreateWebhooksRequestEventsItem",
+ "CreateWebhooksRequestEventsItemEventType",
+ "CreateWebhooksResponse",
+ "DeleteAgentAgentIdWebhookSubscriptionsResponse",
+ "DeleteWebhooksResponse",
+ "GetAgentAgentIdWebhookSubscriptionsResponse",
+ "GetWebhookResponse",
+ "GetWebhookResponseData",
+ "PostAgentAgentIdWebhookSubscriptionsRequestEventTypesItem",
+ "PostAgentAgentIdWebhookSubscriptionsResponse",
+]
diff --git a/src/smallestai/waves/__init__.py b/src/smallestai/waves/__init__.py
index 123180c2..f202e0b2 100644
--- a/src/smallestai/waves/__init__.py
+++ b/src/smallestai/waves/__init__.py
@@ -6,17 +6,15 @@
from importlib import import_module
if typing.TYPE_CHECKING:
- from . import lightning_v2tts, lightning_v31tts, pulse_stt_streaming, speech_to_speech, streaming_tts, tts
+ from . import pulse_stt_streaming, speech_to_speech, streaming_tts, tts
from .types.pronunciation_item import PronunciationItem
# Backward-compat shims for the 4.3.1 surface (source in stream_tts.py,
- # protected by .fernignore). Use the namespaced Fern client for new code:
- # `client.waves.lightning_v31tts.connect(...)`.
+ # protected by .fernignore). For new code use the namespaced Fern client,
+ # e.g. `client.waves.tts.connect(...)` for the unified TTS WebSocket.
from .stream_tts import TTSConfig, WavesStreamingTTS
_dynamic_imports: typing.Dict[str, str] = {
"PronunciationItem": ".types.pronunciation_item",
- "lightning_v2tts": ".lightning_v2tts",
- "lightning_v31tts": ".lightning_v31tts",
"pulse_stt_streaming": ".pulse_stt_streaming",
"speech_to_speech": ".speech_to_speech",
"streaming_tts": ".streaming_tts",
@@ -52,8 +50,6 @@ def __dir__():
"PronunciationItem",
"TTSConfig",
"WavesStreamingTTS",
- "lightning_v2tts",
- "lightning_v31tts",
"pulse_stt_streaming",
"speech_to_speech",
"streaming_tts",
diff --git a/src/smallestai/waves/client.py b/src/smallestai/waves/client.py
index a780d22b..cdb261f6 100644
--- a/src/smallestai/waves/client.py
+++ b/src/smallestai/waves/client.py
@@ -4,27 +4,25 @@
import typing
-from .. import core
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.request_options import RequestOptions
from .raw_client import AsyncRawWavesClient, RawWavesClient
-from .types.add_voice_waves_response import AddVoiceWavesResponse
from .types.delete_pronunciation_dict_response import DeletePronunciationDictResponse
-from .types.delete_voice_waves_response import DeleteVoiceWavesResponse
-from .types.get_cloned_voices_waves_response import GetClonedVoicesWavesResponse
from .types.get_voices_waves_request_model import GetVoicesWavesRequestModel
from .types.get_voices_waves_response import GetVoicesWavesResponse
-from .types.lightning_large_request_language import LightningLargeRequestLanguage
-from .types.lightning_large_request_output_format import LightningLargeRequestOutputFormat
-from .types.lightning_request_language import LightningRequestLanguage
-from .types.lightning_request_output_format import LightningRequestOutputFormat
-from .types.lightning_v31request_language import LightningV31RequestLanguage
-from .types.lightning_v31request_model import LightningV31RequestModel
-from .types.lightning_v31request_output_format import LightningV31RequestOutputFormat
-from .types.lightningv2request_language import Lightningv2RequestLanguage
-from .types.lightningv2request_output_format import Lightningv2RequestOutputFormat
from .types.pronunciation_dict import PronunciationDict
from .types.pronunciation_item import PronunciationItem
+from .types.synthesize_lightning_large_waves_request_output_format import (
+ SynthesizeLightningLargeWavesRequestOutputFormat,
+)
+from .types.synthesize_lightning_v2waves_request_output_format import SynthesizeLightningV2WavesRequestOutputFormat
+from .types.synthesize_lightning_waves_request_output_format import SynthesizeLightningWavesRequestOutputFormat
+from .types.synthesize_sse_lightning_large_waves_request_output_format import (
+ SynthesizeSseLightningLargeWavesRequestOutputFormat,
+)
+from .types.synthesize_sse_lightning_v2waves_request_output_format import (
+ SynthesizeSseLightningV2WavesRequestOutputFormat,
+)
from .types.transcribe_pulse_waves_request_capitalize import TranscribePulseWavesRequestCapitalize
from .types.transcribe_pulse_waves_request_emotion_detection import TranscribePulseWavesRequestEmotionDetection
from .types.transcribe_pulse_waves_request_encoding import TranscribePulseWavesRequestEncoding
@@ -35,12 +33,11 @@
from .types.transcribe_pulse_waves_response import TranscribePulseWavesResponse
from .types.tts_request_language import TtsRequestLanguage
from .types.tts_request_model import TtsRequestModel
+from .types.tts_request_number_pronunciation_language import TtsRequestNumberPronunciationLanguage
from .types.tts_request_output_format import TtsRequestOutputFormat
from .types.update_pronunciation_dict_response import UpdatePronunciationDictResponse
if typing.TYPE_CHECKING:
- from .lightning_v2tts.client import AsyncLightningV2TtsClient, LightningV2TtsClient
- from .lightning_v31tts.client import AsyncLightningV31TtsClient, LightningV31TtsClient
from .pulse_stt_streaming.client import AsyncPulseSttStreamingClient, PulseSttStreamingClient
from .speech_to_speech.client import AsyncSpeechToSpeechClient, SpeechToSpeechClient
from .streaming_tts.client import AsyncStreamingTtsClient, StreamingTtsClient
@@ -55,8 +52,6 @@ def __init__(self, *, client_wrapper: SyncClientWrapper):
self._client_wrapper = client_wrapper
self._tts: typing.Optional[TtsClient] = None
self._streaming_tts: typing.Optional[StreamingTtsClient] = None
- self._lightning_v2tts: typing.Optional[LightningV2TtsClient] = None
- self._lightning_v31tts: typing.Optional[LightningV31TtsClient] = None
self._pulse_stt_streaming: typing.Optional[PulseSttStreamingClient] = None
self._speech_to_speech: typing.Optional[SpeechToSpeechClient] = None
@@ -221,44 +216,22 @@ def delete_pronunciation_dict(
def synthesize_lightning(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningRequestOutputFormat] = OMIT,
+ output_format: typing.Optional[SynthesizeLightningWavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[bytes]:
+ ) -> None:
"""
Get speech for given text using the Waves API
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[LightningRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningRequestOutputFormat]
- The format of the output audio.
+ output_format : typing.Optional[SynthesizeLightningWavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[bytes]
- Synthesized speech retrieved successfully.
+ None
Examples
--------
@@ -267,79 +240,30 @@ def synthesize_lightning(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.waves.synthesize_lightning(
- text="text",
- voice_id="voice_id",
- )
+ client.waves.synthesize_lightning()
"""
- with self._raw_client.synthesize_lightning(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- language=language,
- output_format=output_format,
- request_options=request_options,
- ) as r:
- yield from r.data
+ _response = self._raw_client.synthesize_lightning(output_format=output_format, request_options=request_options)
+ return _response.data
def synthesize_lightning_large(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[bytes]:
+ ) -> None:
"""
Get speech for given text using the Waves API
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ output_format : typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[bytes]
- Synthesized speech retrieved successfully.
+ None
Examples
--------
@@ -348,83 +272,46 @@ def synthesize_lightning_large(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.waves.synthesize_lightning_large(
- text="text",
- voice_id="voice_id",
- )
+ client.waves.synthesize_lightning_large()
"""
- with self._raw_client.synthesize_lightning_large(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- request_options=request_options,
- ) as r:
- yield from r.data
+ _response = self._raw_client.synthesize_lightning_large(
+ output_format=output_format, request_options=request_options
+ )
+ return _response.data
- def synthesize_lightning_v2(
+ def synthesize_sse_lightning_large(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[bytes]:
+ ) -> None:
"""
- Get speech for given text using the Waves API
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
+ The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
+ ## When to Use
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
+ - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+ - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+ - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+ - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
+ ## How It Works
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
+ 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+ 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+ 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+ 4. **End of Stream**: The API sends a completion event when all audio has been delivered
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ Parameters
+ ----------
+ output_format : typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[bytes]
- Synthesized speech retrieved successfully.
+ None
Examples
--------
@@ -433,97 +320,32 @@ def synthesize_lightning_v2(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- client.waves.synthesize_lightning_v2(
- text="Hey i am your a text to speech model",
- voice_id="malcom",
- )
+ client.waves.synthesize_sse_lightning_large()
"""
- with self._raw_client.synthesize_lightning_v2(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- request_options=request_options,
- ) as r:
- yield from r.data
+ _response = self._raw_client.synthesize_sse_lightning_large(
+ output_format=output_format, request_options=request_options
+ )
+ return _response.data
- def synthesize_sse_lightning_large(
+ def synthesize_lightning_v2(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[typing.Any]:
+ ) -> None:
"""
- The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
-
- ## When to Use
-
- - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
- - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
- - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
- - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-
- ## How It Works
-
- 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
- 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
- 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
- 4. **End of Stream**: The API sends a completion event when all audio has been delivered
+ Get speech for given text using the Waves API
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ output_format : typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
- Yields
- ------
- typing.Iterator[typing.Any]
- Synthesized speech retrieved successfully.
+ Returns
+ -------
+ None
Examples
--------
@@ -532,43 +354,19 @@ def synthesize_sse_lightning_large(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- response = client.waves.synthesize_sse_lightning_large(
- text="text",
- voice_id="voice_id",
- )
- for chunk in response:
- yield chunk
+ client.waves.synthesize_lightning_v2()
"""
- with self._raw_client.synthesize_sse_lightning_large(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- request_options=request_options,
- ) as r:
- yield from r.data
+ _response = self._raw_client.synthesize_lightning_v2(
+ output_format=output_format, request_options=request_options
+ )
+ return _response.data
def synthesize_sse_lightning_v2(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[typing.Any]:
+ ) -> None:
"""
The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
@@ -589,43 +387,14 @@ def synthesize_sse_lightning_v2(
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ output_format : typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
- Yields
- ------
- typing.Iterator[typing.Any]
- Synthesized speech retrieved successfully.
+ Returns
+ -------
+ None
Examples
--------
@@ -634,33 +403,18 @@ def synthesize_sse_lightning_v2(
client = SmallestAI(
api_key="YOUR_API_KEY",
)
- response = client.waves.synthesize_sse_lightning_v2(
- text="text",
- voice_id="voice_id",
- )
- for chunk in response:
- yield chunk
+ client.waves.synthesize_sse_lightning_v2()
"""
- with self._raw_client.synthesize_sse_lightning_v2(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- request_options=request_options,
- ) as r:
- yield from r.data
+ _response = self._raw_client.synthesize_sse_lightning_v2(
+ output_format=output_format, request_options=request_options
+ )
+ return _response.data
def get_voices(
self, model: GetVoicesWavesRequestModel, *, request_options: typing.Optional[RequestOptions] = None
) -> GetVoicesWavesResponse:
"""
- List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
+ List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
Parameters
----------
@@ -689,134 +443,29 @@ def get_voices(
_response = self._raw_client.get_voices(model, request_options=request_options)
return _response.data
- def add_voice(
- self, *, display_name: str, file: core.File, request_options: typing.Optional[RequestOptions] = None
- ) -> AddVoiceWavesResponse:
- """
- **Deprecated** โ use `POST /waves/v1/voice-cloning` instead. The new
- endpoint defaults to `lightning-v3.1`, supports optional metadata,
- and returns pre-generated sample clips. This endpoint only clones
- onto `lightning-large` and the resulting voices do not work on
- `lightning-v3.1` (returns an empty WAV). Kept live for backward
- compatibility; new integrations should migrate.
-
- Parameters
- ----------
- display_name : str
- Display name for the voice clone.
-
- file : core.File
- See core.File for more documentation
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AddVoiceWavesResponse
- Voice clone created successfully
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.waves.add_voice(
- display_name="displayName",
- )
- """
- _response = self._raw_client.add_voice(display_name=display_name, file=file, request_options=request_options)
- return _response.data
-
- def get_cloned_voices(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetClonedVoicesWavesResponse:
- """
- **Deprecated** โ use `GET /waves/v1/voice-cloning` instead. The new
- list endpoint returns the same data plus a `modelIds` array per
- clone. Kept live for backward compatibility.
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- GetClonedVoicesWavesResponse
- Voices retrieved successfully.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.waves.get_cloned_voices()
- """
- _response = self._raw_client.get_cloned_voices(request_options=request_options)
- return _response.data
-
- def delete_voice(
- self, *, voice_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> DeleteVoiceWavesResponse:
- """
- Delete a voice clone by `voiceId`. Despite the `/lightning-large/`
- path, this endpoint deletes any voice clone on the organization,
- including clones created via `POST /waves/v1/voice-cloning`.
-
- Parameters
- ----------
- voice_id : str
- The unique identifier of the voice clone to delete.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- DeleteVoiceWavesResponse
- Voice clone deleted successfully
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.waves.delete_voice(
- voice_id="voiceId",
- )
- """
- _response = self._raw_client.delete_voice(voice_id=voice_id, request_options=request_options)
- return _response.data
-
- def synthesize_tts(
- self,
- *,
- text: str,
- voice_id: str,
- model: typing.Optional[TtsRequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[TtsRequestLanguage] = OMIT,
- output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- word_timestamps: typing.Optional[bool] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[bytes]:
+ def synthesize_tts(
+ self,
+ *,
+ text: str,
+ voice_id: str,
+ model: typing.Optional[TtsRequestModel] = OMIT,
+ sample_rate: typing.Optional[int] = OMIT,
+ speed: typing.Optional[float] = OMIT,
+ language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
+ output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
+ pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ word_timestamps: typing.Optional[bool] = OMIT,
+ session_id: typing.Optional[str] = OMIT,
+ request_id: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> typing.Iterator[bytes]:
"""
Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
- **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). On `lightning_v3.1` the full 12-language catalog applies (see voice catalog).
+ **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). Pro supports 31 languages total (10 Indic, 8 Asian & Middle Eastern, 13 European including Dutch and Swedish). Pass the matching ISO 639-1 code (e.g. `ta`, `de`, `ja`) with a Pro voice from that language, or use `auto` to route across all supported languages with any English or Hindi voice. See the [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages) for the full list. On `lightning_v3.1` the model accepts 20 language codes (10 European + 10 Indic) plus `auto`; the trained voice catalog covers 12 of those directly.
## When to use this
@@ -829,7 +478,7 @@ def synthesize_tts(
- 44 kHz natural, expressive synthesis
- Model selectable per request via `model` body parameter
- Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
- - 12 documented languages on `lightning_v3.1`. On `lightning_v3.1_pro`: `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`.
+ - 20 accepted language codes on `lightning_v3.1` (12 with trained voices, 8 additional routed via English/Hindi voices). On `lightning_v3.1_pro`: 31 languages with dedicated voices (10 Indic, 8 Asian & Middle Eastern, 13 European); `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`. Both models accept `language: auto` for cross-language routing.
- Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- Sample rates: 8 kHz โ 44.1 kHz
- Speed: 0.5ร โ 2ร
@@ -920,7 +569,7 @@ def synthesize_tts(
- `lightning_v3.1` (default) โ standard Lightning v3.1.
- `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
for supported voice IDs.
Same concurrency and latency profile across both. Other request
@@ -936,17 +585,56 @@ def synthesize_tts(
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
Each voice has its own `tags.language` set in the voice catalog โ
query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
the voice was trained on; passing other codes is accepted by the
API but produces English-pronounced output.
- **On `lightning_v3.1`**, the full 12-language catalog applies.
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
- **On `lightning_v3.1_pro`**:
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
- Pass `en` โ UK + American accented English.
- Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
@@ -959,7 +647,7 @@ def synthesize_tts(
The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -994,6 +682,7 @@ def synthesize_tts(
sample_rate=sample_rate,
speed=speed,
language=language,
+ number_pronunciation_language=number_pronunciation_language,
output_format=output_format,
pronunciation_dicts=pronunciation_dicts,
word_timestamps=word_timestamps,
@@ -1012,6 +701,7 @@ def synthesize_sse_tts(
sample_rate: typing.Optional[int] = OMIT,
speed: typing.Optional[float] = OMIT,
language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
word_timestamps: typing.Optional[bool] = OMIT,
@@ -1025,7 +715,7 @@ def synthesize_sse_tts(
Pick the model with the `model` body parameter, same as the sync route.
- **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/waves/api-reference/api-reference/text-to-speech/tts).
+ **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/models/api-reference/text-to-speech/stream-speech-web-socket).
## When to use this
@@ -1077,7 +767,7 @@ def synthesize_sse_tts(
- `lightning_v3.1` (default) โ standard Lightning v3.1.
- `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
for supported voice IDs.
Same concurrency and latency profile across both. Other request
@@ -1093,17 +783,56 @@ def synthesize_sse_tts(
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
Each voice has its own `tags.language` set in the voice catalog โ
query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
the voice was trained on; passing other codes is accepted by the
API but produces English-pronounced output.
- **On `lightning_v3.1`**, the full 12-language catalog applies.
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
- **On `lightning_v3.1_pro`**:
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
- Pass `en` โ UK + American accented English.
- Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
@@ -1116,7 +845,7 @@ def synthesize_sse_tts(
The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -1153,6 +882,7 @@ def synthesize_sse_tts(
sample_rate=sample_rate,
speed=speed,
language=language,
+ number_pronunciation_language=number_pronunciation_language,
output_format=output_format,
pronunciation_dicts=pronunciation_dicts,
word_timestamps=word_timestamps,
@@ -1162,56 +892,55 @@ def synthesize_sse_tts(
) as r:
yield from r.data
- def synthesize_lightning_v31(
+ def transcribe_pulse(
self,
*,
- text: str,
- voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
+ request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]],
+ language: typing.Optional[TranscribePulseWavesRequestLanguage] = None,
+ encoding: typing.Optional[TranscribePulseWavesRequestEncoding] = None,
+ webhook_url: typing.Optional[str] = None,
+ webhook_extra: typing.Optional[str] = None,
+ word_timestamps: typing.Optional[bool] = None,
+ diarize: typing.Optional[bool] = None,
+ gender_detection: typing.Optional[TranscribePulseWavesRequestGenderDetection] = None,
+ emotion_detection: typing.Optional[TranscribePulseWavesRequestEmotionDetection] = None,
+ format: typing.Optional[TranscribePulseWavesRequestFormat] = None,
+ punctuate: typing.Optional[TranscribePulseWavesRequestPunctuate] = None,
+ capitalize: typing.Optional[TranscribePulseWavesRequestCapitalize] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[bytes]:
+ ) -> TranscribePulseWavesResponse:
"""
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech) and select Lightning v3.1 via the `model` body field (default).
-
- Synthesize speech from text in a single request. The simplest way to get audio when you have the full text up front โ pass `text` + `voice_id`, get back binary audio.
+ Transcribe an audio file to text using the Pulse model. The fastest way to get a transcript when you already have a recording โ pass either the raw bytes or a URL.
## When to use this
- - **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
- - **Use the SSE streaming endpoint** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
- - **Use the WebSocket endpoint** when text arrives incrementally (LLM token streams, live captioning).
+ Use this endpoint when you have a complete audio file (call recording, voicemail, podcast episode) and want the transcript back in one response. For live transcription as audio arrives, use the realtime WebSocket endpoint (`WSS /waves/v1/pulse/get_text`) instead.
- ## Key features
+ ## Input methods
- - 44 kHz natural, expressive synthesis
- - Cloned voice IDs (`voice_*`) work โ same param as catalog voices
- - 12 documented languages โ see the model card for the full list
- - Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- - Sample rates: 8 kHz โ 44.1 kHz
- - Speed: 0.5ร โ 2ร
- - Per-call pronunciation dictionaries via `pronunciation_dicts`
+ Send the audio in one of two ways:
+
+ 1. **Raw bytes** โ `Content-Type: application/octet-stream` with the audio in the body. All knobs (`language`, `word_timestamps`, etc.) are query parameters.
+ 2. **URL** โ `Content-Type: application/json` with `{"url": "..."}` in the body. Useful when the audio already lives in object storage. Same query parameters apply.
+
+ Pulse autodetects the language across 30+ supported locales. Pass `language` explicitly when you already know it โ detection is fast but skipping it is faster.
## Examples
- **cURL**
+ **cURL** (raw bytes)
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en&word_timestamps=true" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/octet-stream" \\
+ --data-binary "@./call.wav"
+ ```
+
+ **cURL** (URL)
```bash
- curl -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech" \\
+ curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en" \\
-H "Authorization: Bearer $SMALLEST_API_KEY" \\
-H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Hello from Lightning v3.1.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
+ -d '{"url": "https://your-bucket.s3.amazonaws.com/call.wav"}'
```
**Python** (`pip install smallestai>=4.4.0`)
@@ -1219,1226 +948,248 @@ def synthesize_lightning_v31(
from smallestai import SmallestAI
client = SmallestAI(api_key="YOUR_API_KEY")
-
- with open("speech.wav", "wb") as f:
- for chunk in client.waves.synthesize_lightning_v3_1(
- text="Hello from Lightning v3.1.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="wav",
- # Optional: cloned voice support
- # voice_id="voice_FlPKRWI7DX",
- # Optional: pin pronunciations for specific words
- # pronunciation_dicts=[""],
- ):
- f.write(chunk)
+ with open("./call.wav", "rb") as f:
+ result = client.waves.transcribe_pulse(
+ request=f.read(),
+ language="en",
+ word_timestamps=True,
+ diarize=True,
+ )
+ print(result.status) # "success"
+ print(result.transcription) # the transcript string
```
**JavaScript / TypeScript** (using `fetch`)
```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech", {
+ import { readFileSync } from "node:fs";
+
+ const audio = readFileSync("./call.wav");
+ const params = new URLSearchParams({ language: "en", word_timestamps: "true", diarize: "true" });
+
+ const res = await fetch(`https://api.smallest.ai/waves/v1/pulse/get_text?${params}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- Accept: "audio/wav",
+ "Content-Type": "application/octet-stream",
},
- body: JSON.stringify({
- text: "Hello from Lightning v3.1.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "wav",
- }),
+ body: audio,
});
- const audio = Buffer.from(await res.arrayBuffer());
- require("node:fs").writeFileSync("speech.wav", audio);
+ const result = await res.json();
+ console.log(result.transcription);
```
## Common gotchas
- - **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
- - **Cloned voices** (`voice_*` from `add_voice`) work on this endpoint and support `pronunciation_dicts`.
- - **`pronunciation_dicts` validates IDs at request time.** Passing an unknown ID returns `Invalid input data` โ create the dict first via the pronunciation-dicts endpoint and save the returned `id`.
- - **Pronunciation matching is case-sensitive.** Add both `Synopsis` and `synopsis` if your text uses both casings.
- - **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` or `axios` as shown above.
+ - **Max file size is 250 MB.** Larger files return HTTP `400` with `{errors: "Audio data too large", status: "error", message: "Error handling audio data"}`. Compress to mono 16 kHz PCM if you're close to the limit; quality is unaffected.
+ - **Formatting flags (`format`, `punctuate`, `capitalize`)** are accepted at the wire level and exposed in the Python SDK as of `smallestai>=4.4.0`. Today they currently return the same transcript regardless of value โ pass them in your integration so it works as the behavior changes.
+ - **Webhook-driven flow**: pass `webhook_url` to receive the transcript asynchronously. The endpoint returns immediately; the transcript hits your webhook when ready. Useful for long files where you don't want to hold an HTTP connection open.
+ - **Speaker diarization** (`diarize=true`) adds latency. Skip it if you only need the words.
+ - **JavaScript / TypeScript**: the official `smallestai` npm package predates the Pulse model, so call this endpoint with `fetch` or `axios` as shown above.
Parameters
----------
- text : str
- The text to convert to speech.
+ request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
- voice_id : str
- The voice identifier to use for speech generation.
+ language : typing.Optional[TranscribePulseWavesRequestLanguage]
+ Language of the audio file. Set explicitly to the known language for best accuracy.
+
+ **26 single-language codes** on this endpoint: `en`, `hi`, `de`, `es`, `ru`, `it`, `fr`, `nl`, `pt`, `uk`, `pl`, `cs`, `sk`, `lv`, `et`, `ro`, `fi`, `sv`, `bg`, `hu`, `da`, `lt`, `mt`, `zh`, `ja`, `ko`.
+
+ **Regional auto-detect aggregators** for unknown audio:
+ - `multi-eu` (default) โ auto-detects across all 21 European codes above plus `en`.
+ - `multi-asian` โ auto-detects across `zh`, `ko`, `ja`, `en`.
+ - `multi-indic`: auto-detects across `en`, `hi`, `gu`, `mr`, `bn`, `or`. India region only.
+
+ Omitting `language` routes to `multi-eu`. See the [Pulse model card](/models/model-cards/speech-to-text/pulse) for the full table.
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
+ encoding : typing.Optional[TranscribePulseWavesRequestEncoding]
+ Audio encoding of the bytes you upload. Mirrors the `encoding`
+ parameter on the realtime WS endpoint.
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
+ - `linear16`, `linear32` โ raw PCM (16-bit and 32-bit)
+ - `alaw`, `mulaw` โ 8 kHz telephony codecs
+ - `opus`, `ogg_opus` โ Opus compressed audio (raw and Ogg container)
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
+ When omitted, the server detects the format from the file's
+ container header (works for `.wav`, `.mp3`, `.flac`, `.ogg`,
+ `.m4a`, `.webm`).
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
+ webhook_url : typing.Optional[str]
- speed : typing.Optional[float]
- The speed of the generated speech.
+ webhook_extra : typing.Optional[str]
- language : typing.Optional[LightningV31RequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
+ word_timestamps : typing.Optional[bool]
+ Whether to include word and utterance level timestamps in the response
- output_format : typing.Optional[LightningV31RequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
+ diarize : typing.Optional[bool]
+ Whether to perform speaker diarization
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ gender_detection : typing.Optional[TranscribePulseWavesRequestGenderDetection]
+ Whether to predict the gender of the speaker
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
+ emotion_detection : typing.Optional[TranscribePulseWavesRequestEmotionDetection]
+ Whether to predict speaker emotions
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
+ format : typing.Optional[TranscribePulseWavesRequestFormat]
+ Master formatting switch for the transcript. When `false`, forces
+ `punctuate=false`, `capitalize=false`, and also disables Inverse
+ Text Normalization (ITN) so it cannot silently reintroduce
+ punctuation or casing.
+
+ When `true`, the `punctuate` and `capitalize` params take effect
+ independently. Leave `format=true` and use those two to fine-tune.
+
+ punctuate : typing.Optional[TranscribePulseWavesRequestPunctuate]
+ When `false`, strips end-of-sentence punctuation (`.`, `,`, `?`, `!`)
+ from the transcript, `words[].word`, and
+ `utterances[].transcript`. Does not affect casing โ use
+ `capitalize` for that. Overridden to `false` when `format=false`.
+
+ capitalize : typing.Optional[TranscribePulseWavesRequestCapitalize]
+ When `false`, lowercases the entire transcript output (transcript,
+ `words[].word`, and `utterances[].transcript`). Does not affect
+ punctuation โ use `punctuate` for that. Overridden to `false`
+ when `format=false`.
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[bytes]
- Synthesized speech retrieved successfully.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- client.waves.synthesize_lightning_v31(
- text="Hey i am your a text to speech model",
- voice_id="daniel",
- )
- """
- with self._raw_client.synthesize_lightning_v31(
- text=text,
- voice_id=voice_id,
- model=model,
- sample_rate=sample_rate,
- speed=speed,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- session_id=session_id,
- request_id=request_id,
- request_options=request_options,
- ) as r:
- yield from r.data
-
- def synthesize_sse_lightning_v31(
- self,
- *,
- text: str,
- voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[typing.Any]:
- """
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech-sse) and select Lightning v3.1 via the `model` body field (default).
-
- Synthesize speech and stream the audio back over Server-Sent Events. The body and parameters are identical to the sync `/get_speech` endpoint โ the difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
-
- ## When to use this
-
- - **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
- - **Use sync `/get_speech`** when total latency doesn't matter and you'd rather get one buffer.
- - **Use the WebSocket endpoint** when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
-
- ## How it works
-
- 1. POST your text + voice settings โ same payload as `/get_speech`.
- 2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\\n` followed by `data: {"audio": ""}\\n\\n`.
- 3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
- 4. A final `data: {"done": true}\\n\\n` frame marks end of stream.
-
- ## Examples
-
- **cURL**
- ```bash
- curl -N -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/stream" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{
- "text": "Streaming this paragraph chunk by chunk so playback can start sooner.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "pcm"
- }'
- ```
-
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- import base64
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
-
- with open("stream.pcm", "wb") as f:
- for chunk in client.waves.synthesize_sse_lightning_v3_1(
- text="Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="pcm",
- ):
- # Each chunk is `{"audio": ""}`.
- # Decode and pipe to your audio pipeline.
- if chunk.get("audio"):
- f.write(base64.b64decode(chunk["audio"]))
- ```
-
- **JavaScript / TypeScript** (using `fetch` + a reader)
- ```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/stream", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- text: "Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "pcm",
- }),
- });
-
- const reader = res.body!.getReader();
- const decoder = new TextDecoder();
- let buf = "";
- let finished = false;
- while (!finished) {
- const { value, done } = await reader.read();
- if (done) break;
- buf += decoder.decode(value);
- const events = buf.split("\\n\\n");
- buf = events.pop() ?? "";
- for (const ev of events) {
- // SSE frames are "event: audio\\ndata: {json}" or just "data: {json}".
- // We only care about the data line โ pull it out and parse.
- const dataLine = ev.split("\\n").find((l) => l.startsWith("data:"));
- if (!dataLine) continue;
- const payload = JSON.parse(dataLine.slice(5).trim());
- if (payload.done) { finished = true; break; }
- if (payload.audio) {
- const pcm = Buffer.from(payload.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- }
- }
- }
- ```
-
- ## Common gotchas
-
- - **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
- - **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
- - **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
- - **First-chunk latency** depends on model warm-up + network distance. Use `output_format=pcm` and a streaming-friendly client to minimize what you can control.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` as shown above.
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
-
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[LightningV31RequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
-
- output_format : typing.Optional[LightningV31RequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
-
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Yields
- ------
- typing.Iterator[typing.Any]
- Synthesized speech retrieved successfully.
-
- Examples
- --------
- from smallestai import SmallestAI
-
- client = SmallestAI(
- api_key="YOUR_API_KEY",
- )
- response = client.waves.synthesize_sse_lightning_v31(
- text="text",
- voice_id="voice_id",
- )
- for chunk in response:
- yield chunk
- """
- with self._raw_client.synthesize_sse_lightning_v31(
- text=text,
- voice_id=voice_id,
- model=model,
- sample_rate=sample_rate,
- speed=speed,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- session_id=session_id,
- request_id=request_id,
- request_options=request_options,
- ) as r:
- yield from r.data
-
- def transcribe_pulse(
- self,
- *,
- request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]],
- language: typing.Optional[TranscribePulseWavesRequestLanguage] = None,
- encoding: typing.Optional[TranscribePulseWavesRequestEncoding] = None,
- webhook_url: typing.Optional[str] = None,
- webhook_extra: typing.Optional[str] = None,
- word_timestamps: typing.Optional[bool] = None,
- diarize: typing.Optional[bool] = None,
- gender_detection: typing.Optional[TranscribePulseWavesRequestGenderDetection] = None,
- emotion_detection: typing.Optional[TranscribePulseWavesRequestEmotionDetection] = None,
- format: typing.Optional[TranscribePulseWavesRequestFormat] = None,
- punctuate: typing.Optional[TranscribePulseWavesRequestPunctuate] = None,
- capitalize: typing.Optional[TranscribePulseWavesRequestCapitalize] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> TranscribePulseWavesResponse:
- """
- Transcribe an audio file to text using the Pulse model. The fastest way to get a transcript when you already have a recording โ pass either the raw bytes or a URL.
-
- ## When to use this
-
- Use this endpoint when you have a complete audio file (call recording, voicemail, podcast episode) and want the transcript back in one response. For live transcription as audio arrives, use the realtime WebSocket endpoint (`WSS /waves/v1/pulse/get_text`) instead.
-
- ## Input methods
-
- Send the audio in one of two ways:
-
- 1. **Raw bytes** โ `Content-Type: application/octet-stream` with the audio in the body. All knobs (`language`, `word_timestamps`, etc.) are query parameters.
- 2. **URL** โ `Content-Type: application/json` with `{"url": "..."}` in the body. Useful when the audio already lives in object storage. Same query parameters apply.
-
- Pulse autodetects the language across 30+ supported locales. Pass `language` explicitly when you already know it โ detection is fast but skipping it is faster.
-
- ## Examples
-
- **cURL** (raw bytes)
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en&word_timestamps=true" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/octet-stream" \\
- --data-binary "@./call.wav"
- ```
-
- **cURL** (URL)
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{"url": "https://your-bucket.s3.amazonaws.com/call.wav"}'
- ```
-
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
- with open("./call.wav", "rb") as f:
- result = client.waves.transcribe_pulse(
- request=f.read(),
- language="en",
- word_timestamps=True,
- diarize=True,
- )
- print(result.status) # "success"
- print(result.transcription) # the transcript string
- ```
-
- **JavaScript / TypeScript** (using `fetch`)
- ```typescript
- import { readFileSync } from "node:fs";
-
- const audio = readFileSync("./call.wav");
- const params = new URLSearchParams({ language: "en", word_timestamps: "true", diarize: "true" });
-
- const res = await fetch(`https://api.smallest.ai/waves/v1/pulse/get_text?${params}`, {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/octet-stream",
- },
- body: audio,
- });
- const result = await res.json();
- console.log(result.transcription);
- ```
-
- ## Common gotchas
-
- - **Max file size is 250 MB.** Larger files return HTTP `400` with `{errors: "Audio data too large", status: "error", message: "Error handling audio data"}`. Compress to mono 16 kHz PCM if you're close to the limit; quality is unaffected.
- - **Formatting flags (`format`, `punctuate`, `capitalize`)** are accepted at the wire level and exposed in the Python SDK as of `smallestai>=4.4.0`. Today they currently return the same transcript regardless of value โ pass them in your integration so it works as the behavior changes.
- - **Webhook-driven flow**: pass `webhook_url` to receive the transcript asynchronously. The endpoint returns immediately; the transcript hits your webhook when ready. Useful for long files where you don't want to hold an HTTP connection open.
- - **Speaker diarization** (`diarize=true`) adds latency. Skip it if you only need the words.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates the Pulse model, so call this endpoint with `fetch` or `axios` as shown above.
-
- Parameters
- ----------
- request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
-
- language : typing.Optional[TranscribePulseWavesRequestLanguage]
- Language of the audio file. Set explicitly to the known language for best accuracy.
-
- **26 single-language codes** on this endpoint: `en`, `hi`, `de`, `es`, `ru`, `it`, `fr`, `nl`, `pt`, `uk`, `pl`, `cs`, `sk`, `lv`, `et`, `ro`, `fi`, `sv`, `bg`, `hu`, `da`, `lt`, `mt`, `zh`, `ja`, `ko`.
-
- **Regional auto-detect aggregators** for unknown audio:
- - `multi-eu` (default) โ auto-detects across all 21 European codes above plus `en`.
- - `multi-asian` โ auto-detects across `zh`, `ko`, `ja`, `en`.
-
- Omitting `language` routes to `multi-eu`. See the [Pulse model card](/waves/model-cards/speech-to-text/pulse) for the full table.
-
- encoding : typing.Optional[TranscribePulseWavesRequestEncoding]
- Audio encoding of the bytes you upload. Mirrors the `encoding`
- parameter on the realtime WS endpoint.
-
- - `linear16`, `linear32` โ raw PCM (16-bit and 32-bit)
- - `alaw`, `mulaw` โ 8 kHz telephony codecs
- - `opus`, `ogg_opus` โ Opus compressed audio (raw and Ogg container)
-
- When omitted, the server detects the format from the file's
- container header (works for `.wav`, `.mp3`, `.flac`, `.ogg`,
- `.m4a`, `.webm`).
-
- webhook_url : typing.Optional[str]
-
- webhook_extra : typing.Optional[str]
-
- word_timestamps : typing.Optional[bool]
- Whether to include word and utterance level timestamps in the response
-
- diarize : typing.Optional[bool]
- Whether to perform speaker diarization
-
- gender_detection : typing.Optional[TranscribePulseWavesRequestGenderDetection]
- Whether to predict the gender of the speaker
-
- emotion_detection : typing.Optional[TranscribePulseWavesRequestEmotionDetection]
- Whether to predict speaker emotions
-
- format : typing.Optional[TranscribePulseWavesRequestFormat]
- Master formatting switch for the transcript. When `false`, forces
- `punctuate=false`, `capitalize=false`, and also disables Inverse
- Text Normalization (ITN) so it cannot silently reintroduce
- punctuation or casing.
-
- When `true`, the `punctuate` and `capitalize` params take effect
- independently. Leave `format=true` and use those two to fine-tune.
-
- punctuate : typing.Optional[TranscribePulseWavesRequestPunctuate]
- When `false`, strips end-of-sentence punctuation (`.`, `,`, `?`, `!`)
- from the transcript, `words[].word`, and
- `utterances[].transcript`. Does not affect casing โ use
- `capitalize` for that. Overridden to `false` when `format=false`.
-
- capitalize : typing.Optional[TranscribePulseWavesRequestCapitalize]
- When `false`, lowercases the entire transcript output (transcript,
- `words[].word`, and `utterances[].transcript`). Does not affect
- punctuation โ use `punctuate` for that. Overridden to `false`
- when `format=false`.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- TranscribePulseWavesResponse
- Speech transcribed successfully
- """
- _response = self._raw_client.transcribe_pulse(
- request=request,
- language=language,
- encoding=encoding,
- webhook_url=webhook_url,
- webhook_extra=webhook_extra,
- word_timestamps=word_timestamps,
- diarize=diarize,
- gender_detection=gender_detection,
- emotion_detection=emotion_detection,
- format=format,
- punctuate=punctuate,
- capitalize=capitalize,
- request_options=request_options,
- )
- return _response.data
-
- @property
- def tts(self):
- if self._tts is None:
- from .tts.client import TtsClient # noqa: E402
-
- self._tts = TtsClient(client_wrapper=self._client_wrapper)
- return self._tts
-
- @property
- def streaming_tts(self):
- if self._streaming_tts is None:
- from .streaming_tts.client import StreamingTtsClient # noqa: E402
-
- self._streaming_tts = StreamingTtsClient(client_wrapper=self._client_wrapper)
- return self._streaming_tts
-
- @property
- def lightning_v2tts(self):
- if self._lightning_v2tts is None:
- from .lightning_v2tts.client import LightningV2TtsClient # noqa: E402
-
- self._lightning_v2tts = LightningV2TtsClient(client_wrapper=self._client_wrapper)
- return self._lightning_v2tts
-
- @property
- def lightning_v31tts(self):
- if self._lightning_v31tts is None:
- from .lightning_v31tts.client import LightningV31TtsClient # noqa: E402
-
- self._lightning_v31tts = LightningV31TtsClient(client_wrapper=self._client_wrapper)
- return self._lightning_v31tts
-
- @property
- def pulse_stt_streaming(self):
- if self._pulse_stt_streaming is None:
- from .pulse_stt_streaming.client import PulseSttStreamingClient # noqa: E402
-
- self._pulse_stt_streaming = PulseSttStreamingClient(client_wrapper=self._client_wrapper)
- return self._pulse_stt_streaming
-
- @property
- def speech_to_speech(self):
- if self._speech_to_speech is None:
- from .speech_to_speech.client import SpeechToSpeechClient # noqa: E402
-
- self._speech_to_speech = SpeechToSpeechClient(client_wrapper=self._client_wrapper)
- return self._speech_to_speech
-
-
-class AsyncWavesClient:
- def __init__(self, *, client_wrapper: AsyncClientWrapper):
- self._raw_client = AsyncRawWavesClient(client_wrapper=client_wrapper)
- self._client_wrapper = client_wrapper
- self._tts: typing.Optional[AsyncTtsClient] = None
- self._streaming_tts: typing.Optional[AsyncStreamingTtsClient] = None
- self._lightning_v2tts: typing.Optional[AsyncLightningV2TtsClient] = None
- self._lightning_v31tts: typing.Optional[AsyncLightningV31TtsClient] = None
- self._pulse_stt_streaming: typing.Optional[AsyncPulseSttStreamingClient] = None
- self._speech_to_speech: typing.Optional[AsyncSpeechToSpeechClient] = None
-
- @property
- def with_raw_response(self) -> AsyncRawWavesClient:
- """
- Retrieves a raw implementation of this client that returns raw responses.
-
- Returns
- -------
- AsyncRawWavesClient
- """
- return self._raw_client
-
- async def get_pronunciation_dicts(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.List[PronunciationDict]:
- """
- Retrieve all pronunciation dictionaries for the authenticated user
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- typing.List[PronunciationDict]
- List of pronunciation dictionaries
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.waves.get_pronunciation_dicts()
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.get_pronunciation_dicts(request_options=request_options)
- return _response.data
-
- async def create_pronunciation_dict(
- self, *, items: typing.Sequence[PronunciationItem], request_options: typing.Optional[RequestOptions] = None
- ) -> PronunciationDict:
- """
- Create a new pronunciation dictionary for the authenticated user
-
- Parameters
- ----------
- items : typing.Sequence[PronunciationItem]
- List of word-pronunciation pairs to create
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- PronunciationDict
- Successfully created pronunciation dictionary
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
- from smallestai.waves import PronunciationItem
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.waves.create_pronunciation_dict(
- items=[
- PronunciationItem(
- word="mysql",
- pronunciation="my-sequel",
- )
- ],
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.create_pronunciation_dict(items=items, request_options=request_options)
- return _response.data
-
- async def update_pronunciation_dict(
- self,
- *,
- id: str,
- items: typing.Sequence[PronunciationItem],
- request_options: typing.Optional[RequestOptions] = None,
- ) -> UpdatePronunciationDictResponse:
- """
- Update an existing pronunciation dictionary for the authenticated user
-
- Parameters
- ----------
- id : str
- ID of the pronunciation dictionary to update
-
- items : typing.Sequence[PronunciationItem]
- Updated list of word-pronunciation pairs
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- UpdatePronunciationDictResponse
- Successfully updated pronunciation dictionary
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
- from smallestai.waves import PronunciationItem
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.waves.update_pronunciation_dict(
- id="64f1234567890abcdef12345",
- items=[
- PronunciationItem(
- word="mysql",
- pronunciation="my-sequel",
- )
- ],
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.update_pronunciation_dict(
- id=id, items=items, request_options=request_options
- )
- return _response.data
-
- async def delete_pronunciation_dict(
- self, *, id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> DeletePronunciationDictResponse:
- """
- Delete an existing pronunciation dictionary for the authenticated user
-
- Parameters
- ----------
- id : str
- ID of the pronunciation dictionary to delete
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- DeletePronunciationDictResponse
- Successfully deleted pronunciation dictionary
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.waves.delete_pronunciation_dict(
- id="64f1234567890abcdef12345",
- )
-
-
- asyncio.run(main())
- """
- _response = await self._raw_client.delete_pronunciation_dict(id=id, request_options=request_options)
- return _response.data
-
- async def synthesize_lightning(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningRequestOutputFormat] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[bytes]:
- """
- Get speech for given text using the Waves API
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[LightningRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningRequestOutputFormat]
- The format of the output audio.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[bytes]
- Synthesized speech retrieved successfully.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.waves.synthesize_lightning(
- text="text",
- voice_id="voice_id",
- )
-
-
- asyncio.run(main())
- """
- async with self._raw_client.synthesize_lightning(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- language=language,
- output_format=output_format,
- request_options=request_options,
- ) as r:
- async for _chunk in r.data:
- yield _chunk
-
- async def synthesize_lightning_large(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[bytes]:
- """
- Get speech for given text using the Waves API
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[bytes]
- Synthesized speech retrieved successfully.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.waves.synthesize_lightning_large(
- text="text",
- voice_id="voice_id",
- )
-
-
- asyncio.run(main())
+ TranscribePulseWavesResponse
+ Speech transcribed successfully
"""
- async with self._raw_client.synthesize_lightning_large(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
+ _response = self._raw_client.transcribe_pulse(
+ request=request,
language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
+ encoding=encoding,
+ webhook_url=webhook_url,
+ webhook_extra=webhook_extra,
+ word_timestamps=word_timestamps,
+ diarize=diarize,
+ gender_detection=gender_detection,
+ emotion_detection=emotion_detection,
+ format=format,
+ punctuate=punctuate,
+ capitalize=capitalize,
request_options=request_options,
- ) as r:
- async for _chunk in r.data:
- yield _chunk
-
- async def synthesize_lightning_v2(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[bytes]:
- """
- Get speech for given text using the Waves API
+ )
+ return _response.data
- Parameters
- ----------
- text : str
- The text to convert to speech.
+ @property
+ def tts(self):
+ if self._tts is None:
+ from .tts.client import TtsClient # noqa: E402
- voice_id : str
- The voice identifier to use for speech generation.
+ self._tts = TtsClient(client_wrapper=self._client_wrapper)
+ return self._tts
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
+ @property
+ def streaming_tts(self):
+ if self._streaming_tts is None:
+ from .streaming_tts.client import StreamingTtsClient # noqa: E402
- speed : typing.Optional[float]
- The speed of the generated speech.
+ self._streaming_tts = StreamingTtsClient(client_wrapper=self._client_wrapper)
+ return self._streaming_tts
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
+ @property
+ def pulse_stt_streaming(self):
+ if self._pulse_stt_streaming is None:
+ from .pulse_stt_streaming.client import PulseSttStreamingClient # noqa: E402
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
+ self._pulse_stt_streaming = PulseSttStreamingClient(client_wrapper=self._client_wrapper)
+ return self._pulse_stt_streaming
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
+ @property
+ def speech_to_speech(self):
+ if self._speech_to_speech is None:
+ from .speech_to_speech.client import SpeechToSpeechClient # noqa: E402
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
+ self._speech_to_speech = SpeechToSpeechClient(client_wrapper=self._client_wrapper)
+ return self._speech_to_speech
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+class AsyncWavesClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawWavesClient(client_wrapper=client_wrapper)
+ self._client_wrapper = client_wrapper
+ self._tts: typing.Optional[AsyncTtsClient] = None
+ self._streaming_tts: typing.Optional[AsyncStreamingTtsClient] = None
+ self._pulse_stt_streaming: typing.Optional[AsyncPulseSttStreamingClient] = None
+ self._speech_to_speech: typing.Optional[AsyncSpeechToSpeechClient] = None
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ @property
+ def with_raw_response(self) -> AsyncRawWavesClient:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
Returns
-------
- typing.AsyncIterator[bytes]
- Synthesized speech retrieved successfully.
-
- Examples
- --------
- import asyncio
-
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- await client.waves.synthesize_lightning_v2(
- text="Hey i am your a text to speech model",
- voice_id="malcom",
- )
-
-
- asyncio.run(main())
+ AsyncRawWavesClient
"""
- async with self._raw_client.synthesize_lightning_v2(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- request_options=request_options,
- ) as r:
- async for _chunk in r.data:
- yield _chunk
+ return self._raw_client
- async def synthesize_sse_lightning_large(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[typing.Any]:
+ async def get_pronunciation_dicts(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> typing.List[PronunciationDict]:
"""
- The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
-
- ## When to Use
-
- - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
- - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
- - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
- - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-
- ## How It Works
-
- 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
- 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
- 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
- 4. **End of Stream**: The API sends a completion event when all audio has been delivered
+ Retrieve all pronunciation dictionaries for the authenticated user
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
- Yields
- ------
- typing.AsyncIterator[typing.Any]
- Synthesized speech retrieved successfully.
+ Returns
+ -------
+ typing.List[PronunciationDict]
+ List of pronunciation dictionaries
Examples
--------
import asyncio
- from smallestai import AsyncSmallestAI
-
- client = AsyncSmallestAI(
- api_key="YOUR_API_KEY",
- )
-
-
- async def main() -> None:
- response = await client.waves.synthesize_sse_lightning_large(
- text="text",
- voice_id="voice_id",
- )
- async for chunk in response:
- yield chunk
-
-
- asyncio.run(main())
- """
- async with self._raw_client.synthesize_sse_lightning_large(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- request_options=request_options,
- ) as r:
- async for _chunk in r.data:
- yield _chunk
-
- async def synthesize_sse_lightning_v2(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[typing.Any]:
- """
- The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
- For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
-
- ## When to Use
-
- - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
- - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
- - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
- - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-
- ## How It Works
-
- 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
- 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
- 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
- 4. **End of Stream**: The API sends a completion event when all audio has been delivered
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
+ from smallestai import AsyncSmallestAI
- speed : typing.Optional[float]
- The speed of the generated speech.
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
+ async def main() -> None:
+ await client.waves.get_pronunciation_dicts()
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_pronunciation_dicts(request_options=request_options)
+ return _response.data
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
+ async def create_pronunciation_dict(
+ self, *, items: typing.Sequence[PronunciationItem], request_options: typing.Optional[RequestOptions] = None
+ ) -> PronunciationDict:
+ """
+ Create a new pronunciation dictionary for the authenticated user
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ Parameters
+ ----------
+ items : typing.Sequence[PronunciationItem]
+ List of word-pronunciation pairs to create
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
- Yields
- ------
- typing.AsyncIterator[typing.Any]
- Synthesized speech retrieved successfully.
+ Returns
+ -------
+ PronunciationDict
+ Successfully created pronunciation dictionary
Examples
--------
import asyncio
from smallestai import AsyncSmallestAI
+ from smallestai.waves import PronunciationItem
client = AsyncSmallestAI(
api_key="YOUR_API_KEY",
@@ -2446,56 +1197,53 @@ async def synthesize_sse_lightning_v2(
async def main() -> None:
- response = await client.waves.synthesize_sse_lightning_v2(
- text="text",
- voice_id="voice_id",
+ await client.waves.create_pronunciation_dict(
+ items=[
+ PronunciationItem(
+ word="mysql",
+ pronunciation="my-sequel",
+ )
+ ],
)
- async for chunk in response:
- yield chunk
asyncio.run(main())
"""
- async with self._raw_client.synthesize_sse_lightning_v2(
- text=text,
- voice_id=voice_id,
- sample_rate=sample_rate,
- speed=speed,
- consistency=consistency,
- similarity=similarity,
- enhancement=enhancement,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- request_options=request_options,
- ) as r:
- async for _chunk in r.data:
- yield _chunk
+ _response = await self._raw_client.create_pronunciation_dict(items=items, request_options=request_options)
+ return _response.data
- async def get_voices(
- self, model: GetVoicesWavesRequestModel, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetVoicesWavesResponse:
+ async def update_pronunciation_dict(
+ self,
+ *,
+ id: str,
+ items: typing.Sequence[PronunciationItem],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UpdatePronunciationDictResponse:
"""
- List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
+ Update an existing pronunciation dictionary for the authenticated user
Parameters
----------
- model : GetVoicesWavesRequestModel
- The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
+ id : str
+ ID of the pronunciation dictionary to update
+
+ items : typing.Sequence[PronunciationItem]
+ Updated list of word-pronunciation pairs
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- GetVoicesWavesResponse
- Voices retrieved successfully.
+ UpdatePronunciationDictResponse
+ Successfully updated pronunciation dictionary
Examples
--------
import asyncio
from smallestai import AsyncSmallestAI
+ from smallestai.waves import PronunciationItem
client = AsyncSmallestAI(
api_key="YOUR_API_KEY",
@@ -2503,42 +1251,42 @@ async def get_voices(
async def main() -> None:
- await client.waves.get_voices(
- model="lightning-v3.1",
+ await client.waves.update_pronunciation_dict(
+ id="64f1234567890abcdef12345",
+ items=[
+ PronunciationItem(
+ word="mysql",
+ pronunciation="my-sequel",
+ )
+ ],
)
asyncio.run(main())
"""
- _response = await self._raw_client.get_voices(model, request_options=request_options)
+ _response = await self._raw_client.update_pronunciation_dict(
+ id=id, items=items, request_options=request_options
+ )
return _response.data
- async def add_voice(
- self, *, display_name: str, file: core.File, request_options: typing.Optional[RequestOptions] = None
- ) -> AddVoiceWavesResponse:
+ async def delete_pronunciation_dict(
+ self, *, id: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> DeletePronunciationDictResponse:
"""
- **Deprecated** โ use `POST /waves/v1/voice-cloning` instead. The new
- endpoint defaults to `lightning-v3.1`, supports optional metadata,
- and returns pre-generated sample clips. This endpoint only clones
- onto `lightning-large` and the resulting voices do not work on
- `lightning-v3.1` (returns an empty WAV). Kept live for backward
- compatibility; new integrations should migrate.
+ Delete an existing pronunciation dictionary for the authenticated user
Parameters
----------
- display_name : str
- Display name for the voice clone.
-
- file : core.File
- See core.File for more documentation
+ id : str
+ ID of the pronunciation dictionary to delete
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AddVoiceWavesResponse
- Voice clone created successfully
+ DeletePronunciationDictResponse
+ Successfully deleted pronunciation dictionary
Examples
--------
@@ -2552,35 +1300,35 @@ async def add_voice(
async def main() -> None:
- await client.waves.add_voice(
- display_name="displayName",
+ await client.waves.delete_pronunciation_dict(
+ id="64f1234567890abcdef12345",
)
asyncio.run(main())
"""
- _response = await self._raw_client.add_voice(
- display_name=display_name, file=file, request_options=request_options
- )
+ _response = await self._raw_client.delete_pronunciation_dict(id=id, request_options=request_options)
return _response.data
- async def get_cloned_voices(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> GetClonedVoicesWavesResponse:
+ async def synthesize_lightning(
+ self,
+ *,
+ output_format: typing.Optional[SynthesizeLightningWavesRequestOutputFormat] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> None:
"""
- **Deprecated** โ use `GET /waves/v1/voice-cloning` instead. The new
- list endpoint returns the same data plus a `modelIds` array per
- clone. Kept live for backward compatibility.
+ Get speech for given text using the Waves API
Parameters
----------
+ output_format : typing.Optional[SynthesizeLightningWavesRequestOutputFormat]
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- GetClonedVoicesWavesResponse
- Voices retrieved successfully.
+ None
Examples
--------
@@ -2594,34 +1342,35 @@ async def get_cloned_voices(
async def main() -> None:
- await client.waves.get_cloned_voices()
+ await client.waves.synthesize_lightning()
asyncio.run(main())
"""
- _response = await self._raw_client.get_cloned_voices(request_options=request_options)
+ _response = await self._raw_client.synthesize_lightning(
+ output_format=output_format, request_options=request_options
+ )
return _response.data
- async def delete_voice(
- self, *, voice_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> DeleteVoiceWavesResponse:
+ async def synthesize_lightning_large(
+ self,
+ *,
+ output_format: typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> None:
"""
- Delete a voice clone by `voiceId`. Despite the `/lightning-large/`
- path, this endpoint deletes any voice clone on the organization,
- including clones created via `POST /waves/v1/voice-cloning`.
+ Get speech for given text using the Waves API
Parameters
----------
- voice_id : str
- The unique identifier of the voice clone to delete.
+ output_format : typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- DeleteVoiceWavesResponse
- Voice clone deleted successfully
+ None
Examples
--------
@@ -2635,432 +1384,248 @@ async def delete_voice(
async def main() -> None:
- await client.waves.delete_voice(
- voice_id="voiceId",
- )
+ await client.waves.synthesize_lightning_large()
asyncio.run(main())
"""
- _response = await self._raw_client.delete_voice(voice_id=voice_id, request_options=request_options)
+ _response = await self._raw_client.synthesize_lightning_large(
+ output_format=output_format, request_options=request_options
+ )
return _response.data
- async def synthesize_tts(
+ async def synthesize_sse_lightning_large(
self,
*,
- text: str,
- voice_id: str,
- model: typing.Optional[TtsRequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[TtsRequestLanguage] = OMIT,
- output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- word_timestamps: typing.Optional[bool] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
+ output_format: typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[bytes]:
+ ) -> None:
"""
- Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
-
- Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
-
- **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). On `lightning_v3.1` the full 12-language catalog applies (see voice catalog).
-
- ## When to use this
-
- - **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
- - **Use `/waves/v1/tts/live`** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
- - **Use `/waves/v1/tts/live`** (WebSocket) when text arrives incrementally (LLM token streams, live captioning).
-
- ## Key features
-
- - 44 kHz natural, expressive synthesis
- - Model selectable per request via `model` body parameter
- - Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
- - 12 documented languages on `lightning_v3.1`. On `lightning_v3.1_pro`: `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`.
- - Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- - Sample rates: 8 kHz โ 44.1 kHz
- - Speed: 0.5ร โ 2ร
- - Per-call pronunciation dictionaries via `pronunciation_dicts`
-
- ## Examples
-
- **cURL โ Lightning v3.1 (default)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Hello from Waves TTS.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- **cURL โ Lightning v3.1 Pro (omit `language` โ defaults to `en + hi`)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Hello from the Lightning v3.1 Pro pool.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- **cURL โ Lightning v3.1 Pro with explicit `language: en` (UK + American accented English)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Good morning, this is a Pro voice speaking.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "language": "en",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- **cURL โ Lightning v3.1 Pro with explicit `language: hi` (Indian accented English + Hindi)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Namaste, this is an Indian-accented Pro voice.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "language": "hi",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- ## Common gotchas
-
- - **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
- - **Pair voice IDs with the right model.** Voice catalogs differ between `lightning_v3.1` and `lightning_v3.1_pro`. The API does not reject mismatched pairings, but using a Pro-only `voice_id` with `model=lightning_v3.1` (or omitting `model`) can return wrong or hallucinated audio. Pair Pro voices with `model=lightning_v3.1_pro`; standard catalog voices with `model=lightning_v3.1` (the default).
- - **Cloned voices** (`voice_*` from `add_voice`) work with `lightning_v3.1` only; voice cloning is not available on `lightning_v3.1_pro`.
- - **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
-
+ The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
+
+ ## When to Use
+
+ - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+ - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+ - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+ - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
+
+ ## How It Works
+
+ 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+ 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+ 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+ 4. **End of Stream**: The API sends a completion event when all audio has been delivered
+
+ Parameters
+ ----------
+ output_format : typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ None
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.waves.synthesize_sse_lightning_large()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.synthesize_sse_lightning_large(
+ output_format=output_format, request_options=request_options
+ )
+ return _response.data
+
+ async def synthesize_lightning_v2(
+ self,
+ *,
+ output_format: typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> None:
+ """
+ Get speech for given text using the Waves API
+
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation. See the model card for available voices per model.
-
- model : typing.Optional[TtsRequestModel]
- TTS model to route the request to. Controls which model pool serves
- this synthesis.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
- quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
- for supported voice IDs.
-
- Same concurrency and latency profile across both. Other request
- parameters behave identically.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[TtsRequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- Each voice has its own `tags.language` set in the voice catalog โ
- query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
- the voice was trained on; passing other codes is accepted by the
- API but produces English-pronounced output.
-
- **On `lightning_v3.1`**, the full 12-language catalog applies.
-
- **On `lightning_v3.1_pro`**:
- - Pass `en` โ UK + American accented English.
- - Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
-
- output_format : typing.Optional[TtsRequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
-
- word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
-
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
-
+ output_format : typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat]
+
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
+ Request-specific configuration.
+
Returns
-------
- typing.AsyncIterator[bytes]
- Synthesized speech retrieved successfully.
-
+ None
+
Examples
--------
import asyncio
-
+
from smallestai import AsyncSmallestAI
-
+
client = AsyncSmallestAI(
api_key="YOUR_API_KEY",
)
-
-
+
+
async def main() -> None:
- await client.waves.synthesize_tts(
- text="Hello from Waves TTS.",
- voice_id="magnus",
- )
-
-
+ await client.waves.synthesize_lightning_v2()
+
+
asyncio.run(main())
"""
- async with self._raw_client.synthesize_tts(
- text=text,
- voice_id=voice_id,
- model=model,
- sample_rate=sample_rate,
- speed=speed,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- word_timestamps=word_timestamps,
- session_id=session_id,
- request_id=request_id,
- request_options=request_options,
- ) as r:
- async for _chunk in r.data:
- yield _chunk
+ _response = await self._raw_client.synthesize_lightning_v2(
+ output_format=output_format, request_options=request_options
+ )
+ return _response.data
- async def synthesize_sse_tts(
+ async def synthesize_sse_lightning_v2(
self,
*,
- text: str,
- voice_id: str,
- model: typing.Optional[TtsRequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[TtsRequestLanguage] = OMIT,
- output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- word_timestamps: typing.Optional[bool] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
+ output_format: typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[str]:
+ ) -> None:
"""
- Synthesize speech and stream the audio back over Server-Sent Events. Same body as `/waves/v1/tts` โ the only difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
-
- Pick the model with the `model` body parameter, same as the sync route.
-
-
- **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/waves/api-reference/api-reference/text-to-speech/tts).
-
-
- ## When to use this
-
- - **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
- - **Use sync `/waves/v1/tts`** when total latency doesn't matter and you'd rather get one buffer.
- - **Use `/waves/v1/tts/live`** (WebSocket) when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
-
- ## How it works
-
- 1. POST your text + voice settings โ same payload as `/waves/v1/tts`, plus optional `model`.
- 2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\\n` followed by `data: {"audio": ""}\\n\\n`.
- 3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
- 4. A final `data: {"done": true}\\n\\n` frame marks end of stream.
-
- ## Examples
-
- **cURL**
- ```bash
- curl -N -X POST "https://api.smallest.ai/waves/v1/tts/live" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{
- "text": "Streaming this paragraph chunk by chunk so playback can start sooner.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "pcm"
- }'
- ```
-
- ## Common gotchas
-
- - **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
- - **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
- - **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
-
+ The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
+ For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
+
+ ## When to Use
+
+ - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+ - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+ - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+ - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
+
+ ## How It Works
+
+ 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+ 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+ 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+ 4. **End of Stream**: The API sends a completion event when all audio has been delivered
+
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation. See the model card for available voices per model.
-
- model : typing.Optional[TtsRequestModel]
- TTS model to route the request to. Controls which model pool serves
- this synthesis.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
- quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
- for supported voice IDs.
-
- Same concurrency and latency profile across both. Other request
- parameters behave identically.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[TtsRequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- Each voice has its own `tags.language` set in the voice catalog โ
- query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
- the voice was trained on; passing other codes is accepted by the
- API but produces English-pronounced output.
-
- **On `lightning_v3.1`**, the full 12-language catalog applies.
-
- **On `lightning_v3.1_pro`**:
- - Pass `en` โ UK + American accented English.
- - Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
-
- output_format : typing.Optional[TtsRequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
-
- word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
-
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
-
+ output_format : typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ None
+
+ Examples
+ --------
+ import asyncio
+
+ from smallestai import AsyncSmallestAI
+
+ client = AsyncSmallestAI(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.waves.synthesize_sse_lightning_v2()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.synthesize_sse_lightning_v2(
+ output_format=output_format, request_options=request_options
+ )
+ return _response.data
+
+ async def get_voices(
+ self, model: GetVoicesWavesRequestModel, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetVoicesWavesResponse:
+ """
+ List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
+
+ Parameters
+ ----------
+ model : GetVoicesWavesRequestModel
+ The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
-
- Yields
- ------
- typing.AsyncIterator[str]
- Synthesized speech retrieved successfully.
-
+
+ Returns
+ -------
+ GetVoicesWavesResponse
+ Voices retrieved successfully.
+
Examples
--------
import asyncio
-
+
from smallestai import AsyncSmallestAI
-
+
client = AsyncSmallestAI(
api_key="YOUR_API_KEY",
)
-
-
+
+
async def main() -> None:
- response = await client.waves.synthesize_sse_tts(
- text="text",
- voice_id="voice_id",
+ await client.waves.get_voices(
+ model="lightning-v3.1",
)
- async for chunk in response:
- yield chunk
-
-
+
+
asyncio.run(main())
"""
- async with self._raw_client.synthesize_sse_tts(
- text=text,
- voice_id=voice_id,
- model=model,
- sample_rate=sample_rate,
- speed=speed,
- language=language,
- output_format=output_format,
- pronunciation_dicts=pronunciation_dicts,
- word_timestamps=word_timestamps,
- session_id=session_id,
- request_id=request_id,
- request_options=request_options,
- ) as r:
- async for _chunk in r.data:
- yield _chunk
+ _response = await self._raw_client.get_voices(model, request_options=request_options)
+ return _response.data
- async def synthesize_lightning_v31(
+ async def synthesize_tts(
self,
*,
text: str,
voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
+ model: typing.Optional[TtsRequestModel] = OMIT,
sample_rate: typing.Optional[int] = OMIT,
speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
+ language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
+ output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ word_timestamps: typing.Optional[bool] = OMIT,
session_id: typing.Optional[str] = OMIT,
request_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> typing.AsyncIterator[bytes]:
"""
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech) and select Lightning v3.1 via the `model` body field (default).
+ Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
+
+ Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
- Synthesize speech from text in a single request. The simplest way to get audio when you have the full text up front โ pass `text` + `voice_id`, get back binary audio.
+ **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). Pro supports 31 languages total (10 Indic, 8 Asian & Middle Eastern, 13 European including Dutch and Swedish). Pass the matching ISO 639-1 code (e.g. `ta`, `de`, `ja`) with a Pro voice from that language, or use `auto` to route across all supported languages with any English or Hindi voice. See the [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages) for the full list. On `lightning_v3.1` the model accepts 20 language codes (10 European + 10 Indic) plus `auto`; the trained voice catalog covers 12 of those directly.
## When to use this
- **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
- - **Use the SSE streaming endpoint** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
- - **Use the WebSocket endpoint** when text arrives incrementally (LLM token streams, live captioning).
+ - **Use `/waves/v1/tts/live`** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
+ - **Use `/waves/v1/tts/live`** (WebSocket) when text arrives incrementally (LLM token streams, live captioning).
## Key features
- 44 kHz natural, expressive synthesis
- - Cloned voice IDs (`voice_*`) work โ same param as catalog voices
- - 12 documented languages โ see the model card for the full list
+ - Model selectable per request via `model` body parameter
+ - Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
+ - 20 accepted language codes on `lightning_v3.1` (12 with trained voices, 8 additional routed via English/Hindi voices). On `lightning_v3.1_pro`: 31 languages with dedicated voices (10 Indic, 8 Asian & Middle Eastern, 13 European); `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`. Both models accept `language: auto` for cross-language routing.
- Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- Sample rates: 8 kHz โ 44.1 kHz
- Speed: 0.5ร โ 2ร
@@ -3068,68 +1633,73 @@ async def synthesize_lightning_v31(
## Examples
- **cURL**
+ **cURL โ Lightning v3.1 (default)**
```bash
- curl -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech" \\
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
-H "Authorization: Bearer $SMALLEST_API_KEY" \\
-H "Content-Type: application/json" \\
-H "Accept: audio/wav" \\
-d '{
- "text": "Hello from Lightning v3.1.",
+ "text": "Hello from Waves TTS.",
"voice_id": "magnus",
"sample_rate": 24000,
"output_format": "wav"
}' --output speech.wav
```
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
+ **cURL โ Lightning v3.1 Pro (omit `language` โ defaults to `en + hi`)**
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/json" \\
+ -H "Accept: audio/wav" \\
+ -d '{
+ "text": "Hello from the Lightning v3.1 Pro pool.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
+ ```
- with open("speech.wav", "wb") as f:
- for chunk in client.waves.synthesize_lightning_v3_1(
- text="Hello from Lightning v3.1.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="wav",
- # Optional: cloned voice support
- # voice_id="voice_FlPKRWI7DX",
- # Optional: pin pronunciations for specific words
- # pronunciation_dicts=[""],
- ):
- f.write(chunk)
+ **cURL โ Lightning v3.1 Pro with explicit `language: en` (UK + American accented English)**
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/json" \\
+ -H "Accept: audio/wav" \\
+ -d '{
+ "text": "Good morning, this is a Pro voice speaking.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "language": "en",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
```
- **JavaScript / TypeScript** (using `fetch`)
- ```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- Accept: "audio/wav",
- },
- body: JSON.stringify({
- text: "Hello from Lightning v3.1.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "wav",
- }),
- });
- const audio = Buffer.from(await res.arrayBuffer());
- require("node:fs").writeFileSync("speech.wav", audio);
+ **cURL โ Lightning v3.1 Pro with explicit `language: hi` (Indian accented English + Hindi)**
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/json" \\
+ -H "Accept: audio/wav" \\
+ -d '{
+ "text": "Namaste, this is an Indian-accented Pro voice.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "language": "hi",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
```
## Common gotchas
- **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
- - **Cloned voices** (`voice_*` from `add_voice`) work on this endpoint and support `pronunciation_dicts`.
- - **`pronunciation_dicts` validates IDs at request time.** Passing an unknown ID returns `Invalid input data` โ create the dict first via the pronunciation-dicts endpoint and save the returned `id`.
- - **Pronunciation matching is case-sensitive.** Add both `Synopsis` and `synopsis` if your text uses both casings.
+ - **Pair voice IDs with the right model.** Voice catalogs differ between `lightning_v3.1` and `lightning_v3.1_pro`. The API does not reject mismatched pairings, but using a Pro-only `voice_id` with `model=lightning_v3.1` (or omitting `model`) can return wrong or hallucinated audio. Pair Pro voices with `model=lightning_v3.1_pro`; standard catalog voices with `model=lightning_v3.1` (the default).
+ - **Cloned voices** (`voice_*` from `add_voice`) work with `lightning_v3.1` only; voice cloning is not available on `lightning_v3.1_pro`.
- **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` or `axios` as shown above.
Parameters
----------
@@ -3137,19 +1707,20 @@ async def synthesize_lightning_v31(
The text to convert to speech.
voice_id : str
- The voice identifier to use for speech generation.
+ The voice identifier to use for speech generation. See the model card for available voices per model.
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
+ model : typing.Optional[TtsRequestModel]
+ TTS model to route the request to. Controls which model pool serves
+ this synthesis.
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
+ - `lightning_v3.1` (default) โ standard Lightning v3.1.
+ - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
+ quality and naturalness, with a curated voice catalog. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
+ for supported voice IDs.
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
+ Same concurrency and latency profile across both. Other request
+ parameters behave identically.
sample_rate : typing.Optional[int]
The sample rate for the generated audio.
@@ -3157,16 +1728,62 @@ async def synthesize_lightning_v31(
speed : typing.Optional[float]
The speed of the generated speech.
- language : typing.Optional[LightningV31RequestLanguage]
+ language : typing.Optional[TtsRequestLanguage]
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
+ Each voice has its own `tags.language` set in the voice catalog โ
+ query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
+ the voice was trained on; passing other codes is accepted by the
+ API but produces English-pronounced output.
+
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
+
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
+ - Pass `en` โ UK + American accented English.
+ - Pass `hi` โ Indian accented English + Hindi (code-switching).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
- output_format : typing.Optional[LightningV31RequestOutputFormat]
+ output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
but requires a decoder to play; `mp3` and `wav` are directly
playable in browsers and most media players. The server default
@@ -3174,7 +1791,10 @@ async def synthesize_lightning_v31(
`mp3` so the generated audio is directly playable.
pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
+
+ word_timestamps : typing.Optional[bool]
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -3202,23 +1822,25 @@ async def synthesize_lightning_v31(
async def main() -> None:
- await client.waves.synthesize_lightning_v31(
- text="Hey i am your a text to speech model",
- voice_id="daniel",
+ await client.waves.synthesize_tts(
+ text="Hello from Waves TTS.",
+ voice_id="magnus",
)
asyncio.run(main())
"""
- async with self._raw_client.synthesize_lightning_v31(
+ async with self._raw_client.synthesize_tts(
text=text,
voice_id=voice_id,
model=model,
sample_rate=sample_rate,
speed=speed,
language=language,
+ number_pronunciation_language=number_pronunciation_language,
output_format=output_format,
pronunciation_dicts=pronunciation_dicts,
+ word_timestamps=word_timestamps,
session_id=session_id,
request_id=request_id,
request_options=request_options,
@@ -3226,35 +1848,41 @@ async def main() -> None:
async for _chunk in r.data:
yield _chunk
- async def synthesize_sse_lightning_v31(
+ async def synthesize_sse_tts(
self,
*,
text: str,
voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
+ model: typing.Optional[TtsRequestModel] = OMIT,
sample_rate: typing.Optional[int] = OMIT,
speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
+ language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
+ output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ word_timestamps: typing.Optional[bool] = OMIT,
session_id: typing.Optional[str] = OMIT,
request_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[typing.Any]:
+ ) -> typing.AsyncIterator[str]:
"""
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech-sse) and select Lightning v3.1 via the `model` body field (default).
+ Synthesize speech and stream the audio back over Server-Sent Events. Same body as `/waves/v1/tts` โ the only difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
+
+ Pick the model with the `model` body parameter, same as the sync route.
- Synthesize speech and stream the audio back over Server-Sent Events. The body and parameters are identical to the sync `/get_speech` endpoint โ the difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
+
+ **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/models/api-reference/text-to-speech/stream-speech-web-socket).
+
## When to use this
- **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
- - **Use sync `/get_speech`** when total latency doesn't matter and you'd rather get one buffer.
- - **Use the WebSocket endpoint** when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
+ - **Use sync `/waves/v1/tts`** when total latency doesn't matter and you'd rather get one buffer.
+ - **Use `/waves/v1/tts/live`** (WebSocket) when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
## How it works
- 1. POST your text + voice settings โ same payload as `/get_speech`.
+ 1. POST your text + voice settings โ same payload as `/waves/v1/tts`, plus optional `model`.
2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\\n` followed by `data: {"audio": ""}\\n\\n`.
3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
4. A final `data: {"done": true}\\n\\n` frame marks end of stream.
@@ -3263,7 +1891,7 @@ async def synthesize_sse_lightning_v31(
**cURL**
```bash
- curl -N -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/stream" \\
+ curl -N -X POST "https://api.smallest.ai/waves/v1/tts/live" \\
-H "Authorization: Bearer $SMALLEST_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
@@ -3274,74 +1902,11 @@ async def synthesize_sse_lightning_v31(
}'
```
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- import base64
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
-
- with open("stream.pcm", "wb") as f:
- for chunk in client.waves.synthesize_sse_lightning_v3_1(
- text="Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="pcm",
- ):
- # Each chunk is `{"audio": ""}`.
- # Decode and pipe to your audio pipeline.
- if chunk.get("audio"):
- f.write(base64.b64decode(chunk["audio"]))
- ```
-
- **JavaScript / TypeScript** (using `fetch` + a reader)
- ```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/stream", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- text: "Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "pcm",
- }),
- });
-
- const reader = res.body!.getReader();
- const decoder = new TextDecoder();
- let buf = "";
- let finished = false;
- while (!finished) {
- const { value, done } = await reader.read();
- if (done) break;
- buf += decoder.decode(value);
- const events = buf.split("\\n\\n");
- buf = events.pop() ?? "";
- for (const ev of events) {
- // SSE frames are "event: audio\\ndata: {json}" or just "data: {json}".
- // We only care about the data line โ pull it out and parse.
- const dataLine = ev.split("\\n").find((l) => l.startsWith("data:"));
- if (!dataLine) continue;
- const payload = JSON.parse(dataLine.slice(5).trim());
- if (payload.done) { finished = true; break; }
- if (payload.audio) {
- const pcm = Buffer.from(payload.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- }
- }
- }
- ```
-
## Common gotchas
- **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
- **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
- **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
- - **First-chunk latency** depends on model warm-up + network distance. Use `output_format=pcm` and a streaming-friendly client to minimize what you can control.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` as shown above.
Parameters
----------
@@ -3349,19 +1914,20 @@ async def synthesize_sse_lightning_v31(
The text to convert to speech.
voice_id : str
- The voice identifier to use for speech generation.
+ The voice identifier to use for speech generation. See the model card for available voices per model.
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
+ model : typing.Optional[TtsRequestModel]
+ TTS model to route the request to. Controls which model pool serves
+ this synthesis.
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
+ - `lightning_v3.1` (default) โ standard Lightning v3.1.
+ - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
+ quality and naturalness, with a curated voice catalog. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
+ for supported voice IDs.
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
+ Same concurrency and latency profile across both. Other request
+ parameters behave identically.
sample_rate : typing.Optional[int]
The sample rate for the generated audio.
@@ -3369,16 +1935,62 @@ async def synthesize_sse_lightning_v31(
speed : typing.Optional[float]
The speed of the generated speech.
- language : typing.Optional[LightningV31RequestLanguage]
+ language : typing.Optional[TtsRequestLanguage]
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
+ Each voice has its own `tags.language` set in the voice catalog โ
+ query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
+ the voice was trained on; passing other codes is accepted by the
+ API but produces English-pronounced output.
+
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
+
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
+ - Pass `en` โ UK + American accented English.
+ - Pass `hi` โ Indian accented English + Hindi (code-switching).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
- output_format : typing.Optional[LightningV31RequestOutputFormat]
+ output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
but requires a decoder to play; `mp3` and `wav` are directly
playable in browsers and most media players. The server default
@@ -3386,7 +1998,10 @@ async def synthesize_sse_lightning_v31(
`mp3` so the generated audio is directly playable.
pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
+
+ word_timestamps : typing.Optional[bool]
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -3399,7 +2014,7 @@ async def synthesize_sse_lightning_v31(
Yields
------
- typing.AsyncIterator[typing.Any]
+ typing.AsyncIterator[str]
Synthesized speech retrieved successfully.
Examples
@@ -3414,7 +2029,7 @@ async def synthesize_sse_lightning_v31(
async def main() -> None:
- response = await client.waves.synthesize_sse_lightning_v31(
+ response = await client.waves.synthesize_sse_tts(
text="text",
voice_id="voice_id",
)
@@ -3424,15 +2039,17 @@ async def main() -> None:
asyncio.run(main())
"""
- async with self._raw_client.synthesize_sse_lightning_v31(
+ async with self._raw_client.synthesize_sse_tts(
text=text,
voice_id=voice_id,
model=model,
sample_rate=sample_rate,
speed=speed,
language=language,
+ number_pronunciation_language=number_pronunciation_language,
output_format=output_format,
pronunciation_dicts=pronunciation_dicts,
+ word_timestamps=word_timestamps,
session_id=session_id,
request_id=request_id,
request_options=request_options,
@@ -3546,8 +2163,9 @@ async def transcribe_pulse(
**Regional auto-detect aggregators** for unknown audio:
- `multi-eu` (default) โ auto-detects across all 21 European codes above plus `en`.
- `multi-asian` โ auto-detects across `zh`, `ko`, `ja`, `en`.
+ - `multi-indic`: auto-detects across `en`, `hi`, `gu`, `mr`, `bn`, `or`. India region only.
- Omitting `language` routes to `multi-eu`. See the [Pulse model card](/waves/model-cards/speech-to-text/pulse) for the full table.
+ Omitting `language` routes to `multi-eu`. See the [Pulse model card](/models/model-cards/speech-to-text/pulse) for the full table.
encoding : typing.Optional[TranscribePulseWavesRequestEncoding]
Audio encoding of the bytes you upload. Mirrors the `encoding`
@@ -3639,22 +2257,6 @@ def streaming_tts(self):
self._streaming_tts = AsyncStreamingTtsClient(client_wrapper=self._client_wrapper)
return self._streaming_tts
- @property
- def lightning_v2tts(self):
- if self._lightning_v2tts is None:
- from .lightning_v2tts.client import AsyncLightningV2TtsClient # noqa: E402
-
- self._lightning_v2tts = AsyncLightningV2TtsClient(client_wrapper=self._client_wrapper)
- return self._lightning_v2tts
-
- @property
- def lightning_v31tts(self):
- if self._lightning_v31tts is None:
- from .lightning_v31tts.client import AsyncLightningV31TtsClient # noqa: E402
-
- self._lightning_v31tts = AsyncLightningV31TtsClient(client_wrapper=self._client_wrapper)
- return self._lightning_v31tts
-
@property
def pulse_stt_streaming(self):
if self._pulse_stt_streaming is None:
diff --git a/src/smallestai/waves/raw_client.py b/src/smallestai/waves/raw_client.py
index 8535c2e7..3dbc6148 100644
--- a/src/smallestai/waves/raw_client.py
+++ b/src/smallestai/waves/raw_client.py
@@ -5,7 +5,6 @@
from json.decoder import JSONDecodeError
from logging import error, warning
-from .. import core
from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.http_response import AsyncHttpResponse, HttpResponse
@@ -19,23 +18,22 @@
from .errors.bad_request_error import BadRequestError
from .errors.internal_server_error import InternalServerError
from .errors.unauthorized_error import UnauthorizedError
-from .types.add_voice_waves_response import AddVoiceWavesResponse
from .types.delete_pronunciation_dict_response import DeletePronunciationDictResponse
-from .types.delete_voice_waves_response import DeleteVoiceWavesResponse
-from .types.get_cloned_voices_waves_response import GetClonedVoicesWavesResponse
from .types.get_voices_waves_request_model import GetVoicesWavesRequestModel
from .types.get_voices_waves_response import GetVoicesWavesResponse
-from .types.lightning_large_request_language import LightningLargeRequestLanguage
-from .types.lightning_large_request_output_format import LightningLargeRequestOutputFormat
-from .types.lightning_request_language import LightningRequestLanguage
-from .types.lightning_request_output_format import LightningRequestOutputFormat
-from .types.lightning_v31request_language import LightningV31RequestLanguage
-from .types.lightning_v31request_model import LightningV31RequestModel
-from .types.lightning_v31request_output_format import LightningV31RequestOutputFormat
-from .types.lightningv2request_language import Lightningv2RequestLanguage
-from .types.lightningv2request_output_format import Lightningv2RequestOutputFormat
from .types.pronunciation_dict import PronunciationDict
from .types.pronunciation_item import PronunciationItem
+from .types.synthesize_lightning_large_waves_request_output_format import (
+ SynthesizeLightningLargeWavesRequestOutputFormat,
+)
+from .types.synthesize_lightning_v2waves_request_output_format import SynthesizeLightningV2WavesRequestOutputFormat
+from .types.synthesize_lightning_waves_request_output_format import SynthesizeLightningWavesRequestOutputFormat
+from .types.synthesize_sse_lightning_large_waves_request_output_format import (
+ SynthesizeSseLightningLargeWavesRequestOutputFormat,
+)
+from .types.synthesize_sse_lightning_v2waves_request_output_format import (
+ SynthesizeSseLightningV2WavesRequestOutputFormat,
+)
from .types.transcribe_pulse_waves_request_capitalize import TranscribePulseWavesRequestCapitalize
from .types.transcribe_pulse_waves_request_emotion_detection import TranscribePulseWavesRequestEmotionDetection
from .types.transcribe_pulse_waves_request_encoding import TranscribePulseWavesRequestEncoding
@@ -46,6 +44,7 @@
from .types.transcribe_pulse_waves_response import TranscribePulseWavesResponse
from .types.tts_request_language import TtsRequestLanguage
from .types.tts_request_model import TtsRequestModel
+from .types.tts_request_number_pronunciation_language import TtsRequestNumberPronunciationLanguage
from .types.tts_request_output_format import TtsRequestOutputFormat
from .types.update_pronunciation_dict_response import UpdatePronunciationDictResponse
from pydantic import ValidationError
@@ -385,59 +384,31 @@ def delete_pronunciation_dict(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.contextmanager
def synthesize_lightning(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningRequestOutputFormat] = OMIT,
+ output_format: typing.Optional[SynthesizeLightningWavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
+ ) -> HttpResponse[None]:
"""
Get speech for given text using the Waves API
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[LightningRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningRequestOutputFormat]
- The format of the output audio.
+ output_format : typing.Optional[SynthesizeLightningWavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
- Synthesized speech retrieved successfully.
+ HttpResponse[None]
"""
- with self._client_wrapper.httpx_client.stream(
+ _response = self._client_wrapper.httpx_client.request(
"waves/v1/lightning/get_speech",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "language": language,
"output_format": output_format,
},
headers={
@@ -445,526 +416,174 @@ def synthesize_lightning(
},
request_options=request_options,
omit=OMIT,
- ) as _response:
-
- def _stream() -> HttpResponse[typing.Iterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return HttpResponse(
- response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
- )
- _response.read()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield _stream()
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return HttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.contextmanager
def synthesize_lightning_large(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
+ ) -> HttpResponse[None]:
"""
Get speech for given text using the Waves API
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ output_format : typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
- Synthesized speech retrieved successfully.
+ HttpResponse[None]
"""
- with self._client_wrapper.httpx_client.stream(
+ _response = self._client_wrapper.httpx_client.request(
"waves/v1/lightning-large/get_speech",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
"output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
omit=OMIT,
- ) as _response:
-
- def _stream() -> HttpResponse[typing.Iterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return HttpResponse(
- response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
- )
- _response.read()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield _stream()
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return HttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.contextmanager
- def synthesize_lightning_v2(
+ def synthesize_sse_lightning_large(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
+ ) -> HttpResponse[None]:
"""
- Get speech for given text using the Waves API
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
+ The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
+ ## When to Use
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
+ - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+ - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+ - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+ - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
+ ## How It Works
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
+ 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+ 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+ 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+ 4. **End of Stream**: The API sends a completion event when all audio has been delivered
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ Parameters
+ ----------
+ output_format : typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
- Synthesized speech retrieved successfully.
+ HttpResponse[None]
"""
- with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-v2/get_speech",
+ _response = self._client_wrapper.httpx_client.request(
+ "waves/v1/lightning-large/stream",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
"output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
omit=OMIT,
- ) as _response:
-
- def _stream() -> HttpResponse[typing.Iterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return HttpResponse(
- response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
- )
- _response.read()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield _stream()
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return HttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.contextmanager
- def synthesize_sse_lightning_large(
+ def synthesize_lightning_v2(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[typing.Any]]]:
+ ) -> HttpResponse[None]:
"""
- The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
-
- ## When to Use
-
- - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
- - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
- - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
- - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-
- ## How It Works
-
- 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
- 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
- 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
- 4. **End of Stream**: The API sends a completion event when all audio has been delivered
+ Get speech for given text using the Waves API
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ output_format : typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
- Yields
- ------
- typing.Iterator[HttpResponse[typing.Iterator[typing.Any]]]
- Synthesized speech retrieved successfully.
+ Returns
+ -------
+ HttpResponse[None]
"""
- with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-large/stream",
+ _response = self._client_wrapper.httpx_client.request(
+ "waves/v1/lightning-v2/get_speech",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
"output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
omit=OMIT,
- ) as _response:
-
- def _stream() -> HttpResponse[typing.Iterator[typing.Any]]:
- try:
- if 200 <= _response.status_code < 300:
-
- def _iter():
- _event_source = EventSource(_response)
- for _sse in _event_source.iter_sse():
- if _sse.data == None:
- return
- try:
- yield typing.cast(
- typing.Any,
- parse_sse_obj(
- sse=_sse,
- type_=typing.Any, # type: ignore
- ),
- )
- except JSONDecodeError as e:
- warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}")
- except (TypeError, ValueError, KeyError, AttributeError) as e:
- warning(
- f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- except Exception as e:
- error(
- f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- return
-
- return HttpResponse(response=_response, data=_iter())
- _response.read()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield _stream()
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return HttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.contextmanager
def synthesize_sse_lightning_v2(
self,
*,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ output_format: typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[typing.Any]]]:
+ ) -> HttpResponse[None]:
"""
The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
@@ -985,305 +604,31 @@ def synthesize_sse_lightning_v2(
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ output_format : typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
- Yields
- ------
- typing.Iterator[HttpResponse[typing.Iterator[typing.Any]]]
- Synthesized speech retrieved successfully.
+ Returns
+ -------
+ HttpResponse[None]
"""
- with self._client_wrapper.httpx_client.stream(
+ _response = self._client_wrapper.httpx_client.request(
"waves/v1/lightning-v2/stream",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
"output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- def _stream() -> HttpResponse[typing.Iterator[typing.Any]]:
- try:
- if 200 <= _response.status_code < 300:
-
- def _iter():
- _event_source = EventSource(_response)
- for _sse in _event_source.iter_sse():
- if _sse.data == None:
- return
- try:
- yield typing.cast(
- typing.Any,
- parse_sse_obj(
- sse=_sse,
- type_=typing.Any, # type: ignore
- ),
- )
- except JSONDecodeError as e:
- warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}")
- except (TypeError, ValueError, KeyError, AttributeError) as e:
- warning(
- f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- except Exception as e:
- error(
- f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- return
-
- return HttpResponse(response=_response, data=_iter())
- _response.read()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield _stream()
-
- def get_voices(
- self, model: GetVoicesWavesRequestModel, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetVoicesWavesResponse]:
- """
- List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
-
- Parameters
- ----------
- model : GetVoicesWavesRequestModel
- The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[GetVoicesWavesResponse]
- Voices retrieved successfully.
- """
- _response = self._client_wrapper.httpx_client.request(
- f"waves/v1/{encode_path_param(model)}/get_voices",
- base_url=self._client_wrapper.get_environment().waves,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- GetVoicesWavesResponse,
- construct_type(
- type_=GetVoicesWavesResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- def add_voice(
- self, *, display_name: str, file: core.File, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[AddVoiceWavesResponse]:
- """
- **Deprecated** โ use `POST /waves/v1/voice-cloning` instead. The new
- endpoint defaults to `lightning-v3.1`, supports optional metadata,
- and returns pre-generated sample clips. This endpoint only clones
- onto `lightning-large` and the resulting voices do not work on
- `lightning-v3.1` (returns an empty WAV). Kept live for backward
- compatibility; new integrations should migrate.
-
- Parameters
- ----------
- display_name : str
- Display name for the voice clone.
-
- file : core.File
- See core.File for more documentation
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[AddVoiceWavesResponse]
- Voice clone created successfully
- """
- _response = self._client_wrapper.httpx_client.request(
- "waves/v1/lightning-large/add_voice",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- data={
- "displayName": display_name,
},
- files={
- "file": file,
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
omit=OMIT,
- force_multipart=True,
)
try:
if 200 <= _response.status_code < 300:
- _data = typing.cast(
- AddVoiceWavesResponse,
- construct_type(
- type_=AddVoiceWavesResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
+ return HttpResponse(response=_response, data=None)
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
@@ -1293,26 +638,27 @@ def add_voice(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def get_cloned_voices(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[GetClonedVoicesWavesResponse]:
+ def get_voices(
+ self, model: GetVoicesWavesRequestModel, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetVoicesWavesResponse]:
"""
- **Deprecated** โ use `GET /waves/v1/voice-cloning` instead. The new
- list endpoint returns the same data plus a `modelIds` array per
- clone. Kept live for backward compatibility.
+ List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
Parameters
----------
+ model : GetVoicesWavesRequestModel
+ The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[GetClonedVoicesWavesResponse]
+ HttpResponse[GetVoicesWavesResponse]
Voices retrieved successfully.
"""
_response = self._client_wrapper.httpx_client.request(
- "waves/v1/lightning-large/get_cloned_voices",
+ f"waves/v1/{encode_path_param(model)}/get_voices",
base_url=self._client_wrapper.get_environment().waves,
method="GET",
request_options=request_options,
@@ -1320,9 +666,9 @@ def get_cloned_voices(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetClonedVoicesWavesResponse,
+ GetVoicesWavesResponse,
construct_type(
- type_=GetClonedVoicesWavesResponse, # type: ignore
+ type_=GetVoicesWavesResponse, # type: ignore
object_=_response.json(),
),
)
@@ -1369,115 +715,30 @@ def get_cloned_voices(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def delete_voice(
- self, *, voice_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[DeleteVoiceWavesResponse]:
- """
- Delete a voice clone by `voiceId`. Despite the `/lightning-large/`
- path, this endpoint deletes any voice clone on the organization,
- including clones created via `POST /waves/v1/voice-cloning`.
-
- Parameters
- ----------
- voice_id : str
- The unique identifier of the voice clone to delete.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[DeleteVoiceWavesResponse]
- Voice clone deleted successfully
- """
- _response = self._client_wrapper.httpx_client.request(
- "waves/v1/lightning-large",
- base_url=self._client_wrapper.get_environment().waves,
- method="DELETE",
- json={
- "voiceId": voice_id,
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- DeleteVoiceWavesResponse,
- construct_type(
- type_=DeleteVoiceWavesResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return HttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- @contextlib.contextmanager
- def synthesize_tts(
- self,
- *,
- text: str,
- voice_id: str,
- model: typing.Optional[TtsRequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[TtsRequestLanguage] = OMIT,
- output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- word_timestamps: typing.Optional[bool] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
+ @contextlib.contextmanager
+ def synthesize_tts(
+ self,
+ *,
+ text: str,
+ voice_id: str,
+ model: typing.Optional[TtsRequestModel] = OMIT,
+ sample_rate: typing.Optional[int] = OMIT,
+ speed: typing.Optional[float] = OMIT,
+ language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
+ output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
+ pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ word_timestamps: typing.Optional[bool] = OMIT,
+ session_id: typing.Optional[str] = OMIT,
+ request_id: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
"""
Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
- **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). On `lightning_v3.1` the full 12-language catalog applies (see voice catalog).
+ **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). Pro supports 31 languages total (10 Indic, 8 Asian & Middle Eastern, 13 European including Dutch and Swedish). Pass the matching ISO 639-1 code (e.g. `ta`, `de`, `ja`) with a Pro voice from that language, or use `auto` to route across all supported languages with any English or Hindi voice. See the [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages) for the full list. On `lightning_v3.1` the model accepts 20 language codes (10 European + 10 Indic) plus `auto`; the trained voice catalog covers 12 of those directly.
## When to use this
@@ -1490,7 +751,7 @@ def synthesize_tts(
- 44 kHz natural, expressive synthesis
- Model selectable per request via `model` body parameter
- Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
- - 12 documented languages on `lightning_v3.1`. On `lightning_v3.1_pro`: `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`.
+ - 20 accepted language codes on `lightning_v3.1` (12 with trained voices, 8 additional routed via English/Hindi voices). On `lightning_v3.1_pro`: 31 languages with dedicated voices (10 Indic, 8 Asian & Middle Eastern, 13 European); `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`. Both models accept `language: auto` for cross-language routing.
- Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- Sample rates: 8 kHz โ 44.1 kHz
- Speed: 0.5ร โ 2ร
@@ -1581,7 +842,7 @@ def synthesize_tts(
- `lightning_v3.1` (default) โ standard Lightning v3.1.
- `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
for supported voice IDs.
Same concurrency and latency profile across both. Other request
@@ -1597,17 +858,56 @@ def synthesize_tts(
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
Each voice has its own `tags.language` set in the voice catalog โ
query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
the voice was trained on; passing other codes is accepted by the
API but produces English-pronounced output.
- **On `lightning_v3.1`**, the full 12-language catalog applies.
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
- **On `lightning_v3.1_pro`**:
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
- Pass `en` โ UK + American accented English.
- Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
@@ -1620,7 +920,7 @@ def synthesize_tts(
The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -1647,6 +947,7 @@ def synthesize_tts(
"sample_rate": sample_rate,
"speed": speed,
"language": language,
+ "number_pronunciation_language": number_pronunciation_language,
"output_format": output_format,
"pronunciation_dicts": pronunciation_dicts,
"word_timestamps": word_timestamps,
@@ -1728,6 +1029,7 @@ def synthesize_sse_tts(
sample_rate: typing.Optional[int] = OMIT,
speed: typing.Optional[float] = OMIT,
language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
word_timestamps: typing.Optional[bool] = OMIT,
@@ -1741,7 +1043,7 @@ def synthesize_sse_tts(
Pick the model with the `model` body parameter, same as the sync route.
- **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/waves/api-reference/api-reference/text-to-speech/tts).
+ **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/models/api-reference/text-to-speech/stream-speech-web-socket).
## When to use this
@@ -1793,7 +1095,7 @@ def synthesize_sse_tts(
- `lightning_v3.1` (default) โ standard Lightning v3.1.
- `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
for supported voice IDs.
Same concurrency and latency profile across both. Other request
@@ -1809,17 +1111,56 @@ def synthesize_sse_tts(
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
Each voice has its own `tags.language` set in the voice catalog โ
query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
the voice was trained on; passing other codes is accepted by the
API but produces English-pronounced output.
- **On `lightning_v3.1`**, the full 12-language catalog applies.
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
- **On `lightning_v3.1_pro`**:
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
- Pass `en` โ UK + American accented English.
- Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
@@ -1832,7 +1173,7 @@ def synthesize_sse_tts(
The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -1859,6 +1200,7 @@ def synthesize_sse_tts(
"sample_rate": sample_rate,
"speed": speed,
"language": language,
+ "number_pronunciation_language": number_pronunciation_language,
"output_format": output_format,
"pronunciation_dicts": pronunciation_dicts,
"word_timestamps": word_timestamps,
@@ -1949,57 +1291,55 @@ def _iter():
yield _stream()
- @contextlib.contextmanager
- def synthesize_lightning_v31(
+ def transcribe_pulse(
self,
*,
- text: str,
- voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
+ request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]],
+ language: typing.Optional[TranscribePulseWavesRequestLanguage] = None,
+ encoding: typing.Optional[TranscribePulseWavesRequestEncoding] = None,
+ webhook_url: typing.Optional[str] = None,
+ webhook_extra: typing.Optional[str] = None,
+ word_timestamps: typing.Optional[bool] = None,
+ diarize: typing.Optional[bool] = None,
+ gender_detection: typing.Optional[TranscribePulseWavesRequestGenderDetection] = None,
+ emotion_detection: typing.Optional[TranscribePulseWavesRequestEmotionDetection] = None,
+ format: typing.Optional[TranscribePulseWavesRequestFormat] = None,
+ punctuate: typing.Optional[TranscribePulseWavesRequestPunctuate] = None,
+ capitalize: typing.Optional[TranscribePulseWavesRequestCapitalize] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
+ ) -> HttpResponse[TranscribePulseWavesResponse]:
"""
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech) and select Lightning v3.1 via the `model` body field (default).
-
- Synthesize speech from text in a single request. The simplest way to get audio when you have the full text up front โ pass `text` + `voice_id`, get back binary audio.
+ Transcribe an audio file to text using the Pulse model. The fastest way to get a transcript when you already have a recording โ pass either the raw bytes or a URL.
## When to use this
- - **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
- - **Use the SSE streaming endpoint** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
- - **Use the WebSocket endpoint** when text arrives incrementally (LLM token streams, live captioning).
+ Use this endpoint when you have a complete audio file (call recording, voicemail, podcast episode) and want the transcript back in one response. For live transcription as audio arrives, use the realtime WebSocket endpoint (`WSS /waves/v1/pulse/get_text`) instead.
- ## Key features
+ ## Input methods
- - 44 kHz natural, expressive synthesis
- - Cloned voice IDs (`voice_*`) work โ same param as catalog voices
- - 12 documented languages โ see the model card for the full list
- - Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- - Sample rates: 8 kHz โ 44.1 kHz
- - Speed: 0.5ร โ 2ร
- - Per-call pronunciation dictionaries via `pronunciation_dicts`
+ Send the audio in one of two ways:
+
+ 1. **Raw bytes** โ `Content-Type: application/octet-stream` with the audio in the body. All knobs (`language`, `word_timestamps`, etc.) are query parameters.
+ 2. **URL** โ `Content-Type: application/json` with `{"url": "..."}` in the body. Useful when the audio already lives in object storage. Same query parameters apply.
+
+ Pulse autodetects the language across 30+ supported locales. Pass `language` explicitly when you already know it โ detection is fast but skipping it is faster.
## Examples
- **cURL**
+ **cURL** (raw bytes)
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en&word_timestamps=true" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/octet-stream" \\
+ --data-binary "@./call.wav"
+ ```
+
+ **cURL** (URL)
```bash
- curl -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech" \\
+ curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en" \\
-H "Authorization: Bearer $SMALLEST_API_KEY" \\
-H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Hello from Lightning v3.1.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
+ -d '{"url": "https://your-bucket.s3.amazonaws.com/call.wav"}'
```
**Python** (`pip install smallestai>=4.4.0`)
@@ -2007,653 +1347,137 @@ def synthesize_lightning_v31(
from smallestai import SmallestAI
client = SmallestAI(api_key="YOUR_API_KEY")
-
- with open("speech.wav", "wb") as f:
- for chunk in client.waves.synthesize_lightning_v3_1(
- text="Hello from Lightning v3.1.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="wav",
- # Optional: cloned voice support
- # voice_id="voice_FlPKRWI7DX",
- # Optional: pin pronunciations for specific words
- # pronunciation_dicts=[""],
- ):
- f.write(chunk)
+ with open("./call.wav", "rb") as f:
+ result = client.waves.transcribe_pulse(
+ request=f.read(),
+ language="en",
+ word_timestamps=True,
+ diarize=True,
+ )
+ print(result.status) # "success"
+ print(result.transcription) # the transcript string
```
**JavaScript / TypeScript** (using `fetch`)
```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech", {
+ import { readFileSync } from "node:fs";
+
+ const audio = readFileSync("./call.wav");
+ const params = new URLSearchParams({ language: "en", word_timestamps: "true", diarize: "true" });
+
+ const res = await fetch(`https://api.smallest.ai/waves/v1/pulse/get_text?${params}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- Accept: "audio/wav",
+ "Content-Type": "application/octet-stream",
},
- body: JSON.stringify({
- text: "Hello from Lightning v3.1.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "wav",
- }),
+ body: audio,
});
- const audio = Buffer.from(await res.arrayBuffer());
- require("node:fs").writeFileSync("speech.wav", audio);
+ const result = await res.json();
+ console.log(result.transcription);
```
## Common gotchas
- - **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
- - **Cloned voices** (`voice_*` from `add_voice`) work on this endpoint and support `pronunciation_dicts`.
- - **`pronunciation_dicts` validates IDs at request time.** Passing an unknown ID returns `Invalid input data` โ create the dict first via the pronunciation-dicts endpoint and save the returned `id`.
- - **Pronunciation matching is case-sensitive.** Add both `Synopsis` and `synopsis` if your text uses both casings.
- - **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` or `axios` as shown above.
+ - **Max file size is 250 MB.** Larger files return HTTP `400` with `{errors: "Audio data too large", status: "error", message: "Error handling audio data"}`. Compress to mono 16 kHz PCM if you're close to the limit; quality is unaffected.
+ - **Formatting flags (`format`, `punctuate`, `capitalize`)** are accepted at the wire level and exposed in the Python SDK as of `smallestai>=4.4.0`. Today they currently return the same transcript regardless of value โ pass them in your integration so it works as the behavior changes.
+ - **Webhook-driven flow**: pass `webhook_url` to receive the transcript asynchronously. The endpoint returns immediately; the transcript hits your webhook when ready. Useful for long files where you don't want to hold an HTTP connection open.
+ - **Speaker diarization** (`diarize=true`) adds latency. Skip it if you only need the words.
+ - **JavaScript / TypeScript**: the official `smallestai` npm package predates the Pulse model, so call this endpoint with `fetch` or `axios` as shown above.
Parameters
----------
- text : str
- The text to convert to speech.
+ request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
- voice_id : str
- The voice identifier to use for speech generation.
+ language : typing.Optional[TranscribePulseWavesRequestLanguage]
+ Language of the audio file. Set explicitly to the known language for best accuracy.
+
+ **26 single-language codes** on this endpoint: `en`, `hi`, `de`, `es`, `ru`, `it`, `fr`, `nl`, `pt`, `uk`, `pl`, `cs`, `sk`, `lv`, `et`, `ro`, `fi`, `sv`, `bg`, `hu`, `da`, `lt`, `mt`, `zh`, `ja`, `ko`.
+
+ **Regional auto-detect aggregators** for unknown audio:
+ - `multi-eu` (default) โ auto-detects across all 21 European codes above plus `en`.
+ - `multi-asian` โ auto-detects across `zh`, `ko`, `ja`, `en`.
+ - `multi-indic`: auto-detects across `en`, `hi`, `gu`, `mr`, `bn`, `or`. India region only.
+
+ Omitting `language` routes to `multi-eu`. See the [Pulse model card](/models/model-cards/speech-to-text/pulse) for the full table.
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
+ encoding : typing.Optional[TranscribePulseWavesRequestEncoding]
+ Audio encoding of the bytes you upload. Mirrors the `encoding`
+ parameter on the realtime WS endpoint.
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
+ - `linear16`, `linear32` โ raw PCM (16-bit and 32-bit)
+ - `alaw`, `mulaw` โ 8 kHz telephony codecs
+ - `opus`, `ogg_opus` โ Opus compressed audio (raw and Ogg container)
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
+ When omitted, the server detects the format from the file's
+ container header (works for `.wav`, `.mp3`, `.flac`, `.ogg`,
+ `.m4a`, `.webm`).
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
+ webhook_url : typing.Optional[str]
- speed : typing.Optional[float]
- The speed of the generated speech.
+ webhook_extra : typing.Optional[str]
- language : typing.Optional[LightningV31RequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
+ word_timestamps : typing.Optional[bool]
+ Whether to include word and utterance level timestamps in the response
- output_format : typing.Optional[LightningV31RequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
+ diarize : typing.Optional[bool]
+ Whether to perform speaker diarization
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ gender_detection : typing.Optional[TranscribePulseWavesRequestGenderDetection]
+ Whether to predict the gender of the speaker
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
+ emotion_detection : typing.Optional[TranscribePulseWavesRequestEmotionDetection]
+ Whether to predict speaker emotions
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
+ format : typing.Optional[TranscribePulseWavesRequestFormat]
+ Master formatting switch for the transcript. When `false`, forces
+ `punctuate=false`, `capitalize=false`, and also disables Inverse
+ Text Normalization (ITN) so it cannot silently reintroduce
+ punctuation or casing.
+
+ When `true`, the `punctuate` and `capitalize` params take effect
+ independently. Leave `format=true` and use those two to fine-tune.
+
+ punctuate : typing.Optional[TranscribePulseWavesRequestPunctuate]
+ When `false`, strips end-of-sentence punctuation (`.`, `,`, `?`, `!`)
+ from the transcript, `words[].word`, and
+ `utterances[].transcript`. Does not affect casing โ use
+ `capitalize` for that. Overridden to `false` when `format=false`.
+
+ capitalize : typing.Optional[TranscribePulseWavesRequestCapitalize]
+ When `false`, lowercases the entire transcript output (transcript,
+ `words[].word`, and `utterances[].transcript`). Does not affect
+ punctuation โ use `punctuate` for that. Overridden to `false`
+ when `format=false`.
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+ Request-specific configuration.
Returns
-------
- typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
- Synthesized speech retrieved successfully.
+ HttpResponse[TranscribePulseWavesResponse]
+ Speech transcribed successfully
"""
- with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-v3.1/get_speech",
+ _response = self._client_wrapper.httpx_client.request(
+ "waves/v1/pulse/get_text",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
- json={
- "text": text,
- "voice_id": voice_id,
- "model": model,
- "sample_rate": sample_rate,
- "speed": speed,
+ params={
"language": language,
- "output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- "session_id": session_id,
- "request_id": request_id,
+ "encoding": encoding,
+ "webhook_url": webhook_url,
+ "webhook_extra": webhook_extra,
+ "word_timestamps": word_timestamps,
+ "diarize": diarize,
+ "gender_detection": gender_detection,
+ "emotion_detection": emotion_detection,
+ "format": format,
+ "punctuate": punctuate,
+ "capitalize": capitalize,
},
+ content=request,
headers={
- "content-type": "application/json",
- "Accept": "audio/wav",
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- def _stream() -> HttpResponse[typing.Iterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return HttpResponse(
- response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
- )
- _response.read()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield _stream()
-
- @contextlib.contextmanager
- def synthesize_sse_lightning_v31(
- self,
- *,
- text: str,
- voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.Iterator[HttpResponse[typing.Iterator[typing.Any]]]:
- """
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech-sse) and select Lightning v3.1 via the `model` body field (default).
-
- Synthesize speech and stream the audio back over Server-Sent Events. The body and parameters are identical to the sync `/get_speech` endpoint โ the difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
-
- ## When to use this
-
- - **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
- - **Use sync `/get_speech`** when total latency doesn't matter and you'd rather get one buffer.
- - **Use the WebSocket endpoint** when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
-
- ## How it works
-
- 1. POST your text + voice settings โ same payload as `/get_speech`.
- 2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\\n` followed by `data: {"audio": ""}\\n\\n`.
- 3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
- 4. A final `data: {"done": true}\\n\\n` frame marks end of stream.
-
- ## Examples
-
- **cURL**
- ```bash
- curl -N -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/stream" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{
- "text": "Streaming this paragraph chunk by chunk so playback can start sooner.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "pcm"
- }'
- ```
-
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- import base64
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
-
- with open("stream.pcm", "wb") as f:
- for chunk in client.waves.synthesize_sse_lightning_v3_1(
- text="Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="pcm",
- ):
- # Each chunk is `{"audio": ""}`.
- # Decode and pipe to your audio pipeline.
- if chunk.get("audio"):
- f.write(base64.b64decode(chunk["audio"]))
- ```
-
- **JavaScript / TypeScript** (using `fetch` + a reader)
- ```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/stream", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- text: "Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "pcm",
- }),
- });
-
- const reader = res.body!.getReader();
- const decoder = new TextDecoder();
- let buf = "";
- let finished = false;
- while (!finished) {
- const { value, done } = await reader.read();
- if (done) break;
- buf += decoder.decode(value);
- const events = buf.split("\\n\\n");
- buf = events.pop() ?? "";
- for (const ev of events) {
- // SSE frames are "event: audio\\ndata: {json}" or just "data: {json}".
- // We only care about the data line โ pull it out and parse.
- const dataLine = ev.split("\\n").find((l) => l.startsWith("data:"));
- if (!dataLine) continue;
- const payload = JSON.parse(dataLine.slice(5).trim());
- if (payload.done) { finished = true; break; }
- if (payload.audio) {
- const pcm = Buffer.from(payload.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- }
- }
- }
- ```
-
- ## Common gotchas
-
- - **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
- - **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
- - **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
- - **First-chunk latency** depends on model warm-up + network distance. Use `output_format=pcm` and a streaming-friendly client to minimize what you can control.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` as shown above.
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
-
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[LightningV31RequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
-
- output_format : typing.Optional[LightningV31RequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
-
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Yields
- ------
- typing.Iterator[HttpResponse[typing.Iterator[typing.Any]]]
- Synthesized speech retrieved successfully.
- """
- with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-v3.1/stream",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- json={
- "text": text,
- "voice_id": voice_id,
- "model": model,
- "sample_rate": sample_rate,
- "speed": speed,
- "language": language,
- "output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- "session_id": session_id,
- "request_id": request_id,
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- def _stream() -> HttpResponse[typing.Iterator[typing.Any]]:
- try:
- if 200 <= _response.status_code < 300:
-
- def _iter():
- _event_source = EventSource(_response)
- for _sse in _event_source.iter_sse():
- if _sse.data == None:
- return
- try:
- yield typing.cast(
- typing.Any,
- parse_sse_obj(
- sse=_sse,
- type_=typing.Any, # type: ignore
- ),
- )
- except JSONDecodeError as e:
- warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}")
- except (TypeError, ValueError, KeyError, AttributeError) as e:
- warning(
- f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- except Exception as e:
- error(
- f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- return
-
- return HttpResponse(response=_response, data=_iter())
- _response.read()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield _stream()
-
- def transcribe_pulse(
- self,
- *,
- request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]],
- language: typing.Optional[TranscribePulseWavesRequestLanguage] = None,
- encoding: typing.Optional[TranscribePulseWavesRequestEncoding] = None,
- webhook_url: typing.Optional[str] = None,
- webhook_extra: typing.Optional[str] = None,
- word_timestamps: typing.Optional[bool] = None,
- diarize: typing.Optional[bool] = None,
- gender_detection: typing.Optional[TranscribePulseWavesRequestGenderDetection] = None,
- emotion_detection: typing.Optional[TranscribePulseWavesRequestEmotionDetection] = None,
- format: typing.Optional[TranscribePulseWavesRequestFormat] = None,
- punctuate: typing.Optional[TranscribePulseWavesRequestPunctuate] = None,
- capitalize: typing.Optional[TranscribePulseWavesRequestCapitalize] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> HttpResponse[TranscribePulseWavesResponse]:
- """
- Transcribe an audio file to text using the Pulse model. The fastest way to get a transcript when you already have a recording โ pass either the raw bytes or a URL.
-
- ## When to use this
-
- Use this endpoint when you have a complete audio file (call recording, voicemail, podcast episode) and want the transcript back in one response. For live transcription as audio arrives, use the realtime WebSocket endpoint (`WSS /waves/v1/pulse/get_text`) instead.
-
- ## Input methods
-
- Send the audio in one of two ways:
-
- 1. **Raw bytes** โ `Content-Type: application/octet-stream` with the audio in the body. All knobs (`language`, `word_timestamps`, etc.) are query parameters.
- 2. **URL** โ `Content-Type: application/json` with `{"url": "..."}` in the body. Useful when the audio already lives in object storage. Same query parameters apply.
-
- Pulse autodetects the language across 30+ supported locales. Pass `language` explicitly when you already know it โ detection is fast but skipping it is faster.
-
- ## Examples
-
- **cURL** (raw bytes)
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en&word_timestamps=true" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/octet-stream" \\
- --data-binary "@./call.wav"
- ```
-
- **cURL** (URL)
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/pulse/get_text?language=en" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{"url": "https://your-bucket.s3.amazonaws.com/call.wav"}'
- ```
-
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
- with open("./call.wav", "rb") as f:
- result = client.waves.transcribe_pulse(
- request=f.read(),
- language="en",
- word_timestamps=True,
- diarize=True,
- )
- print(result.status) # "success"
- print(result.transcription) # the transcript string
- ```
-
- **JavaScript / TypeScript** (using `fetch`)
- ```typescript
- import { readFileSync } from "node:fs";
-
- const audio = readFileSync("./call.wav");
- const params = new URLSearchParams({ language: "en", word_timestamps: "true", diarize: "true" });
-
- const res = await fetch(`https://api.smallest.ai/waves/v1/pulse/get_text?${params}`, {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/octet-stream",
- },
- body: audio,
- });
- const result = await res.json();
- console.log(result.transcription);
- ```
-
- ## Common gotchas
-
- - **Max file size is 250 MB.** Larger files return HTTP `400` with `{errors: "Audio data too large", status: "error", message: "Error handling audio data"}`. Compress to mono 16 kHz PCM if you're close to the limit; quality is unaffected.
- - **Formatting flags (`format`, `punctuate`, `capitalize`)** are accepted at the wire level and exposed in the Python SDK as of `smallestai>=4.4.0`. Today they currently return the same transcript regardless of value โ pass them in your integration so it works as the behavior changes.
- - **Webhook-driven flow**: pass `webhook_url` to receive the transcript asynchronously. The endpoint returns immediately; the transcript hits your webhook when ready. Useful for long files where you don't want to hold an HTTP connection open.
- - **Speaker diarization** (`diarize=true`) adds latency. Skip it if you only need the words.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates the Pulse model, so call this endpoint with `fetch` or `axios` as shown above.
-
- Parameters
- ----------
- request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
-
- language : typing.Optional[TranscribePulseWavesRequestLanguage]
- Language of the audio file. Set explicitly to the known language for best accuracy.
-
- **26 single-language codes** on this endpoint: `en`, `hi`, `de`, `es`, `ru`, `it`, `fr`, `nl`, `pt`, `uk`, `pl`, `cs`, `sk`, `lv`, `et`, `ro`, `fi`, `sv`, `bg`, `hu`, `da`, `lt`, `mt`, `zh`, `ja`, `ko`.
-
- **Regional auto-detect aggregators** for unknown audio:
- - `multi-eu` (default) โ auto-detects across all 21 European codes above plus `en`.
- - `multi-asian` โ auto-detects across `zh`, `ko`, `ja`, `en`.
-
- Omitting `language` routes to `multi-eu`. See the [Pulse model card](/waves/model-cards/speech-to-text/pulse) for the full table.
-
- encoding : typing.Optional[TranscribePulseWavesRequestEncoding]
- Audio encoding of the bytes you upload. Mirrors the `encoding`
- parameter on the realtime WS endpoint.
-
- - `linear16`, `linear32` โ raw PCM (16-bit and 32-bit)
- - `alaw`, `mulaw` โ 8 kHz telephony codecs
- - `opus`, `ogg_opus` โ Opus compressed audio (raw and Ogg container)
-
- When omitted, the server detects the format from the file's
- container header (works for `.wav`, `.mp3`, `.flac`, `.ogg`,
- `.m4a`, `.webm`).
-
- webhook_url : typing.Optional[str]
-
- webhook_extra : typing.Optional[str]
-
- word_timestamps : typing.Optional[bool]
- Whether to include word and utterance level timestamps in the response
-
- diarize : typing.Optional[bool]
- Whether to perform speaker diarization
-
- gender_detection : typing.Optional[TranscribePulseWavesRequestGenderDetection]
- Whether to predict the gender of the speaker
-
- emotion_detection : typing.Optional[TranscribePulseWavesRequestEmotionDetection]
- Whether to predict speaker emotions
-
- format : typing.Optional[TranscribePulseWavesRequestFormat]
- Master formatting switch for the transcript. When `false`, forces
- `punctuate=false`, `capitalize=false`, and also disables Inverse
- Text Normalization (ITN) so it cannot silently reintroduce
- punctuation or casing.
-
- When `true`, the `punctuate` and `capitalize` params take effect
- independently. Leave `format=true` and use those two to fine-tune.
-
- punctuate : typing.Optional[TranscribePulseWavesRequestPunctuate]
- When `false`, strips end-of-sentence punctuation (`.`, `,`, `?`, `!`)
- from the transcript, `words[].word`, and
- `utterances[].transcript`. Does not affect casing โ use
- `capitalize` for that. Overridden to `false` when `format=false`.
-
- capitalize : typing.Optional[TranscribePulseWavesRequestCapitalize]
- When `false`, lowercases the entire transcript output (transcript,
- `words[].word`, and `utterances[].transcript`). Does not affect
- punctuation โ use `punctuate` for that. Overridden to `false`
- when `format=false`.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- HttpResponse[TranscribePulseWavesResponse]
- Speech transcribed successfully
- """
- _response = self._client_wrapper.httpx_client.request(
- "waves/v1/pulse/get_text",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- params={
- "language": language,
- "encoding": encoding,
- "webhook_url": webhook_url,
- "webhook_extra": webhook_extra,
- "word_timestamps": word_timestamps,
- "diarize": diarize,
- "gender_detection": gender_detection,
- "emotion_detection": emotion_detection,
- "format": format,
- "punctuate": punctuate,
- "capitalize": capitalize,
- },
- content=request,
- headers={
- "content-type": "application/octet-stream",
+ "content-type": "application/octet-stream",
},
request_options=request_options,
omit=OMIT,
@@ -2711,1099 +1535,28 @@ def transcribe_pulse(
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-class AsyncRawWavesClient:
- def __init__(self, *, client_wrapper: AsyncClientWrapper):
- self._client_wrapper = client_wrapper
-
- async def get_pronunciation_dicts(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[typing.List[PronunciationDict]]:
- """
- Retrieve all pronunciation dictionaries for the authenticated user
-
- Parameters
- ----------
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[typing.List[PronunciationDict]]
- List of pronunciation dictionaries
- """
- _response = await self._client_wrapper.httpx_client.request(
- "waves/v1/pronunciation-dicts",
- base_url=self._client_wrapper.get_environment().waves,
- method="GET",
- request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- typing.List[PronunciationDict],
- construct_type(
- type_=typing.List[PronunciationDict], # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def create_pronunciation_dict(
- self, *, items: typing.Sequence[PronunciationItem], request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[PronunciationDict]:
- """
- Create a new pronunciation dictionary for the authenticated user
-
- Parameters
- ----------
- items : typing.Sequence[PronunciationItem]
- List of word-pronunciation pairs to create
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[PronunciationDict]
- Successfully created pronunciation dictionary
- """
- _response = await self._client_wrapper.httpx_client.request(
- "waves/v1/pronunciation-dicts",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- json={
- "items": convert_and_respect_annotation_metadata(
- object_=items, annotation=typing.Sequence[PronunciationItem], direction="write"
- ),
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- PronunciationDict,
- construct_type(
- type_=PronunciationDict, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def update_pronunciation_dict(
- self,
- *,
- id: str,
- items: typing.Sequence[PronunciationItem],
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[UpdatePronunciationDictResponse]:
- """
- Update an existing pronunciation dictionary for the authenticated user
-
- Parameters
- ----------
- id : str
- ID of the pronunciation dictionary to update
-
- items : typing.Sequence[PronunciationItem]
- Updated list of word-pronunciation pairs
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[UpdatePronunciationDictResponse]
- Successfully updated pronunciation dictionary
- """
- _response = await self._client_wrapper.httpx_client.request(
- "waves/v1/pronunciation-dicts",
- base_url=self._client_wrapper.get_environment().waves,
- method="PUT",
- json={
- "id": id,
- "items": convert_and_respect_annotation_metadata(
- object_=items, annotation=typing.Sequence[PronunciationItem], direction="write"
- ),
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- UpdatePronunciationDictResponse,
- construct_type(
- type_=UpdatePronunciationDictResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- async def delete_pronunciation_dict(
- self, *, id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[DeletePronunciationDictResponse]:
- """
- Delete an existing pronunciation dictionary for the authenticated user
-
- Parameters
- ----------
- id : str
- ID of the pronunciation dictionary to delete
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Returns
- -------
- AsyncHttpResponse[DeletePronunciationDictResponse]
- Successfully deleted pronunciation dictionary
- """
- _response = await self._client_wrapper.httpx_client.request(
- "waves/v1/pronunciation-dicts",
- base_url=self._client_wrapper.get_environment().waves,
- method="DELETE",
- json={
- "id": id,
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- DeletePronunciationDictResponse,
- construct_type(
- type_=DeletePronunciationDictResponse, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- @contextlib.asynccontextmanager
- async def synthesize_lightning(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningRequestOutputFormat] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- Get speech for given text using the Waves API
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[LightningRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningRequestOutputFormat]
- The format of the output audio.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- Synthesized speech retrieved successfully.
- """
- async with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning/get_speech",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "language": language,
- "output_format": output_format,
- },
- headers={
- "content-type": "application/json",
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- @contextlib.asynccontextmanager
- async def synthesize_lightning_large(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- Get speech for given text using the Waves API
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- Synthesized speech retrieved successfully.
- """
- async with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-large/get_speech",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
- "output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- @contextlib.asynccontextmanager
- async def synthesize_lightning_v2(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- Get speech for given text using the Waves API
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- Synthesized speech retrieved successfully.
- """
- async with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-v2/get_speech",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
- "output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- @contextlib.asynccontextmanager
- async def synthesize_sse_lightning_large(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningLargeRequestLanguage] = OMIT,
- output_format: typing.Optional[LightningLargeRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[typing.Any]]]:
- """
- The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
-
- ## When to Use
-
- - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
- - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
- - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
- - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-
- ## How It Works
-
- 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
- 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
- 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
- 4. **End of Stream**: The API sends a completion event when all audio has been delivered
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[LightningLargeRequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[LightningLargeRequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Yields
- ------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[typing.Any]]]
- Synthesized speech retrieved successfully.
- """
- async with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-large/stream",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
- "output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[typing.Any]]:
- try:
- if 200 <= _response.status_code < 300:
-
- async def _iter():
- _event_source = EventSource(_response)
- async for _sse in _event_source.aiter_sse():
- if _sse.data == None:
- return
- try:
- yield typing.cast(
- typing.Any,
- parse_sse_obj(
- sse=_sse,
- type_=typing.Any, # type: ignore
- ),
- )
- except JSONDecodeError as e:
- warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}")
- except (TypeError, ValueError, KeyError, AttributeError) as e:
- warning(
- f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- except Exception as e:
- error(
- f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- return
-
- return AsyncHttpResponse(response=_response, data=_iter())
- await _response.aread()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- @contextlib.asynccontextmanager
- async def synthesize_sse_lightning_v2(
- self,
- *,
- text: str,
- voice_id: str,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- consistency: typing.Optional[float] = OMIT,
- similarity: typing.Optional[float] = OMIT,
- enhancement: typing.Optional[float] = OMIT,
- language: typing.Optional[Lightningv2RequestLanguage] = OMIT,
- output_format: typing.Optional[Lightningv2RequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[typing.Any]]]:
- """
- The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
- For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
-
- ## When to Use
-
- - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
- - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
- - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
- - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
-
- ## How It Works
-
- 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
- 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
- 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
- 4. **End of Stream**: The API sends a completion event when all audio has been delivered
-
- Parameters
- ----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- consistency : typing.Optional[float]
- This parameter controls word repetition and skipping. Decrease it to prevent skipped words, and increase it to prevent repetition.
-
- similarity : typing.Optional[float]
- This parameter controls the similarity between the generated speech and the reference audio. Increase it to make the speech more similar to the reference audio.
-
- enhancement : typing.Optional[float]
- Enhances speech quality at the cost of increased latency.
-
- language : typing.Optional[Lightningv2RequestLanguage]
- Determines how numbers are spelled out. If set to 'en', numbers will be read as individual digits in English. If set to 'hi', numbers will be read as individual digits in Hindi.
-
- output_format : typing.Optional[Lightningv2RequestOutputFormat]
- The format of the output audio.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
-
- Yields
- ------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[typing.Any]]]
- Synthesized speech retrieved successfully.
- """
- async with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-v2/stream",
- base_url=self._client_wrapper.get_environment().waves,
- method="POST",
- json={
- "text": text,
- "voice_id": voice_id,
- "sample_rate": sample_rate,
- "speed": speed,
- "consistency": consistency,
- "similarity": similarity,
- "enhancement": enhancement,
- "language": language,
- "output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- },
- request_options=request_options,
- omit=OMIT,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[typing.Any]]:
- try:
- if 200 <= _response.status_code < 300:
-
- async def _iter():
- _event_source = EventSource(_response)
- async for _sse in _event_source.aiter_sse():
- if _sse.data == None:
- return
- try:
- yield typing.cast(
- typing.Any,
- parse_sse_obj(
- sse=_sse,
- type_=typing.Any, # type: ignore
- ),
- )
- except JSONDecodeError as e:
- warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}")
- except (TypeError, ValueError, KeyError, AttributeError) as e:
- warning(
- f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- except Exception as e:
- error(
- f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- return
-
- return AsyncHttpResponse(response=_response, data=_iter())
- await _response.aread()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
+class AsyncRawWavesClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
- async def get_voices(
- self, model: GetVoicesWavesRequestModel, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetVoicesWavesResponse]:
+ async def get_pronunciation_dicts(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[typing.List[PronunciationDict]]:
"""
- List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/waves/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/waves/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
+ Retrieve all pronunciation dictionaries for the authenticated user
Parameters
----------
- model : GetVoicesWavesRequestModel
- The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[GetVoicesWavesResponse]
- Voices retrieved successfully.
+ AsyncHttpResponse[typing.List[PronunciationDict]]
+ List of pronunciation dictionaries
"""
_response = await self._client_wrapper.httpx_client.request(
- f"waves/v1/{encode_path_param(model)}/get_voices",
+ "waves/v1/pronunciation-dicts",
base_url=self._client_wrapper.get_environment().waves,
method="GET",
request_options=request_options,
@@ -3811,24 +1564,13 @@ async def get_voices(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetVoicesWavesResponse,
+ typing.List[PronunciationDict],
construct_type(
- type_=GetVoicesWavesResponse, # type: ignore
+ type_=typing.List[PronunciationDict], # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 401:
raise UnauthorizedError(
headers=dict(_response.headers),
@@ -3860,53 +1602,46 @@ async def get_voices(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def add_voice(
- self, *, display_name: str, file: core.File, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[AddVoiceWavesResponse]:
+ async def create_pronunciation_dict(
+ self, *, items: typing.Sequence[PronunciationItem], request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[PronunciationDict]:
"""
- **Deprecated** โ use `POST /waves/v1/voice-cloning` instead. The new
- endpoint defaults to `lightning-v3.1`, supports optional metadata,
- and returns pre-generated sample clips. This endpoint only clones
- onto `lightning-large` and the resulting voices do not work on
- `lightning-v3.1` (returns an empty WAV). Kept live for backward
- compatibility; new integrations should migrate.
+ Create a new pronunciation dictionary for the authenticated user
Parameters
----------
- display_name : str
- Display name for the voice clone.
-
- file : core.File
- See core.File for more documentation
+ items : typing.Sequence[PronunciationItem]
+ List of word-pronunciation pairs to create
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[AddVoiceWavesResponse]
- Voice clone created successfully
+ AsyncHttpResponse[PronunciationDict]
+ Successfully created pronunciation dictionary
"""
_response = await self._client_wrapper.httpx_client.request(
- "waves/v1/lightning-large/add_voice",
+ "waves/v1/pronunciation-dicts",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
- data={
- "displayName": display_name,
+ json={
+ "items": convert_and_respect_annotation_metadata(
+ object_=items, annotation=typing.Sequence[PronunciationItem], direction="write"
+ ),
},
- files={
- "file": file,
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
omit=OMIT,
- force_multipart=True,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- AddVoiceWavesResponse,
+ PronunciationDict,
construct_type(
- type_=AddVoiceWavesResponse, # type: ignore
+ type_=PronunciationDict, # type: ignore
object_=_response.json(),
),
)
@@ -3953,36 +1688,54 @@ async def add_voice(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_cloned_voices(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[GetClonedVoicesWavesResponse]:
+ async def update_pronunciation_dict(
+ self,
+ *,
+ id: str,
+ items: typing.Sequence[PronunciationItem],
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[UpdatePronunciationDictResponse]:
"""
- **Deprecated** โ use `GET /waves/v1/voice-cloning` instead. The new
- list endpoint returns the same data plus a `modelIds` array per
- clone. Kept live for backward compatibility.
+ Update an existing pronunciation dictionary for the authenticated user
Parameters
----------
+ id : str
+ ID of the pronunciation dictionary to update
+
+ items : typing.Sequence[PronunciationItem]
+ Updated list of word-pronunciation pairs
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[GetClonedVoicesWavesResponse]
- Voices retrieved successfully.
+ AsyncHttpResponse[UpdatePronunciationDictResponse]
+ Successfully updated pronunciation dictionary
"""
_response = await self._client_wrapper.httpx_client.request(
- "waves/v1/lightning-large/get_cloned_voices",
+ "waves/v1/pronunciation-dicts",
base_url=self._client_wrapper.get_environment().waves,
- method="GET",
+ method="PUT",
+ json={
+ "id": id,
+ "items": convert_and_respect_annotation_metadata(
+ object_=items, annotation=typing.Sequence[PronunciationItem], direction="write"
+ ),
+ },
+ headers={
+ "content-type": "application/json",
+ },
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetClonedVoicesWavesResponse,
+ UpdatePronunciationDictResponse,
construct_type(
- type_=GetClonedVoicesWavesResponse, # type: ignore
+ type_=UpdatePronunciationDictResponse, # type: ignore
object_=_response.json(),
),
)
@@ -4029,33 +1782,31 @@ async def get_cloned_voices(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def delete_voice(
- self, *, voice_id: str, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[DeleteVoiceWavesResponse]:
+ async def delete_pronunciation_dict(
+ self, *, id: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[DeletePronunciationDictResponse]:
"""
- Delete a voice clone by `voiceId`. Despite the `/lightning-large/`
- path, this endpoint deletes any voice clone on the organization,
- including clones created via `POST /waves/v1/voice-cloning`.
+ Delete an existing pronunciation dictionary for the authenticated user
Parameters
----------
- voice_id : str
- The unique identifier of the voice clone to delete.
+ id : str
+ ID of the pronunciation dictionary to delete
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[DeleteVoiceWavesResponse]
- Voice clone deleted successfully
+ AsyncHttpResponse[DeletePronunciationDictResponse]
+ Successfully deleted pronunciation dictionary
"""
_response = await self._client_wrapper.httpx_client.request(
- "waves/v1/lightning-large",
+ "waves/v1/pronunciation-dicts",
base_url=self._client_wrapper.get_environment().waves,
method="DELETE",
json={
- "voiceId": voice_id,
+ "id": id,
},
headers={
"content-type": "application/json",
@@ -4066,9 +1817,9 @@ async def delete_voice(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- DeleteVoiceWavesResponse,
+ DeletePronunciationDictResponse,
construct_type(
- type_=DeleteVoiceWavesResponse, # type: ignore
+ type_=DeletePronunciationDictResponse, # type: ignore
object_=_response.json(),
),
)
@@ -4115,533 +1866,374 @@ async def delete_voice(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.asynccontextmanager
- async def synthesize_tts(
- self,
- *,
- text: str,
- voice_id: str,
- model: typing.Optional[TtsRequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[TtsRequestLanguage] = OMIT,
- output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- word_timestamps: typing.Optional[bool] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
-
- Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
-
- **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). On `lightning_v3.1` the full 12-language catalog applies (see voice catalog).
-
- ## When to use this
-
- - **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
- - **Use `/waves/v1/tts/live`** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
- - **Use `/waves/v1/tts/live`** (WebSocket) when text arrives incrementally (LLM token streams, live captioning).
-
- ## Key features
-
- - 44 kHz natural, expressive synthesis
- - Model selectable per request via `model` body parameter
- - Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
- - 12 documented languages on `lightning_v3.1`. On `lightning_v3.1_pro`: `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`.
- - Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- - Sample rates: 8 kHz โ 44.1 kHz
- - Speed: 0.5ร โ 2ร
- - Per-call pronunciation dictionaries via `pronunciation_dicts`
-
- ## Examples
-
- **cURL โ Lightning v3.1 (default)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Hello from Waves TTS.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- **cURL โ Lightning v3.1 Pro (omit `language` โ defaults to `en + hi`)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Hello from the Lightning v3.1 Pro pool.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- **cURL โ Lightning v3.1 Pro with explicit `language: en` (UK + American accented English)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Good morning, this is a Pro voice speaking.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "language": "en",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- **cURL โ Lightning v3.1 Pro with explicit `language: hi` (Indian accented English + Hindi)**
- ```bash
- curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -H "Accept: audio/wav" \\
- -d '{
- "text": "Namaste, this is an Indian-accented Pro voice.",
- "voice_id": "meher",
- "model": "lightning_v3.1_pro",
- "language": "hi",
- "sample_rate": 24000,
- "output_format": "wav"
- }' --output speech.wav
- ```
-
- ## Common gotchas
-
- - **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
- - **Pair voice IDs with the right model.** Voice catalogs differ between `lightning_v3.1` and `lightning_v3.1_pro`. The API does not reject mismatched pairings, but using a Pro-only `voice_id` with `model=lightning_v3.1` (or omitting `model`) can return wrong or hallucinated audio. Pair Pro voices with `model=lightning_v3.1_pro`; standard catalog voices with `model=lightning_v3.1` (the default).
- - **Cloned voices** (`voice_*` from `add_voice`) work with `lightning_v3.1` only; voice cloning is not available on `lightning_v3.1_pro`.
- - **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
-
+ async def synthesize_lightning(
+ self,
+ *,
+ output_format: typing.Optional[SynthesizeLightningWavesRequestOutputFormat] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[None]:
+ """
+ Get speech for given text using the Waves API
+
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation. See the model card for available voices per model.
-
- model : typing.Optional[TtsRequestModel]
- TTS model to route the request to. Controls which model pool serves
- this synthesis.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
- quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
- for supported voice IDs.
-
- Same concurrency and latency profile across both. Other request
- parameters behave identically.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[TtsRequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- Each voice has its own `tags.language` set in the voice catalog โ
- query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
- the voice was trained on; passing other codes is accepted by the
- API but produces English-pronounced output.
-
- **On `lightning_v3.1`**, the full 12-language catalog applies.
-
- **On `lightning_v3.1_pro`**:
- - Pass `en` โ UK + American accented English.
- - Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
-
- output_format : typing.Optional[TtsRequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
-
- word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
-
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
-
+ output_format : typing.Optional[SynthesizeLightningWavesRequestOutputFormat]
+
request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
+ Request-specific configuration.
+
Returns
-------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- Synthesized speech retrieved successfully.
+ AsyncHttpResponse[None]
"""
- async with self._client_wrapper.httpx_client.stream(
- "waves/v1/tts",
+ _response = await self._client_wrapper.httpx_client.request(
+ "waves/v1/lightning/get_speech",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
- "text": text,
- "voice_id": voice_id,
- "model": model,
- "sample_rate": sample_rate,
- "speed": speed,
- "language": language,
"output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- "word_timestamps": word_timestamps,
- "session_id": session_id,
- "request_id": request_id,
},
headers={
"content-type": "application/json",
- "Accept": "audio/wav",
},
request_options=request_options,
omit=OMIT,
- ) as _response:
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return AsyncHttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ async def synthesize_lightning_large(
+ self,
+ *,
+ output_format: typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[None]:
+ """
+ Get speech for given text using the Waves API
- yield await _stream()
+ Parameters
+ ----------
+ output_format : typing.Optional[SynthesizeLightningLargeWavesRequestOutputFormat]
- @contextlib.asynccontextmanager
- async def synthesize_sse_tts(
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[None]
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "waves/v1/lightning-large/get_speech",
+ base_url=self._client_wrapper.get_environment().waves,
+ method="POST",
+ json={
+ "output_format": output_format,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return AsyncHttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def synthesize_sse_lightning_large(
self,
*,
- text: str,
- voice_id: str,
- model: typing.Optional[TtsRequestModel] = OMIT,
- sample_rate: typing.Optional[int] = OMIT,
- speed: typing.Optional[float] = OMIT,
- language: typing.Optional[TtsRequestLanguage] = OMIT,
- output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
- pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
- word_timestamps: typing.Optional[bool] = OMIT,
- session_id: typing.Optional[str] = OMIT,
- request_id: typing.Optional[str] = OMIT,
+ output_format: typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[None]:
+ """
+ The Lightning-Large SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
+
+ ## When to Use
+
+ - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+ - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+ - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+ - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
+
+ ## How It Works
+
+ 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+ 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+ 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+ 4. **End of Stream**: The API sends a completion event when all audio has been delivered
+
+ Parameters
+ ----------
+ output_format : typing.Optional[SynthesizeSseLightningLargeWavesRequestOutputFormat]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[None]
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "waves/v1/lightning-large/stream",
+ base_url=self._client_wrapper.get_environment().waves,
+ method="POST",
+ json={
+ "output_format": output_format,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return AsyncHttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def synthesize_lightning_v2(
+ self,
+ *,
+ output_format: typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[str]]]:
+ ) -> AsyncHttpResponse[None]:
"""
- Synthesize speech and stream the audio back over Server-Sent Events. Same body as `/waves/v1/tts` โ the only difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
-
- Pick the model with the `model` body parameter, same as the sync route.
-
-
- **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/waves/api-reference/api-reference/text-to-speech/tts).
-
-
- ## When to use this
-
- - **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
- - **Use sync `/waves/v1/tts`** when total latency doesn't matter and you'd rather get one buffer.
- - **Use `/waves/v1/tts/live`** (WebSocket) when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
-
- ## How it works
-
- 1. POST your text + voice settings โ same payload as `/waves/v1/tts`, plus optional `model`.
- 2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\\n` followed by `data: {"audio": ""}\\n\\n`.
- 3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
- 4. A final `data: {"done": true}\\n\\n` frame marks end of stream.
-
- ## Examples
-
- **cURL**
- ```bash
- curl -N -X POST "https://api.smallest.ai/waves/v1/tts/live" \\
- -H "Authorization: Bearer $SMALLEST_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{
- "text": "Streaming this paragraph chunk by chunk so playback can start sooner.",
- "voice_id": "magnus",
- "sample_rate": 24000,
- "output_format": "pcm"
- }'
- ```
-
- ## Common gotchas
-
- - **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
- - **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
- - **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
-
+ Get speech for given text using the Waves API
+
Parameters
----------
- text : str
- The text to convert to speech.
-
- voice_id : str
- The voice identifier to use for speech generation. See the model card for available voices per model.
-
- model : typing.Optional[TtsRequestModel]
- TTS model to route the request to. Controls which model pool serves
- this synthesis.
-
- - `lightning_v3.1` (default) โ standard Lightning v3.1.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
- quality and naturalness, with a curated voice catalog. See the
- [Lightning v3.1 Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro)
- for supported voice IDs.
-
- Same concurrency and latency profile across both. Other request
- parameters behave identically.
-
- sample_rate : typing.Optional[int]
- The sample rate for the generated audio.
-
- speed : typing.Optional[float]
- The speed of the generated speech.
-
- language : typing.Optional[TtsRequestLanguage]
- Language code for synthesis. Influences pronunciation, number/date
- normalization, and phoneme selection.
-
- Each voice has its own `tags.language` set in the voice catalog โ
- query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
- the voice was trained on; passing other codes is accepted by the
- API but produces English-pronounced output.
-
- **On `lightning_v3.1`**, the full 12-language catalog applies.
-
- **On `lightning_v3.1_pro`**:
- - Pass `en` โ UK + American accented English.
- - Pass `hi` โ Indian accented English + Hindi (code-switching).
- - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage).
-
- output_format : typing.Optional[TtsRequestOutputFormat]
- Format of the returned audio. `pcm` is the lowest-latency option
- but requires a decoder to play; `mp3` and `wav` are directly
- playable in browsers and most media players. The server default
- is `pcm` when the field is omitted โ the API playground uses
- `mp3` so the generated audio is directly playable.
-
- pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
-
- word_timestamps : typing.Optional[bool]
- **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/waves/documentation/text-to-speech-lightning/word-timestamps).
-
- session_id : typing.Optional[str]
- Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
-
- request_id : typing.Optional[str]
- Optional client-provided request identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Request-Id`.
-
+ output_format : typing.Optional[SynthesizeLightningV2WavesRequestOutputFormat]
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
-
- Yields
- ------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[str]]]
- Synthesized speech retrieved successfully.
+
+ Returns
+ -------
+ AsyncHttpResponse[None]
"""
- async with self._client_wrapper.httpx_client.stream(
- "waves/v1/tts/live",
+ _response = await self._client_wrapper.httpx_client.request(
+ "waves/v1/lightning-v2/get_speech",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
- "text": text,
- "voice_id": voice_id,
- "model": model,
- "sample_rate": sample_rate,
- "speed": speed,
- "language": language,
"output_format": output_format,
- "pronunciation_dicts": pronunciation_dicts,
- "word_timestamps": word_timestamps,
- "session_id": session_id,
- "request_id": request_id,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
omit=OMIT,
- ) as _response:
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return AsyncHttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[str]]:
- try:
- if 200 <= _response.status_code < 300:
+ async def synthesize_sse_lightning_v2(
+ self,
+ *,
+ output_format: typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[None]:
+ """
+ The Lightning v2 SSE API provides real-time text-to-speech streaming capabilities with high-quality voice synthesis. This API uses Server-Sent Events (SSE) to deliver audio chunks as they're generated, enabling low-latency audio playback without waiting for the entire audio file to process.
+ For an end-to-end example of how to use the Lightning v2 SSE API, check out [Text to Speech (SSE) Example](https://github.com/smallest-inc/waves-examples/blob/main/lightning_v2/http_streaming/http_streaming_api.py)
- async def _iter():
- _event_source = EventSource(_response)
- async for _sse in _event_source.aiter_sse():
- if _sse.data == None:
- return
- try:
- yield typing.cast(
- str,
- parse_sse_obj(
- sse=_sse,
- type_=str, # type: ignore
- ),
- )
- except JSONDecodeError as e:
- warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}")
- except (TypeError, ValueError, KeyError, AttributeError) as e:
- warning(
- f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- except Exception as e:
- error(
- f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}"
- )
- return
+ ## When to Use
- return AsyncHttpResponse(response=_response, data=_iter())
- await _response.aread()
- if _response.status_code == 400:
- raise BadRequestError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 401:
- raise UnauthorizedError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- if _response.status_code == 500:
- raise InternalServerError(
- headers=dict(_response.headers),
- body=typing.cast(
- typing.Any,
- construct_type(
- type_=typing.Any, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ - **Interactive Applications**: Perfect for chatbots, virtual assistants, and other applications requiring immediate voice responses
+ - **Long-Form Content**: Efficiently stream audio for articles, stories, or other long-form content without buffering delays
+ - **Voice User Interfaces**: Create natural-sounding voice interfaces with minimal perceived latency
+ - **Accessibility Solutions**: Provide real-time audio versions of written content for users with visual impairments
+
+ ## How It Works
+
+ 1. **Make a POST Request**: Send your text and voice settings to the API endpoint
+ 2. **Receive Audio Chunks**: The API processes your text and streams audio back as base64-encoded chunks with 1024 byte size
+ 3. **Process the Stream**: Handle the SSE events to decode and play audio chunks sequentially
+ 4. **End of Stream**: The API sends a completion event when all audio has been delivered
+
+ Parameters
+ ----------
+ output_format : typing.Optional[SynthesizeSseLightningV2WavesRequestOutputFormat]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[None]
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "waves/v1/lightning-v2/stream",
+ base_url=self._client_wrapper.get_environment().waves,
+ method="POST",
+ json={
+ "output_format": output_format,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return AsyncHttpResponse(response=_response, data=None)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- yield await _stream()
+ async def get_voices(
+ self, model: GetVoicesWavesRequestModel, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetVoicesWavesResponse]:
+ """
+ List voices available for Lightning v3.1. The response is the union of the standard and Pro voice catalogs โ the API does not return a per-voice "is Pro" flag, so consult the [Lightning v3.1 Pro](/models/model-cards/text-to-speech/lightning-v-3-1-pro) and [Lightning v3.1](/models/model-cards/text-to-speech/lightning-v-3-1) model cards for the canonical per-pool voice lists. Use the `voice_id` from this response together with `"model": "lightning_v3.1"` (default) or `"model": "lightning_v3.1_pro"` on the unified `/waves/v1/tts` route to pick the pool.
+
+ Parameters
+ ----------
+ model : GetVoicesWavesRequestModel
+ The catalog to query. Currently only `lightning-v3.1` is supported โ the response returns the union of standard Lightning v3.1 voices and Lightning v3.1 Pro voices. The API does not include a per-voice Pro flag; consult the model cards for the canonical per-pool catalogs.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetVoicesWavesResponse]
+ Voices retrieved successfully.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"waves/v1/{encode_path_param(model)}/get_voices",
+ base_url=self._client_wrapper.get_environment().waves,
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetVoicesWavesResponse,
+ construct_type(
+ type_=GetVoicesWavesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 400:
+ raise BadRequestError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 500:
+ raise InternalServerError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ typing.Any,
+ construct_type(
+ type_=typing.Any, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
@contextlib.asynccontextmanager
- async def synthesize_lightning_v31(
+ async def synthesize_tts(
self,
*,
text: str,
voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
+ model: typing.Optional[TtsRequestModel] = OMIT,
sample_rate: typing.Optional[int] = OMIT,
speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
+ language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
+ output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ word_timestamps: typing.Optional[bool] = OMIT,
session_id: typing.Optional[str] = OMIT,
request_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
"""
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech) and select Lightning v3.1 via the `model` body field (default).
+ Synthesize speech from text in a single request. Pass `text` + `voice_id`, get back binary audio.
- Synthesize speech from text in a single request. The simplest way to get audio when you have the full text up front โ pass `text` + `voice_id`, get back binary audio.
+ Pick the model with the `model` body parameter: default `lightning_v3.1`, or `lightning_v3.1_pro` for the Pro pool. Other request parameters are identical across models.
+
+ **Language behaviour on `lightning_v3.1_pro`:** pass `language: en` for UK + American accented English, pass `language: hi` for Indian accented English + Hindi (code-switching), or omit `language` to default to `en + hi` (mixed Indian + Western English coverage). Pro supports 31 languages total (10 Indic, 8 Asian & Middle Eastern, 13 European including Dutch and Swedish). Pass the matching ISO 639-1 code (e.g. `ta`, `de`, `ja`) with a Pro voice from that language, or use `auto` to route across all supported languages with any English or Hindi voice. See the [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro#supported-languages) for the full list. On `lightning_v3.1` the model accepts 20 language codes (10 European + 10 Indic) plus `auto`; the trained voice catalog covers 12 of those directly.
## When to use this
- **Use this** for short utterances you can render before playback (notifications, prompts, batch jobs, audio file generation).
- - **Use the SSE streaming endpoint** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
- - **Use the WebSocket endpoint** when text arrives incrementally (LLM token streams, live captioning).
+ - **Use `/waves/v1/tts/live`** when you want playback to start before the full audio is ready (long passages, latency-sensitive apps).
+ - **Use `/waves/v1/tts/live`** (WebSocket) when text arrives incrementally (LLM token streams, live captioning).
## Key features
- 44 kHz natural, expressive synthesis
- - Cloned voice IDs (`voice_*`) work โ same param as catalog voices
- - 12 documented languages โ see the model card for the full list
+ - Model selectable per request via `model` body parameter
+ - Cloned voice IDs (`voice_*`) work on `lightning_v3.1` โ same param as catalog voices
+ - 20 accepted language codes on `lightning_v3.1` (12 with trained voices, 8 additional routed via English/Hindi voices). On `lightning_v3.1_pro`: 31 languages with dedicated voices (10 Indic, 8 Asian & Middle Eastern, 13 European); `language: en` โ UK + American accented English; `language: hi` โ Indian accented English + Hindi; omit `language` โ defaults to `en + hi`. Both models accept `language: auto` for cross-language routing.
- Output formats: `pcm`, `mp3`, `wav`, `ulaw`, `alaw`
- Sample rates: 8 kHz โ 44.1 kHz
- Speed: 0.5ร โ 2ร
@@ -4649,68 +2241,73 @@ async def synthesize_lightning_v31(
## Examples
- **cURL**
+ **cURL โ Lightning v3.1 (default)**
```bash
- curl -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech" \\
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
-H "Authorization: Bearer $SMALLEST_API_KEY" \\
-H "Content-Type: application/json" \\
-H "Accept: audio/wav" \\
-d '{
- "text": "Hello from Lightning v3.1.",
+ "text": "Hello from Waves TTS.",
"voice_id": "magnus",
"sample_rate": 24000,
"output_format": "wav"
}' --output speech.wav
```
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
+ **cURL โ Lightning v3.1 Pro (omit `language` โ defaults to `en + hi`)**
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/json" \\
+ -H "Accept: audio/wav" \\
+ -d '{
+ "text": "Hello from the Lightning v3.1 Pro pool.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
+ ```
- with open("speech.wav", "wb") as f:
- for chunk in client.waves.synthesize_lightning_v3_1(
- text="Hello from Lightning v3.1.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="wav",
- # Optional: cloned voice support
- # voice_id="voice_FlPKRWI7DX",
- # Optional: pin pronunciations for specific words
- # pronunciation_dicts=[""],
- ):
- f.write(chunk)
+ **cURL โ Lightning v3.1 Pro with explicit `language: en` (UK + American accented English)**
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/json" \\
+ -H "Accept: audio/wav" \\
+ -d '{
+ "text": "Good morning, this is a Pro voice speaking.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "language": "en",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
```
- **JavaScript / TypeScript** (using `fetch`)
- ```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/get_speech", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- Accept: "audio/wav",
- },
- body: JSON.stringify({
- text: "Hello from Lightning v3.1.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "wav",
- }),
- });
- const audio = Buffer.from(await res.arrayBuffer());
- require("node:fs").writeFileSync("speech.wav", audio);
+ **cURL โ Lightning v3.1 Pro with explicit `language: hi` (Indian accented English + Hindi)**
+ ```bash
+ curl -X POST "https://api.smallest.ai/waves/v1/tts" \\
+ -H "Authorization: Bearer $SMALLEST_API_KEY" \\
+ -H "Content-Type: application/json" \\
+ -H "Accept: audio/wav" \\
+ -d '{
+ "text": "Namaste, this is an Indian-accented Pro voice.",
+ "voice_id": "meher",
+ "model": "lightning_v3.1_pro",
+ "language": "hi",
+ "sample_rate": 24000,
+ "output_format": "wav"
+ }' --output speech.wav
```
## Common gotchas
- **Set `Accept: audio/wav`.** Omitting it can return an empty or unplayable response.
- - **Cloned voices** (`voice_*` from `add_voice`) work on this endpoint and support `pronunciation_dicts`.
- - **`pronunciation_dicts` validates IDs at request time.** Passing an unknown ID returns `Invalid input data` โ create the dict first via the pronunciation-dicts endpoint and save the returned `id`.
- - **Pronunciation matching is case-sensitive.** Add both `Synopsis` and `synopsis` if your text uses both casings.
+ - **Pair voice IDs with the right model.** Voice catalogs differ between `lightning_v3.1` and `lightning_v3.1_pro`. The API does not reject mismatched pairings, but using a Pro-only `voice_id` with `model=lightning_v3.1` (or omitting `model`) can return wrong or hallucinated audio. Pair Pro voices with `model=lightning_v3.1_pro`; standard catalog voices with `model=lightning_v3.1` (the default).
+ - **Cloned voices** (`voice_*` from `add_voice`) work with `lightning_v3.1` only; voice cloning is not available on `lightning_v3.1_pro`.
- **44.1 kHz output** is supported but most playback environments are happy with 24 kHz โ drop the sample rate if bandwidth matters.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` or `axios` as shown above.
Parameters
----------
@@ -4718,19 +2315,20 @@ async def synthesize_lightning_v31(
The text to convert to speech.
voice_id : str
- The voice identifier to use for speech generation.
+ The voice identifier to use for speech generation. See the model card for available voices per model.
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
+ model : typing.Optional[TtsRequestModel]
+ TTS model to route the request to. Controls which model pool serves
+ this synthesis.
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
+ - `lightning_v3.1` (default) โ standard Lightning v3.1.
+ - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
+ quality and naturalness, with a curated voice catalog. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
+ for supported voice IDs.
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
+ Same concurrency and latency profile across both. Other request
+ parameters behave identically.
sample_rate : typing.Optional[int]
The sample rate for the generated audio.
@@ -4738,16 +2336,62 @@ async def synthesize_lightning_v31(
speed : typing.Optional[float]
The speed of the generated speech.
- language : typing.Optional[LightningV31RequestLanguage]
+ language : typing.Optional[TtsRequestLanguage]
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
+ Each voice has its own `tags.language` set in the voice catalog โ
+ query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
+ the voice was trained on; passing other codes is accepted by the
+ API but produces English-pronounced output.
+
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
+
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
+ - Pass `en` โ UK + American accented English.
+ - Pass `hi` โ Indian accented English + Hindi (code-switching).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
- output_format : typing.Optional[LightningV31RequestOutputFormat]
+ output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
but requires a decoder to play; `mp3` and `wav` are directly
playable in browsers and most media players. The server default
@@ -4755,7 +2399,10 @@ async def synthesize_lightning_v31(
`mp3` so the generated audio is directly playable.
pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
+
+ word_timestamps : typing.Optional[bool]
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -4772,7 +2419,7 @@ async def synthesize_lightning_v31(
Synthesized speech retrieved successfully.
"""
async with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-v3.1/get_speech",
+ "waves/v1/tts",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
@@ -4782,8 +2429,10 @@ async def synthesize_lightning_v31(
"sample_rate": sample_rate,
"speed": speed,
"language": language,
+ "number_pronunciation_language": number_pronunciation_language,
"output_format": output_format,
"pronunciation_dicts": pronunciation_dicts,
+ "word_timestamps": word_timestamps,
"session_id": session_id,
"request_id": request_id,
},
@@ -4854,35 +2503,41 @@ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
yield await _stream()
@contextlib.asynccontextmanager
- async def synthesize_sse_lightning_v31(
+ async def synthesize_sse_tts(
self,
*,
text: str,
voice_id: str,
- model: typing.Optional[LightningV31RequestModel] = OMIT,
+ model: typing.Optional[TtsRequestModel] = OMIT,
sample_rate: typing.Optional[int] = OMIT,
speed: typing.Optional[float] = OMIT,
- language: typing.Optional[LightningV31RequestLanguage] = OMIT,
- output_format: typing.Optional[LightningV31RequestOutputFormat] = OMIT,
+ language: typing.Optional[TtsRequestLanguage] = OMIT,
+ number_pronunciation_language: typing.Optional[TtsRequestNumberPronunciationLanguage] = OMIT,
+ output_format: typing.Optional[TtsRequestOutputFormat] = OMIT,
pronunciation_dicts: typing.Optional[typing.Sequence[str]] = OMIT,
+ word_timestamps: typing.Optional[bool] = OMIT,
session_id: typing.Optional[str] = OMIT,
request_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[typing.Any]]]:
+ ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[str]]]:
"""
- **Endpoint scheduled for retirement.** This URL will stop accepting requests **60 days from the Lightning v3.1 Pro launch (2026-05-15)** โ i.e. on **2026-07-14**. The Lightning v3.1 model itself is current and stays. Migrate to [`POST /waves/v1/tts/live`](/waves/api-reference/api-reference/text-to-speech/synthesize-speech-sse) and select Lightning v3.1 via the `model` body field (default).
+ Synthesize speech and stream the audio back over Server-Sent Events. Same body as `/waves/v1/tts` โ the only difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
- Synthesize speech and stream the audio back over Server-Sent Events. The body and parameters are identical to the sync `/get_speech` endpoint โ the difference is the response is a stream of base64-encoded PCM chunks instead of one binary blob.
+ Pick the model with the `model` body parameter, same as the sync route.
+
+
+ **The same URL serves the WebSocket endpoint.** `wss://api.smallest.ai/waves/v1/tts/live` accepts a WebSocket upgrade for streaming-text scenarios (LLM token streams, live captioning). The HTTP `POST` documented on this page returns SSE; use `wss://` to use the WebSocket protocol instead. See the [WebSocket reference](/models/api-reference/text-to-speech/stream-speech-web-socket).
+
## When to use this
- **Use this** when you want playback to start before synthesis is complete โ long passages, latency-sensitive UI, live narration.
- - **Use sync `/get_speech`** when total latency doesn't matter and you'd rather get one buffer.
- - **Use the WebSocket endpoint** when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
+ - **Use sync `/waves/v1/tts`** when total latency doesn't matter and you'd rather get one buffer.
+ - **Use `/waves/v1/tts/live`** (WebSocket) when the *text* arrives incrementally (LLM token stream). SSE assumes you have the full text up front.
## How it works
- 1. POST your text + voice settings โ same payload as `/get_speech`.
+ 1. POST your text + voice settings โ same payload as `/waves/v1/tts`, plus optional `model`.
2. The response is `Content-Type: text/event-stream`. Each chunk frame is `event: audio\\n` followed by `data: {"audio": ""}\\n\\n`.
3. Decode each chunk's `audio` field with base64 and feed the PCM bytes to your audio pipeline (browser `MediaSource`, ffmpeg pipe, raw PCM player, etc.).
4. A final `data: {"done": true}\\n\\n` frame marks end of stream.
@@ -4891,7 +2546,7 @@ async def synthesize_sse_lightning_v31(
**cURL**
```bash
- curl -N -X POST "https://api.smallest.ai/waves/v1/lightning-v3.1/stream" \\
+ curl -N -X POST "https://api.smallest.ai/waves/v1/tts/live" \\
-H "Authorization: Bearer $SMALLEST_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
@@ -4902,74 +2557,11 @@ async def synthesize_sse_lightning_v31(
}'
```
- **Python** (`pip install smallestai>=4.4.0`)
- ```python
- import base64
- from smallestai import SmallestAI
-
- client = SmallestAI(api_key="YOUR_API_KEY")
-
- with open("stream.pcm", "wb") as f:
- for chunk in client.waves.synthesize_sse_lightning_v3_1(
- text="Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id="magnus",
- sample_rate=24000,
- output_format="pcm",
- ):
- # Each chunk is `{"audio": ""}`.
- # Decode and pipe to your audio pipeline.
- if chunk.get("audio"):
- f.write(base64.b64decode(chunk["audio"]))
- ```
-
- **JavaScript / TypeScript** (using `fetch` + a reader)
- ```typescript
- const res = await fetch("https://api.smallest.ai/waves/v1/lightning-v3.1/stream", {
- method: "POST",
- headers: {
- Authorization: `Bearer ${process.env.SMALLEST_API_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- text: "Streaming this paragraph chunk by chunk so playback can start sooner.",
- voice_id: "magnus",
- sample_rate: 24000,
- output_format: "pcm",
- }),
- });
-
- const reader = res.body!.getReader();
- const decoder = new TextDecoder();
- let buf = "";
- let finished = false;
- while (!finished) {
- const { value, done } = await reader.read();
- if (done) break;
- buf += decoder.decode(value);
- const events = buf.split("\\n\\n");
- buf = events.pop() ?? "";
- for (const ev of events) {
- // SSE frames are "event: audio\\ndata: {json}" or just "data: {json}".
- // We only care about the data line โ pull it out and parse.
- const dataLine = ev.split("\\n").find((l) => l.startsWith("data:"));
- if (!dataLine) continue;
- const payload = JSON.parse(dataLine.slice(5).trim());
- if (payload.done) { finished = true; break; }
- if (payload.audio) {
- const pcm = Buffer.from(payload.audio, "base64");
- // โฆ hand pcm to your audio pipeline
- }
- }
- }
- ```
-
## Common gotchas
- **Use a streaming-friendly client.** `curl -N`, Python `iter_lines`, or a `fetch` `ReadableStream` reader. Buffering clients will hide the latency win.
- **Audio is base64 inside the event payload**, not the raw event bytes. Decode the `data.audio` field per event.
- **`output_format=pcm`** gives the lowest overhead for streaming playback. `wav`/`mp3` work but add per-chunk framing bytes.
- - **First-chunk latency** depends on model warm-up + network distance. Use `output_format=pcm` and a streaming-friendly client to minimize what you can control.
- - **JavaScript / TypeScript**: the official `smallestai` npm package predates Lightning v3.1, so call this endpoint with `fetch` as shown above.
Parameters
----------
@@ -4977,19 +2569,20 @@ async def synthesize_sse_lightning_v31(
The text to convert to speech.
voice_id : str
- The voice identifier to use for speech generation.
+ The voice identifier to use for speech generation. See the model card for available voices per model.
- model : typing.Optional[LightningV31RequestModel]
- TTS model to route the request to.
+ model : typing.Optional[TtsRequestModel]
+ TTS model to route the request to. Controls which model pool serves
+ this synthesis.
- - `lightning_v3.1` (default) โ standard Lightning v3.1 pool.
- - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool with a curated
- voice catalog. See the
- [Pro model card](/waves/model-cards/text-to-speech/lightning-v-3-1-pro).
+ - `lightning_v3.1` (default) โ standard Lightning v3.1.
+ - `lightning_v3.1_pro` โ Lightning v3.1 Pro pool. Improved audio
+ quality and naturalness, with a curated voice catalog. See the
+ [Lightning v3.1 Pro model card](/models/model-cards/text-to-speech/lightning-v-3-1-pro)
+ for supported voice IDs.
- New integrations should use the unified
- `/waves/v1/tts` route instead of this endpoint, but the `model`
- field is supported here for backwards-compatible Pro opt-in.
+ Same concurrency and latency profile across both. Other request
+ parameters behave identically.
sample_rate : typing.Optional[int]
The sample rate for the generated audio.
@@ -4997,16 +2590,62 @@ async def synthesize_sse_lightning_v31(
speed : typing.Optional[float]
The speed of the generated speech.
- language : typing.Optional[LightningV31RequestLanguage]
+ language : typing.Optional[TtsRequestLanguage]
Language code for synthesis. Influences pronunciation, number/date
normalization, and phoneme selection.
- - **Indian:** `en`, `hi`, `mr` (Marathi), `kn` (Kannada), `ta` (Tamil),
- `bn` (Bengali), `gu` (Gujarati), `te` (Telugu), `ml` (Malayalam),
- `pa` (Punjabi), `or` (Odia)
- - **European:** `es` (Spanish)
+ **Default on `lightning_v3.1_pro`:** when `language` is omitted, the
+ Pro pool defaults to **`en + hi`** (mixed Indian + Western English
+ coverage, auto-detected from the input text).
+
+ Each voice has its own `tags.language` set in the voice catalog โ
+ query `GET /waves/v1/lightning-v3.1/get_voices`. Pass a language
+ the voice was trained on; passing other codes is accepted by the
+ API but produces English-pronounced output.
+
+ **`auto` (recommended for cross-language use cases):** routes internally
+ based on the input text. Any English or Hindi voice can be used
+ across all supported languages when `auto` is set; the platform
+ handles language-appropriate routing without needing a code per
+ call.
+
+ **On `lightning_v3.1`** โ 20 supported languages:
+ - 10 European: English, Spanish, French, German, Italian, Dutch, Swedish, Portuguese, Polish, Russian
+ - 10 Indic: Hindi, Marathi, Gujarati, Punjabi, Bengali, Odia, Tamil, Telugu, Kannada, Malayalam
+
+ **On `lightning_v3.1_pro`** โ 31 supported languages (adds 11 over base):
+ - 13 European: base 10 plus Greek, Finnish, Norwegian
+ - 8 Asian & Middle Eastern: Chinese, Japanese, Korean, Indonesian, Malay, Vietnamese, Turkish, Arabic
+ - 10 Indic: same as base
+ - Pass `en` โ UK + American accented English.
+ - Pass `hi` โ Indian accented English + Hindi (code-switching).
+ - Omit `language` โ defaults to `en + hi` (mixed Indian + Western English coverage, auto-detected from input text).
+
+ number_pronunciation_language : typing.Optional[TtsRequestNumberPronunciationLanguage]
+ Optional. Sets the language used to read out numeric content โ
+ numbers, currency amounts, times, and the numeric parts of dates
+ and years โ independently of the synthesis voice. Ordinary words
+ are not translated.
+
+ - If you **omit `language`**, this value also becomes the
+ synthesis language: model selection and voice routing follow it.
+ - If you **set `language` explicitly**, `language` always wins for
+ synthesis and `number_pronunciation_language` only changes how
+ numeric content is normalized. It works both ways โ read numbers
+ in Hindi under an English voice, or in English under a Hindi
+ voice (tuned for Indian, often mixed-script, use cases).
+ - Omit this field to keep the existing behaviour โ normalization
+ follows `language`.
+
+ Note: only numeric tokens are re-spoken; the words around them
+ stay in the text language. On a cross-language request names may
+ also render in the target script (e.g. "Smith" โ "เคธเฅเคฎเคฟเคฅ"), which
+ is generally the desired reading for native-language voices.
+
+ Accepts the same language codes as `language` (including `auto`,
+ `nl`, `sv`).
- output_format : typing.Optional[LightningV31RequestOutputFormat]
+ output_format : typing.Optional[TtsRequestOutputFormat]
Format of the returned audio. `pcm` is the lowest-latency option
but requires a decoder to play; `mp3` and `wav` are directly
playable in browsers and most media players. The server default
@@ -5014,7 +2653,10 @@ async def synthesize_sse_lightning_v31(
`mp3` so the generated audio is directly playable.
pronunciation_dicts : typing.Optional[typing.Sequence[str]]
- The IDs of the pronunciation dictionaries to use for speech generation.
+ The IDs of the pronunciation dictionaries to use for speech generation. Available on both `lightning_v3.1` and `lightning_v3.1_pro`.
+
+ word_timestamps : typing.Optional[bool]
+ **WebSocket-only feature.** Accepted on this endpoint but ignored โ no per-word timing information is returned in the sync HTTP or SSE response shape. To receive `status: "word_timestamp"` frames with per-word `{ id, word, start, end }` data, use the WebSocket endpoint `wss://api.smallest.ai/waves/v1/tts/live`. See [Word-level timestamps](/models/documentation/text-to-speech-lightning/word-timestamps).
session_id : typing.Optional[str]
Optional client-provided session identifier for correlation. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Max 128 characters. Echoed back in response headers as `X-External-Session-Id`.
@@ -5027,11 +2669,11 @@ async def synthesize_sse_lightning_v31(
Yields
------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[typing.Any]]]
+ typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[str]]]
Synthesized speech retrieved successfully.
"""
async with self._client_wrapper.httpx_client.stream(
- "waves/v1/lightning-v3.1/stream",
+ "waves/v1/tts/live",
base_url=self._client_wrapper.get_environment().waves,
method="POST",
json={
@@ -5041,8 +2683,10 @@ async def synthesize_sse_lightning_v31(
"sample_rate": sample_rate,
"speed": speed,
"language": language,
+ "number_pronunciation_language": number_pronunciation_language,
"output_format": output_format,
"pronunciation_dicts": pronunciation_dicts,
+ "word_timestamps": word_timestamps,
"session_id": session_id,
"request_id": request_id,
},
@@ -5050,7 +2694,7 @@ async def synthesize_sse_lightning_v31(
omit=OMIT,
) as _response:
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[typing.Any]]:
+ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[str]]:
try:
if 200 <= _response.status_code < 300:
@@ -5061,10 +2705,10 @@ async def _iter():
return
try:
yield typing.cast(
- typing.Any,
+ str,
parse_sse_obj(
sse=_sse,
- type_=typing.Any, # type: ignore
+ type_=str, # type: ignore
),
)
except JSONDecodeError as e:
@@ -5236,8 +2880,9 @@ async def transcribe_pulse(
**Regional auto-detect aggregators** for unknown audio:
- `multi-eu` (default) โ auto-detects across all 21 European codes above plus `en`.
- `multi-asian` โ auto-detects across `zh`, `ko`, `ja`, `en`.
+ - `multi-indic`: auto-detects across `en`, `hi`, `gu`, `mr`, `bn`, `or`. India region only.
- Omitting `language` routes to `multi-eu`. See the [Pulse model card](/waves/model-cards/speech-to-text/pulse) for the full table.
+ Omitting `language` routes to `multi-eu`. See the [Pulse model card](/models/model-cards/speech-to-text/pulse) for the full table.
encoding : typing.Optional[TranscribePulseWavesRequestEncoding]
Audio encoding of the bytes you upload. Mirrors the `encoding`
diff --git a/tests/custom/test_versioning_helper.py b/tests/custom/test_versioning_helper.py
new file mode 100644
index 00000000..94cc3366
--- /dev/null
+++ b/tests/custom/test_versioning_helper.py
@@ -0,0 +1,74 @@
+"""Unit tests for the Versioning helper โ the 409 discriminator + facade wiring.
+
+No network: these exercise the exception-discrimination logic against synthetic
+ApiError bodies (dev's rich `data.conflict` shape, prod's lean `errors` shape, the
+migration flag, and the base-revision case). Live coverage is in
+tests/velocity/v2_versioning_e2e.py.
+"""
+from smallestai.atoms.helpers import (
+ Versioning,
+ VersioningError,
+ MigrationRequiredError,
+ DraftConflictError,
+ BaseRevisionUnavailableError,
+ SecurityCheckFailedError,
+)
+from smallestai.atoms.helpers.versioning import _discriminate_conflict
+from smallestai.core.api_error import ApiError
+
+
+def _conflict(body):
+ return _discriminate_conflict(ApiError(status_code=409, headers={}, body=body))
+
+
+def test_migration_required():
+ e = _conflict({"error_type": "versioning_v2_migration_required"})
+ assert isinstance(e, MigrationRequiredError)
+
+
+def test_draft_conflict_rich_dev_shape():
+ e = _conflict(
+ {"data": {"conflict": {"expectedRevision": 2, "latestRevision": 3,
+ "diffs": [{"section": "workflow_prompt"}]}}}
+ )
+ assert isinstance(e, DraftConflictError)
+ assert e.expected_revision == 2
+ assert e.latest_revision == 3
+ assert len(e.diffs) == 1
+
+
+def test_draft_conflict_lean_prod_shape():
+ e = _conflict({"status": False, "errors": ["workflow_prompt.globalPrompt"]})
+ assert isinstance(e, DraftConflictError)
+ assert e.fields == ["workflow_prompt.globalPrompt"]
+
+
+def test_base_revision_unavailable():
+ e = _conflict({"status": False, "errors": ["base_revision_unavailable"]})
+ assert isinstance(e, BaseRevisionUnavailableError)
+
+
+def test_all_versioning_errors_share_base():
+ for cls in (
+ MigrationRequiredError,
+ DraftConflictError,
+ BaseRevisionUnavailableError,
+ SecurityCheckFailedError,
+ ):
+ assert issubclass(cls, VersioningError)
+
+
+def test_facade_wires_generated_clients():
+ branches = object()
+ revisions = object()
+
+ class _Atoms:
+ agent_versioning_branches = branches
+ agent_versioning_revisions = revisions
+
+ class _Client:
+ atoms = _Atoms()
+
+ v = Versioning(_Client())
+ assert v.branches is branches
+ assert v.revisions is revisions
diff --git a/tests/velocity/v2_versioning_api_probe.py b/tests/velocity/v2_versioning_api_probe.py
new file mode 100644
index 00000000..c4f1eae7
--- /dev/null
+++ b/tests/velocity/v2_versioning_api_probe.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+"""Verbose live probe of the Agent Versioning v2 (branch/revision) REST API.
+
+Runs the full lifecycle against the LIVE API using plain `requests` (no SDK), on
+a throwaway forked branch that it archives afterward, and prints the real
+response detail at each step so you can eyeball the actual shapes.
+
+Only dependency: requests.
+
+ pip install requests
+ export SMALLEST_API_KEY=sk_... # required
+ export SMALLEST_BASE_URL=https://api.smallest.ai/atoms/v1 # prod (default)
+ # or https://api.dev.smallest.ai/atoms/v1 for dev
+ export E2E_AGENT_ID= # optional; omit to auto-create a throwaway agent
+ python tests/velocity/v2_versioning_api_probe.py
+"""
+import json
+import os
+import sys
+import time
+
+import requests
+
+BASE = os.environ.get("SMALLEST_BASE_URL", "https://api.smallest.ai/atoms/v1").rstrip("/")
+KEY = os.environ.get("SMALLEST_API_KEY")
+AGENT = os.environ.get("E2E_AGENT_ID")
+if not KEY:
+ print("SMALLEST_API_KEY is required")
+ sys.exit(2)
+H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
+
+_ok = 0
+_fail = 0
+_failed = []
+
+
+def call(method, path, **kw):
+ r = requests.request(method, f"{BASE}{path}", headers=H, timeout=45, **kw)
+ try:
+ body = r.json()
+ except Exception:
+ body = {"_raw": r.text[:400]}
+ return r.status_code, body, dict(r.headers)
+
+
+def check(cond, label, detail=""):
+ global _ok, _fail
+ print((" PASS " if cond else " FAIL ") + label + (f" {detail}" if detail else ""))
+ if cond:
+ _ok += 1
+ else:
+ _fail += 1
+ _failed.append(label)
+
+
+def show(title, obj, keys=None):
+ if isinstance(obj, dict) and keys:
+ obj = {k: obj.get(k) for k in keys}
+ print(f" {title}: {json.dumps(obj, default=str)[:600]}")
+
+
+def main():
+ print(f"\n=== Agent Versioning v2 API probe @ {BASE} ===")
+ created = False
+ agent_id = AGENT
+
+ # 0. Create a throwaway agent if none was given.
+ if not agent_id:
+ sc, b, _ = call("POST", "/agent", json={
+ "name": "v2-api-probe",
+ "globalPrompt": "You are a probe agent.",
+ "firstMessage": "Hi, probe here.",
+ "language": {"switching": {"isEnabled": False}},
+ })
+ agent_id = b.get("data")
+ created = True
+ check(sc in (200, 201) and agent_id, "POST /agent (create throwaway)", f"http={sc} id={agent_id}")
+
+ # 1. List branches -> find Main.
+ sc, b, _ = call("GET", f"/agent/{agent_id}/branches")
+ branches = b.get("data", {}).get("branches", [])
+ main = next((x for x in branches if x.get("branch", {}).get("isDefault")), branches[0] if branches else None)
+ check(sc == 200 and main, "GET /branches", f"http={sc} count={len(branches)}")
+ show("Main BranchSummary", main, ["isLive", "hasOpenDraft", "revisionsCount", "headRevisionNumber"])
+ show("Main .branch", main.get("branch", {}), ["_id", "name", "isDefault", "status", "headRevisionId"])
+ main_id = main["branch"]["_id"]
+
+ # 2. Fork a throwaway branch off Main.
+ sc, b, _ = call("POST", f"/agent/{agent_id}/branches", json={"sourceBranchId": main_id, "name": f"probe-{os.getpid()}"})
+ fork = b.get("data", {})
+ fork_id = fork.get("_id")
+ check(sc in (200, 201) and fork_id, "POST /branches (fork)", f"http={sc} fork={fork_id} status={fork.get('status')}")
+
+ try:
+ # 3. Edit the draft (camelCase config fields).
+ sc, b, _ = call("PUT", f"/agent/{agent_id}/branches/{fork_id}/draft",
+ json={"globalPrompt": "Probe prompt v1.", "firstMessage": "Hello from probe."})
+ check(sc == 200, "PUT /draft (globalPrompt + firstMessage)", f"http={sc}")
+
+ # 4. Get the draft detail.
+ sc, b, _ = call("GET", f"/agent/{agent_id}/branches/{fork_id}/draft")
+ d = b.get("data", {})
+ check(sc == 200, "GET /draft (DraftDetail)", f"http={sc} editCount={d.get('editCount')}")
+ show("draft.latest", d.get("latest", {}), ["_id", "draftRevision", "status", "workflowType"])
+
+ # 5. Publish -> poll -> published.
+ sc, b, _ = call("POST", f"/agent/{agent_id}/branches/{fork_id}/draft/publish", json={"label": "probe v1"})
+ state = b.get("data", {}).get("state")
+ check(sc in (200, 202), "POST /draft/publish", f"http={sc} state={state}")
+ rev_id, rev = None, {}
+ deadline = time.monotonic() + 90
+ while time.monotonic() < deadline:
+ sc, rl, _ = call("GET", f"/agent/{agent_id}/branches/{fork_id}/revisions", params={"limit": 1})
+ revs = rl.get("data", {}).get("revisions", [])
+ if revs:
+ rev_id = revs[0]["_id"]
+ sc, rb, _ = call("GET", f"/agent/{agent_id}/branches/{fork_id}/revisions/{rev_id}")
+ rev = rb.get("data", {}).get("revision", {})
+ if rev.get("status") == "published":
+ break
+ time.sleep(2)
+ check(rev.get("status") == "published", "publish -> revision published",
+ f"rev={rev_id} status={rev.get('status')} securityCheck={(rev.get('securityCheck') or {}).get('status')}")
+ show("Revision", rev, ["_id", "revisionNumber", "status", "label", "publishedByName", "promptScoreStale"])
+
+ # 6. Test call (webcall).
+ sc, b, _ = call("POST", f"/agent/{agent_id}/branches/{fork_id}/test-call", json={"mode": "webcall"})
+ check(sc == 200, "POST /test-call (webcall)", f"http={sc}")
+ show("TestCallResult", b.get("data", {}), ["conversationId", "callId", "roomName", "host"])
+
+ # 7. Second edit + publish so we have two revisions (also gives restore an older target).
+ call("PUT", f"/agent/{agent_id}/branches/{fork_id}/draft", json={"globalPrompt": "Probe prompt v2."})
+ sc, b, _ = call("POST", f"/agent/{agent_id}/branches/{fork_id}/draft/publish", json={"label": "probe v2"})
+ check(sc in (200, 202), "POST /draft/publish (2nd)", f"http={sc} state={b.get('data',{}).get('state')}")
+ rev2_id = None
+ deadline = time.monotonic() + 90
+ while time.monotonic() < deadline:
+ sc, rl, _ = call("GET", f"/agent/{agent_id}/branches/{fork_id}/revisions", params={"limit": 2})
+ revs = rl.get("data", {}).get("revisions", [])
+ newest = revs[0] if revs else {}
+ if newest.get("_id") and newest["_id"] != rev_id and newest.get("status") == "published":
+ rev2_id = newest["_id"]
+ break
+ time.sleep(2)
+ check(rev2_id is not None, "2nd revision committed", f"rev2={rev2_id}")
+
+ # 8. Diff the two committed revisions (a and b are 24-hex revision ids).
+ sc, b, _ = call("GET", f"/agent/{agent_id}/diff", params={"a": rev_id, "b": rev2_id})
+ d = b.get("data", {})
+ check(sc == 200, "GET /diff (rev1 vs rev2)", f"http={sc}")
+ show("diff", {"unchangedSections": d.get("unchangedSections"),
+ "diffs": [x.get("section") for x in d.get("diffs", [])]})
+
+ # 9. Optimistic-concurrency conflict (stale expectedRevision).
+ call("PUT", f"/agent/{agent_id}/branches/{fork_id}/draft", json={"globalPrompt": "edit A"})
+ sc, gd, _ = call("GET", f"/agent/{agent_id}/branches/{fork_id}/draft")
+ stale = gd.get("data", {}).get("latest", {}).get("draftRevision")
+ call("PUT", f"/agent/{agent_id}/branches/{fork_id}/draft", json={"expectedRevision": stale, "globalPrompt": "edit B"})
+ sc, b, _ = call("PUT", f"/agent/{agent_id}/branches/{fork_id}/draft", json={"expectedRevision": stale, "globalPrompt": "edit C"})
+ check(sc == 409, "PUT /draft stale expectedRevision -> 409", f"http={sc}")
+ show("conflict body", b)
+
+ # 10. Restore the FIRST revision (older than head) -> new head revision.
+ sc, b, _ = call("POST", f"/agent/{agent_id}/branches/{fork_id}/revisions/{rev_id}/restore")
+ check(sc == 200, "POST /revisions/{id}/restore (older revision)", f"http={sc} state={b.get('data',{}).get('state')}")
+
+ # 11. Make the fork live, then revert Main.
+ sc, b, _ = call("POST", f"/agent/{agent_id}/branches/{fork_id}/live")
+ check(sc == 200, "POST /branches/{fork}/live", f"http={sc} isLive={b.get('data',{}).get('isLive')}")
+ sc, _, _ = call("POST", f"/agent/{agent_id}/branches/{main_id}/live")
+ check(sc == 200, "POST /branches/{main}/live (revert)", f"http={sc}")
+
+ # 12. v1 endpoint is deprecated under the branch model -> 409 migration.
+ sc, b, hdr = call("POST", f"/agent/{agent_id}/drafts", json={})
+ check(sc == 409 and b.get("error_type") == "versioning_v2_migration_required",
+ "v1 POST /drafts -> 409 migration", f"http={sc} error_type={b.get('error_type')} Deprecation={hdr.get('Deprecation')}")
+ finally:
+ sc, b, _ = call("POST", f"/agent/{agent_id}/branches/{fork_id}/archive")
+ print(f"\ncleanup: archived fork {fork_id} -> http={sc} {b.get('data', b)}")
+ if created:
+ print(f"note: created throwaway agent {agent_id} (no delete endpoint; remove from dashboard if you want).")
+
+ print(f"\n==== {_ok} PASS / {_fail} FAIL ====")
+ if _failed:
+ print("failed:", _failed)
+ return 1 if _fail else 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/tests/velocity/v2_versioning_e2e.py b/tests/velocity/v2_versioning_e2e.py
new file mode 100644
index 00000000..e7e5f5e0
--- /dev/null
+++ b/tests/velocity/v2_versioning_e2e.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python3
+"""Live E2E for the Agent Versioning v2 (branch/revision) SDK surface.
+
+Runs the full branch/revision lifecycle THROUGH the generated typed client and
+the `Versioning` helper, against a live backend, on a throwaway forked branch
+that it archives afterward. This is the script we run against every regen that
+touches the versioning surface, before shipping.
+
+Requirements:
+ SMALLEST_API_KEY - bearer key for the target environment
+ SMALLEST_BASE_URL - optional atoms base, e.g. https://api.dev.smallest.ai/atoms/v1
+ (omit to hit Production)
+ E2E_AGENT_ID - optional agent to fork from (default: a dev test agent)
+
+Usage:
+ SMALLEST_API_KEY=sk_... SMALLEST_BASE_URL=https://api.dev.smallest.ai/atoms/v1 \
+ python tests/velocity/v2_versioning_e2e.py
+"""
+import os
+import sys
+
+from smallestai import SmallestAI
+from smallestai.environment import SmallestAIEnvironment
+from smallestai.atoms.helpers.versioning import (
+ Versioning,
+ DraftConflictError,
+ MigrationRequiredError,
+)
+
+KEY = os.environ.get("SMALLEST_API_KEY")
+BASE = os.environ.get("SMALLEST_BASE_URL")
+AGENT = os.environ.get("E2E_AGENT_ID", "6a61ec314138fd26c7acd84f")
+
+if not KEY:
+ print("SMALLEST_API_KEY is required")
+ sys.exit(2)
+
+
+def make_client() -> SmallestAI:
+ if BASE:
+ host = BASE.split("/atoms")[0]
+ ws = host.replace("https://", "wss://").replace("http://", "ws://")
+ env = SmallestAIEnvironment(atoms=BASE, waves=host, waves_ws=ws)
+ return SmallestAI(environment=env, api_key=KEY)
+ return SmallestAI(api_key=KEY)
+
+
+_ok = 0
+_fail = 0
+_failed = []
+
+
+def check(cond: bool, label: str, detail: str = "") -> None:
+ global _ok, _fail
+ print((" PASS " if cond else " FAIL ") + label + (f" {detail}" if detail else ""))
+ if cond:
+ _ok += 1
+ else:
+ _fail += 1
+ _failed.append(label)
+
+
+def main() -> int:
+ client = make_client()
+ v = Versioning(client)
+ fork_id = None
+ try:
+ print(f"\n== agent {AGENT} @ {BASE or 'PRODUCTION'} ==")
+
+ # list + find main
+ listed = v.branches.list(id=AGENT)
+ branches = listed.data.branches
+ main = next(b for b in branches if b.branch.is_default)
+ main_id = main.branch.id
+ check(bool(branches) and main_id, "branches.list -> main", f"main={main_id} isLive={main.is_live}")
+
+ # fork
+ fk = v.branches.create_branch(id=AGENT, source_branch_id=main_id, name=f"e2e-{os.getpid()}")
+ fork_id = fk.data.id
+ check(fork_id is not None and fk.data.status == "active", "create_branch (fork)", f"id={fork_id}")
+
+ # typed update_draft via helper
+ ud = v.update_draft(AGENT, fork_id, global_prompt="E2E typed prompt", first_message="Hi from e2e")
+ check(ud is not None, "update_draft (typed kwargs)", f"draftRevision={getattr(ud.data,'draft_revision',None)}")
+
+ # get_draft
+ gd = v.branches.get_draft(id=AGENT, branch_id=fork_id)
+ check(gd.data.latest is not None, "get_draft (DraftDetail)", f"editCount={gd.data.edit_count}")
+ cur_rev = gd.data.latest.draft_revision
+
+ # publish + wait_for_commit (helper handles scanning->poll)
+ revision = v.publish_and_wait(AGENT, fork_id, label="e2e", timeout=90, poll_interval=2)
+ check(getattr(revision, "status", None) == "published", "publish_and_wait -> published",
+ f"rev={getattr(revision,'id',None)} securityCheck={getattr(getattr(revision,'security_check',None),'status',None)}")
+
+ # revisions list / get / history
+ rl = v.revisions.list(id=AGENT, branch_id=fork_id)
+ check(rl.data.revisions is not None, "revisions.list", f"count={len(rl.data.revisions or [])}")
+ rid = revision.id
+ rg = v.revisions.get(id=AGENT, branch_id=fork_id, revision_id=rid)
+ check(rg.data is not None, "revisions.get", "")
+ rh = v.revisions.get_history(id=AGENT, branch_id=fork_id, revision_id=rid)
+ check(rh.data is not None, "revisions.get_history", "")
+
+ # test_call
+ tc = v.branches.test_call(id=AGENT, branch_id=fork_id, mode="webcall")
+ check(tc.data is not None, "test_call (webcall)", "")
+
+ # DraftConflict discriminator: open a fresh edit then send a stale expected_revision
+ v.update_draft(AGENT, fork_id, global_prompt="edit A")
+ gd2 = v.branches.get_draft(id=AGENT, branch_id=fork_id)
+ latest_rev = gd2.data.latest.draft_revision
+ v.update_draft(AGENT, fork_id, expected_revision=latest_rev, global_prompt="edit B") # bumps
+ conflict_raised = False
+ try:
+ v.update_draft(AGENT, fork_id, expected_revision=latest_rev, global_prompt="edit C stale")
+ except DraftConflictError as e:
+ conflict_raised = True
+ # dev returns the rich data.conflict (diffs); prod returns lean errors (fields).
+ has_detail = bool(e.diffs) or bool(getattr(e, "fields", []))
+ check(has_detail, "DraftConflictError discriminated",
+ f"expected={e.expected_revision} latest={e.latest_revision} diffs={len(e.diffs)} fields={getattr(e,'fields',[])}")
+ check(conflict_raised, "stale expected_revision raises DraftConflictError")
+
+ # publish the pending draft edits as a 2nd revision, so restore has an
+ # OLDER target than head (restoring the current head is a 409 no-op).
+ rev2 = v.publish_and_wait(AGENT, fork_id, label="e2e-2", timeout=90, poll_interval=2)
+ check(getattr(rev2, "status", None) == "published", "second publish -> published",
+ f"rev={getattr(rev2,'id',None)}")
+
+ # restore the FIRST revision (older than head) -> new head revision
+ rr = v.revisions.restore(id=AGENT, branch_id=fork_id, revision_id=rid)
+ check(rr.data is not None, "revisions.restore (older revision)", f"state={getattr(rr.data,'state',None)}")
+ ml = v.branches.make_live(id=AGENT, branch_id=fork_id)
+ check(ml.data is not None, "make_live (fork)", "")
+ v.branches.make_live(id=AGENT, branch_id=main_id) # revert
+ check(True, "make_live (revert to main)")
+
+ print(f"\n==== {_ok} PASS / {_fail} FAIL ====")
+ if _failed:
+ print("failed:", _failed)
+ return 1 if _fail else 0
+ finally:
+ if fork_id:
+ try:
+ arch = v.branches.archive(id=AGENT, branch_id=fork_id)
+ print(f"cleanup: archived fork {fork_id} -> {arch.data}")
+ except Exception as e:
+ print(f"cleanup: archive failed for {fork_id}: {e}")
+
+
+if __name__ == "__main__":
+ sys.exit(main())