Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "gradient-labs"
version = "0.12.1"
version = "0.13.0"
description = "Python bindings for the Gradient Labs API"
readme = "README.md"
requires-python = ">=3.9,<4.0"
Expand Down
5 changes: 4 additions & 1 deletion src/gradient_labs/_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
from .errors import ResponseError

API_BASE_URL = "https://api.gradient-labs.ai"
USER_AGENT = "Gradient Labs Python"

# Keep VERSION in step with `version` in pyproject.toml.
VERSION = "0.13.0"
USER_AGENT = f"Gradient Labs Python/{VERSION}"


class HttpClient:
Expand Down
284 changes: 204 additions & 80 deletions src/gradient_labs/_outbound_conversation_start.py
Original file line number Diff line number Diff line change
@@ -1,95 +1,145 @@
from typing import Optional, Dict, Any
from typing import Optional, Dict, Any, List
from enum import Enum

from dataclasses import dataclass
from dataclasses_json import dataclass_json

from ._http_client import HttpClient
from ._conversation_start import CustomerSupportPlatformIdentifier


class CustomerSource(str, Enum):
"""Identifies where customer data originates from."""
class OutboundSupportPlatform(str, Enum):
"""Identifies the support platform an outbound chat or email is delivered on."""

INTERCOM = "intercom"
FRESHCHAT = "freshchat"
FRESHDESK = "freshdesk"
PUBLIC_API = "public-api"
SALESFORCE = "salesforce"
ZENDESK = "zendesk"
VOICE = "livekit"
VOICE_TWILIO = "twilio"
VOICE_TALKDESK = "talkdesk"
VOICE_INTERCOM = "intercom-voice"
WEB_APP = "web-app"
FILE = "file"
INTERCOM: str = "intercom"
ZENDESK: str = "zendesk"
SALESFORCE: str = "salesforce"

# PUBLIC_API delivers the conversation to your own webhook endpoint.
PUBLIC_API: str = "public-api"

class SupportPlatform(str, Enum):
"""Identifies the support platform where the conversation will be created."""

FRESHCHAT = "freshchat"
FRESHDESK = "freshdesk"
INTERCOM = "intercom"
PUBLIC_API = "public-api"
SALESFORCE = "salesforce"
ZENDESK = "zendesk"
VOICE = "livekit"
VOICE_TWILIO = "twilio"
VOICE_TALKDESK = "talkdesk"
VOICE_INTERCOM = "intercom-voice"
WEB_APP = "web-app"
@dataclass_json
@dataclass(frozen=True)
class StartOutboundChatConversationParams:
"""Parameters for starting an outbound live chat conversation."""

# customer_id is your own identifier for the customer, as used in your systems.
# It is stored as the customer's company customer ID, and is the identifier echoed
# back to you in tool and webhook payloads.
customer_id: str

# procedure_id is the ID of the outbound procedure that defines what the AI agent
# should accomplish in this conversation. The procedure must be of type "outbound",
# must be live (deployed), and must be enabled for the chat channel.
procedure_id: str

# support_platform is the support platform the chat is delivered on.
# Valid values: "intercom", "public-api".
support_platform: OutboundSupportPlatform

# customer_support_platform_identifiers optionally links the customer to their
# record(s) in third-party support platforms (e.g. Intercom), alongside customer_id.
#
# The platform named in support_platform needs an identifier here, unless the
# customer already carries one from an earlier conversation.
customer_support_platform_identifiers: Optional[
List[CustomerSupportPlatformIdentifier]
] = None

# body is the content of the initial message to send to the customer.
# If omitted, the AI agent will generate an appropriate opening message based on
# the procedure.
body: Optional[str] = None

# resources is a JSON object containing structured data that the AI agent
# can use during the conversation. This should be organized as a dict
# where keys are resource type names and values are the corresponding data.
# Example: {"customer_profile": {"tier": "premium", "lifetime_value": 5000}}
resources: Optional[Dict[str, Any]] = None


@dataclass_json
@dataclass(frozen=True)
class StartOutboundConversationParams:
"""Parameters for starting a new outbound conversation.
class StartOutboundEmailConversationParams:
"""Parameters for starting an outbound email conversation."""

This kicks off a proactive conversation where your AI agent initiates
contact with a customer.
"""

