This library facilitates the interaction between a user and a CAI server. It achieves this by providing a higher-level interface mediator.
This higher-level interface mediator is structured around a series of python files generated from protobuf files. These protobuf files specify the details of the interface, and can be used to generate code in 10+ high-level languages. They are found in the ONDEWO VTSI API along with the older Google protobufs from Dialogueflow that were used at the start. The ONDEWO PROTO-COMPILER will generate the needed files directly in this library.
Python Installation
You can install the library by installing it directly from the PyPi:
pip install ondewo-vtsi-client
Or, you could clone it and install the requirements:
git clone git@github.com:ondewo/ondewo-vtsi-client-python.git
cd ondewo-vtsi-client-python
make setup_developer_environment_locally
Repository Structure
.
├── examples
│ ├── analysis
│ │ └── analysis.py
│ ├── caller
│ │ ├── caller_deployment_minimal.py
│ │ ├── caller_deployment_mirror_mode.py
│ │ ├── configure_context_parameters.py
│ │ ├── full_config.py
│ │ └── make_multiple_calls.py
│ └── listener
│ ├── listener_deployment.py
│ └── minimal_listener.py
├── ondewo
│ ├── nlu
│ │ ├── agent_pb2_grpc.py
│ │ ├── agent_pb2.py
│ │ ├── agent_pb2.pyi
│ │ ├── aiservices_pb2_grpc.py
│ │ ├── aiservices_pb2.py
│ │ ├── aiservices_pb2.pyi
│ │ ├── ccai_project_pb2_grpc.py
│ │ ├── ccai_project_pb2.py
│ │ ├── ccai_project_pb2.pyi
│ │ ├── common_pb2_grpc.py
│ │ ├── common_pb2.py
│ │ ├── common_pb2.pyi
│ │ ├── context_pb2_grpc.py
│ │ ├── context_pb2.py
│ │ ├── context_pb2.pyi
│ │ ├── entity_type_pb2_grpc.py
│ │ ├── entity_type_pb2.py
│ │ ├── entity_type_pb2.pyi
│ │ ├── __init__.py
│ │ ├── intent_pb2_grpc.py
│ │ ├── intent_pb2.py
│ │ ├── intent_pb2.pyi
│ │ ├── operation_metadata_pb2_grpc.py
│ │ ├── operation_metadata_pb2.py
│ │ ├── operation_metadata_pb2.pyi
│ │ ├── operations_pb2_grpc.py
│ │ ├── operations_pb2.py
│ │ ├── operations_pb2.pyi
│ │ ├── project_role_pb2_grpc.py
│ │ ├── project_role_pb2.py
│ │ ├── project_role_pb2.pyi
│ │ ├── project_statistics_pb2_grpc.py
│ │ ├── project_statistics_pb2.py
│ │ ├── project_statistics_pb2.pyi
│ │ ├── server_statistics_pb2_grpc.py
│ │ ├── server_statistics_pb2.py
│ │ ├── server_statistics_pb2.pyi
│ │ ├── session_pb2_grpc.py
│ │ ├── session_pb2.py
│ │ ├── session_pb2.pyi
│ │ ├── user_pb2_grpc.py
│ │ ├── user_pb2.py
│ │ ├── user_pb2.pyi
│ │ ├── utility_pb2_grpc.py
│ │ ├── utility_pb2.py
│ │ ├── utility_pb2.pyi
│ │ ├── webhook_pb2_grpc.py
│ │ ├── webhook_pb2.py
│ │ └── webhook_pb2.pyi
│ ├── qa
│ │ ├── __init__.py
│ │ ├── qa_pb2_grpc.py
│ │ ├── qa_pb2.py
│ │ └── qa_pb2.pyi
│ ├── s2t
│ │ ├── __init__.py
│ │ ├── speech_to_text_pb2_grpc.py
│ │ ├── speech_to_text_pb2.py
│ │ └── speech_to_text_pb2.pyi
│ ├── sip
│ │ ├── __init__.py
│ │ ├── sip_pb2_grpc.py
│ │ ├── sip_pb2.py
│ │ └── sip_pb2.pyi
│ ├── t2s
│ │ ├── __init__.py
│ │ ├── text_to_speech_pb2_grpc.py
│ │ ├── text_to_speech_pb2.py
│ │ └── text_to_speech_pb2.pyi
│ ├── vtsi
│ │ ├── client.py
│ │ ├── __init__.py
│ │ ├── vtsi_pb2_grpc.py
│ │ ├── vtsi_pb2.py
│ │ └── vtsi_pb2.pyi
│ └── __init__.py
├── ondewo-proto-compiler
├── ondewo-vtsi-api
├── CONTRIBUTING.md
├── Dockerfile.utils
├── LICENSE
├── Makefile
├── MANIFEST.in
├── mypy.ini
├── README.md
├── RELEASE.md
├── requirements-dev.txt
├── requirements.txt
├── setup.cfg
├── setup.py
└── temp.txt
Build
The make build command is dependent on 2 repositories and their speciefied version:
- ondewo-vtsi-api --
VTSI_API_GIT_BRANCHinMakefile - ondewo-proto-compiler --
ONDEWO_PROTO_COMPILER_GIT_BRANCHinMakefile
It will generate a _pb2.py, _pb2.pyi and _pb2_grpc.py file for every .proto in the api submodule.
⚠️ All Files in the
ondewofolder that dont havepb2in their name are handwritten, and therefor need to be manually adjusted to any changes in the proto-code.
Examples
The /examples folder provides a possible implementation of this library. To run an example, simple execute it like any other python file. To specify the server and credentials, you need to provide the following values:
- host
// The hostname of the Server - e.g. 127.0.0.1 - port
// Port of the Server - e.g. 6600 - grpc_cert
// gRPC Certificate of the server (required for a secure channel) - keycloak_url
// Base URL of the Keycloak server (D18 auth) - e.g. https://keycloak.example.com/auth - realm
// Keycloak realm name (D18 auth) - client_id
// Id of the public Keycloak SDK client (D18 auth) - no client secret - username
// Username / email of the (2FA-exempt technical) user for the ROPC grant (D18 auth) - password
// Password of that user
The client authenticates with the D18 Keycloak offline-token (bearer) flow: it logs in once against a
public Keycloak client with grant_type=password + scope=offline_access, then auto-refreshes the
short-lived access token. The legacy http_token (Authorization: Basic) is removed (D5) and is no longer
required. The service wrappers under ondewo/vtsi/client/services/ sit on top of the generated
_pb2_grpc.py stubs and inject the freshly refreshed token via self.metadata on every call, so application
code stays clean. Without a wrapper the same call would attach the token manually:
metadata = [("authorization", "Bearer <token>")]
stub.StartCaller(request, metadata=metadata)
Softphone accounts
client.services.softphones manages SIP accounts on a project's Asterisk for humans using a softphone such
as Zoiper. The create (and rotate) response is the only place the SIP password and the password-protected
PKCS#12 bundle ever appear, so store them at once and never log the response; Get, List and provisioning
return public material only. See examples/softphones/create_softphone_account.py for the full flow.
from ondewo.vtsi.softphones_pb2 import (
SOFTPHONE_TRANSPORT_SECURITY_CLIENT_CERTIFICATE,
CreateSoftphoneAccountRequest,
GetSoftphoneProvisioningRequest,
SoftphoneAccount,
)
created = client.services.softphones.create_softphone_account(
request=CreateSoftphoneAccountRequest(
vtsi_project_name="projects/<project_uuid>/project",
softphone_account=SoftphoneAccount(
sip_username="support-01",
transport_security=SOFTPHONE_TRANSPORT_SECURITY_CLIENT_CERTIFICATE,
),
),
)
# created.credentials.sip_password / .pkcs12_bundle / .pkcs12_password are shown ONCE.
provisioning = client.services.softphones.get_softphone_provisioning(
request=GetSoftphoneProvisioningRequest(name=created.softphone_account.name),
)
print(provisioning.zoiper_instructions)
Campaigns, status streams and VtsiEvents
client.services.campaigns places a set of outbound calls while keeping at most max_parallel_calls of them
running at once, retries a failed call up to max_attempts times, and reports the progress (not started, in
progress, completed, failed, attempts). Calls join a campaign through
client.services.calls.add_callers_to_campaign / add_scheduled_callers_to_campaign. A VTSI server older than
these RPCs answers UNIMPLEMENTED and starts nothing; do not fall back to start_callers then, because that starts
every caller at once. client.services.events manages VtsiEvent subscriptions and webhooks (custom header values
are write-only and come back masked; moving a webhook to another origin requires re-sending its headers with real
values) and streams the events themselves.
client.services.calls streams the status of callers, listeners and scheduled callers.
from ondewo.vtsi.calls_pb2 import AddCallersToCampaignRequest
from ondewo.vtsi.campaigns_pb2 import (
CAMPAIGN_START_MODE_START,
Campaign,
CampaignAssignment,
StreamCampaignStatusRequest,
)
response = client.services.calls.add_callers_to_campaign(
request=AddCallersToCampaignRequest(
vtsi_project_name="projects/<project_uuid>/project",
caller_requests=[...], # e.g. 100 StartCallerRequest
campaign_assignment=CampaignAssignment(
new_campaign=Campaign(max_parallel_calls=10, max_attempts=2),
start_mode=CAMPAIGN_START_MODE_START,
),
),
)
for update in client.services.campaigns.stream_campaign_status(
request=StreamCampaignStatusRequest(
vtsi_project_name="projects/<project_uuid>/project",
campaign_names=[response.campaign.name],
),
):
print(update)
With the AsyncClient a stream is iterated, not awaited: async for update in client.services.campaigns.stream_campaign_status(request=...).
Retrying a batch start safely: idempotency_key
StartCallersRequest, StartListenersRequest, StartScheduledCallersRequest, AddCallersToCampaignRequest and
AddScheduledCallersToCampaignRequest take an optional idempotency_key (at most 255 printable ASCII characters,
no whitespace; empty means no deduplication). A retry carrying the same key returns the response of the first
successful attempt, on whichever replica it reaches, instead of starting the calls a second time. The key is scoped
to the VTSI project and the RPC and kept for 24 hours by default. Reusing a key with a different request is refused
with INVALID_ARGUMENT; a retry while the first attempt is still running is answered ABORTED (retry later); a
failed first attempt stores nothing. A replayed response carries no common_services_config. To make a single
caller or listener idempotent, send it as a batch of one.
One shared gRPC channel (opt-in)
By default every service (projects, calls, logs) opens its own gRPC channel, i.e. over TLS its own connection
and handshake. Pass use_shared_channel=True to let all services share one channel, built with
build_shared_channel (each method keeps the retry policy it
would have had on its own channel); disconnect() closes it once, and a later connect() shares again:
from ondewo.vtsi.client.client import Client
client = Client(config=config, use_secure_channel=True, use_shared_channel=True)
AsyncClient takes the same keyword; construct it inside the running event loop that uses it.
Automatic Release Process
The entire process is automated to make development easier. The actual steps are simple:
TODO after Pull Request was merged in:
-
Checkout master:
git checkout master
-
Pull the new stuff:
git pull -
(If not already, run the
setup_developer_environment_locallycommand):make setup_developer_environment_locally -
Update the
ONDEWO_VTSI_VERSIONin theMakefile -
Add the new Release Notes in
RELEASE.mdin the format:## Release ONDEWO VTSI Python Client X.X.X <---- Beginning of Notes ...<NOTES>... ***************** <---- End of Notes -
Release:
make ondewo_release
The release process can be divided into 6 Steps:
buildspecified version of theondewo-vtsi-apicommit and pushall changes in code resulting from thebuild- Create and push the
release branche.g.release/1.3.20 - Create and push the
release tage.g.1.3.20 - Create a new
Releaseon GitHub - Publish the built
distfolder topypi.org
⚠️ The Release Automation checks if the build has created all the proto-code files, but it does not check the code-integrity. Please build and test the generated code prior to starting the release process.
Metadata
Release files for ondewo-vtsi-client 9.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ondewo_vtsi_client-9.1.0.tar.gz | 702.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ondewo_vtsi_client-9.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.4 MB
Release files / ondewo_vtsi_client-9.1.0.tar.gz
| Download URL | ondewo_vtsi_client-9.1.0.tar.gz |
|---|---|
| Size | 702.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c0d7c69530aa9e6117cecea15cd81336793a5be2cab714112a13de67c87b291a
|
|
BLAKE2b-256 checksum How to use checksums |
a1561bb8931f8c007f8946f1c12238ba6509e4287636328afc5132f90806db0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / ondewo_vtsi_client-9.1.0-py3-none-any.whl
| Download URL | ondewo_vtsi_client-9.1.0-py3-none-any.whl |
|---|---|
| Size | 736.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
caa01d53a5523b322dadc68488434638226725f4867b000dfa7e6266a36f5b1c
|
|
BLAKE2b-256 checksum How to use checksums |
f1618616a0044f70e97016ce6d35a56e81073732c4e833ba4aee6fe1f7a4ec63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|