# customer_id is the external identifier for the customer in your support platform.
# For Intercom, this is the external ID you've defined for the user (e.g., "user-123456").
# For other platforms, this is the customer identifier used by that platform.
# customer_id is your own identifier for the customer, as used in your systems.
# It is stored as the customer's company customer ID, and is the identifier echoed
# back to you in tool and webhook payloads.
customer_id: str

# customer_source is the source of the customer data.
# For example, a customer ID and phone number might be from Intercom, but the outbound
# conversation is initiated via Twilio.
customer_source: CustomerSource

# procedure_id is the ID of the outbound procedure that defines what the AI agent
# should accomplish in this conversation. The procedure must be of type "outbound"
# and must be live (deployed).
# should accomplish in this conversation. The procedure must be of type "outbound",
# must be live (deployed), and must be enabled for the email channel.
procedure_id: str

# support_platform is the support platform where the conversation should be created.
# Valid values include "intercom", "zendesk", "freshdesk", "freshchat".
# If not provided, the system will automatically select the first connected platform
# in priority order: intercom, zendesk, freshchat, freshdesk, public-api.
support_platform: Optional[SupportPlatform] = None
# support_platform is the support platform the email is sent from.
# Valid values: "intercom", "zendesk", "salesforce", "public-api".
support_platform: OutboundSupportPlatform

# channel specifies the communication channel for this conversation.
# If not provided, defaults to "email".
# Valid values: "email", "web", "sms", "voice", etc.
channel: Optional[str] = None
# customer_support_platform_identifiers optionally links the customer to their
# record(s) in third-party support platforms (e.g. Intercom, Zendesk, Salesforce),
# alongside customer_id.
#
# The platform named in support_platform needs an identifier here, unless the
# customer already carries one from an earlier conversation. Zendesk requires type
# "zendesk_support_user"; Salesforce requires type "salesforce_contact_id".
customer_support_platform_identifiers: Optional[
List[CustomerSupportPlatformIdentifier]
] = None

# subject is the subject line for the initial message (primarily used for email channels).
# Only used if body is also provided. If both subject and body are omitted, the AI agent
# will generate the initial message.
# subject is the subject line for the initial email. Required if body is provided,
# and forbidden otherwise. If both are omitted, the AI agent will write the opening
# email.
subject: Optional[str] = None

# body is the content of the initial message to send to the customer.
# If provided, this message will be sent instead of having the AI agent generate one.
# If omitted, the AI agent will generate an appropriate initial message based on the procedure.
# body is the content of the initial email to send to the customer. Required if
# subject is provided, and forbidden otherwise.
body: Optional[str] = None

# resources is a JSON object containing structured data that the AI agent
# can use during the conversation. This should be organized as a dict
# where keys are resource type names and values are the corresponding data.
# Example: {"customer_profile": {"tier": "premium", "lifetime_value": 5000}}
# The data will be made available to the AI agent for context during conversation processing.
resources: Optional[Dict[str, Any]] = None


@dataclass_json
@dataclass(frozen=True)
class StartOutboundPhoneConversationParams:
"""Parameters for placing an outbound phone call."""

# customer_id is your own identifier for the customer, as used in your systems.
# It is stored as the customer's company customer ID, and is the identifier echoed
# back to you in tool and webhook payloads.
customer_id: str

# procedure_id is the ID of the outbound procedure that defines what the AI agent
# should accomplish on the call. The procedure must be of type "outbound", must be
# live (deployed), and must be enabled for the "voice" channel.
procedure_id: str

# to_phone_number is the customer's phone number to dial (E.164 format,
# e.g. "+14155551234").
to_phone_number: str

# from_phone_number is the caller ID to place the call from (E.164 format).
# It must be a phone number already provisioned for your company.
from_phone_number: str

# customer_support_platform_identifiers optionally links the customer to their
# record(s) in third-party support platforms (e.g. Intercom, Zendesk, Salesforce),
# alongside customer_id. They are also used to pull that platform's customer data
# into the call as context.
customer_support_platform_identifiers: Optional[
List[CustomerSupportPlatformIdentifier]
] = None

# resources is a JSON object containing structured data that the AI agent
# can use during the conversation. This should be organized as a dict
# where keys are resource type names and values are the corresponding data.
# Example: {"customer_profile": {"tier": "premium", "lifetime_value": 5000}}
resources: Optional[Dict[str, Any]] = None


Expand All @@ -103,36 +153,77 @@ class StartOutboundConversationResponse:
conversation_id: str


def start_outbound_conversation(
*, client: HttpClient, params: StartOutboundConversationParams
def _support_platform_value(platform: OutboundSupportPlatform) -> str:
return platform.value if isinstance(platform, OutboundSupportPlatform) else platform


def _identifiers(
identifiers: List[CustomerSupportPlatformIdentifier],
) -> List[Dict[str, Any]]:
return [i.to_dict() for i in identifiers]


def start_outbound_chat_conversation(
*, client: HttpClient, params: StartOutboundChatConversationParams
) -> StartOutboundConversationResponse:
"""Creates and starts a new outbound conversation where the AI agent
proactively initiates contact with a customer.
"""Creates and starts a new outbound live chat conversation in which the AI agent
proactively initiates contact with a customer, following the instructions defined
in the specified outbound procedure.

The conversation follows the instructions defined in the specified outbound procedure.
If body is provided, that message will be sent as the opening message. Otherwise,
the AI agent will generate one based on the procedure.

If support_platform is not provided, the system will automatically select the highest
priority platform that has integration settings configured for your company.
The customer is created, or matched to an existing record, from customer_id and any
customer_support_platform_identifiers you supply. The platform the chat is delivered
on needs an identifier for that customer.
"""
body = {
"customer_id": params.customer_id,
"procedure_id": params.procedure_id,
"support_platform": _support_platform_value(params.support_platform),
}

if params.customer_support_platform_identifiers is not None:
body["customer_support_platform_identifiers"] = _identifiers(
params.customer_support_platform_identifiers
)
if params.body is not None:
body["body"] = params.body
if params.resources is not None:
body["resources"] = params.resources

rsp = client.post(
path="outbound/conversations/chat",
body=body,
)
return StartOutboundConversationResponse.from_dict(rsp)


def start_outbound_email_conversation(
*, client: HttpClient, params: StartOutboundEmailConversationParams
) -> StartOutboundConversationResponse:
"""Creates and starts a new outbound email conversation in which the AI agent
proactively initiates contact with a customer, following the instructions defined
in the specified outbound procedure.

If body and subject are provided, that message will be sent as the initial message.
Otherwise, the AI agent will generate an appropriate initial message based on the procedure.
If body and subject are provided, that email will be sent as the opening message.
Otherwise, the AI agent will write one based on the procedure.

The customer is created, or matched to an existing record, from customer_id and any
customer_support_platform_identifiers you supply. The platform the email is sent
from needs an identifier for that customer, so sending from Zendesk needs a Zendesk
identifier, and so on.
"""
body = {
"customer_id": params.customer_id,
"customer_source": params.customer_source.value
if isinstance(params.customer_source, CustomerSource)
else params.customer_source,
"procedure_id": params.procedure_id,
"support_platform": _support_platform_value(params.support_platform),
}

if params.support_platform is not None:
body["support_platform"] = (
params.support_platform.value
if isinstance(params.support_platform, SupportPlatform)
else params.support_platform
if params.customer_support_platform_identifiers is not None:
body["customer_support_platform_identifiers"] = _identifiers(
params.customer_support_platform_identifiers
)
if params.channel is not None:
body["channel"] = params.channel
if params.subject is not None:
body["subject"] = params.subject
if params.body is not None:
Expand All @@ -141,7 +232,40 @@ def start_outbound_conversation(
body["resources"] = params.resources

rsp = client.post(
path="outbound/conversations",
path="outbound/conversations/email",
body=body,
)
return StartOutboundConversationResponse.from_dict(rsp)


def start_outbound_phone_conversation(
*, client: HttpClient, params: StartOutboundPhoneConversationParams
) -> StartOutboundConversationResponse:
"""Places an outbound phone call in which the AI agent proactively contacts a
customer, following the instructions defined in the specified outbound procedure.

from_phone_number must be a phone number already provisioned for your company.

The customer is created, or matched to an existing record, from customer_id and any
customer_support_platform_identifiers you supply. The dialled number is recorded
against that same customer.
"""
body = {
"customer_id": params.customer_id,
"procedure_id": params.procedure_id,
"to_phone_number": params.to_phone_number,
"from_phone_number": params.from_phone_number,
}

if params.customer_support_platform_identifiers is not None:
body["customer_support_platform_identifiers"] = _identifiers(
params.customer_support_platform_identifiers
)
if params.resources is not None:
body["resources"] = params.resources

rsp = client.post(
path="outbound/conversations/phone",
body=body,
)
return StartOutboundConversationResponse.from_dict(rsp)
Loading
Loading