Skip to content

Commit ffd9f8d

Browse files
authored
Merge pull request #5 from speechmatics/v0.0.5
Add option to change default assistant from cli and other small improvements
2 parents f777fd0 + 4c3082e commit ffd9f8d

File tree

12 files changed

+192
-13
lines changed

12 files changed

+192
-13
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
python-version: [ "3.10", "3.11" ]
14+
python-version: [ "3.9", "3.10", "3.11", "3.12" ]
1515

1616
steps:
1717
- uses: actions/checkout@v2

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7+
## [0.0.5] - 2024-11-13
8+
9+
### Added
10+
11+
- Added the option to change the assistant from CLI
12+
- Added the option to load conversation_config from a config file
13+
- Added client handling of unexpected messages from the server
14+
15+
### Changed
16+
17+
- Allow versions of websockets from `10.0` up to and including `13.1` to mitigate extra_headers compatibility issue
18+
with websockets `14.0`
19+
- Improved documentation for Interaction class
20+
721
## [0.0.4] - 2024-11-12
822

923
### Added
@@ -13,8 +27,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1327
- `ResponseInterrupted`: Indicates an interruption in the TTS audio stream from the server.
1428
The message contains the textual content up to the point where the utterance was stopped.
1529
- `ResponseCompleted`: Indicates the completion of TTS audio transmission from the server.
16-
- `ConversationEnding`: Indicates the session will continue in one-sided mode during TTS playback of the final words.
1730
The message includes the textual content of the utterance just spoken.
31+
- `ConversationEnding`: Indicates the session will continue in one-sided mode during TTS playback of the final words.
1832
- `AddAudio`: Implicit name for all inbound binary messages.
1933
The client confirms receipt by sending an `ServerMessageType.AudioReceived` message.
2034
- `AudioReceived`: Response to `ServerMessageType.AddAudio`, indicating that audio has been added successfully.

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,39 @@ python setup.py install --user
3333
speechmatics-flow --url $URL --auth-token $TOKEN --ssl-mode insecure
3434
```
3535

36+
### Change Assistant (Amelia → Humphrey)
37+
38+
To set the assistant to *Humphrey* instead of *Amelia* run this command:
39+
40+
```bash
41+
speechmatics-flow --url $URL --auth-token $TOKEN --ssl-mode insecure --assistant humphrey
42+
```
43+
44+
### Load conversation_config from a config file
45+
46+
Instead of manually setting up conversation parameters, you can load them from a configuration file.
47+
48+
Create a JSON file with the template details, for example "conversation_config.json" and run flow client
49+
using the `--config-file` option
50+
51+
```json
52+
{
53+
"template_id": "flow-service-assistant-humphrey",
54+
"template_variables": {
55+
"persona": "You are an English butler named Humphrey.",
56+
"style": "Be charming but unpredictable.",
57+
"context": "You are taking a customer's order at a fast food restaurant."
58+
}
59+
}
60+
```
61+
62+
```bash
63+
speechmatics-flow --url $URL --auth-token $TOKEN --ssl-mode insecure --config-file conversation_config.json
64+
```
65+
66+
> **Hint**: Why limit Humphrey? Try changing the template_variables to see what happens if he’s not a butler but
67+
> perhaps... a pirate, a celebrity chef, or a royal advisor. We won’t stop you. 🏴‍☠️
68+
3669
## Support
3770

3871
If you have any issues with this library or encounter any bugs then please get in touch with us at

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.4
1+
0.0.5

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
httpx==0.27.1
22
pyaudio==0.2.14
3-
websockets>=10
3+
setuptools
4+
websockets>=10,<=13.1

speechmatics_flow/cli.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import sys
1111
from dataclasses import dataclass
1212
from socket import gaierror
13-
from typing import Any, Dict
1413

1514
import httpx
1615
from websockets.exceptions import WebSocketException
@@ -25,6 +24,7 @@
2524
Interaction,
2625
ConnectionSettings,
2726
)
27+
from speechmatics_flow.templates import TemplateOptions
2828

2929
LOGGER = logging.getLogger(__name__)
3030

@@ -99,17 +99,22 @@ def get_conversation_config(
9999
:param args: Keyword arguments probably from the command line.
100100
:type args: Dict
101101
102-
:return: Settings for the ASR engine.
102+
:return: Settings for the Flow engine.
103103
:rtype: models.ConversationConfig
104104
"""
105105

106-
config: Dict[str, Any] = {}
106+
config = {}
107+
# First, get configuration from the config file if provided.
107108
if args.get("config_file"):
108109
with open(args["config_file"], encoding="utf-8") as config_file:
109110
config = json.load(config_file)
110111

111112
if config.get("conversation_config"):
112-
config.update(config.pop("conversation_config"))
113+
config = config["conversation_config"]
114+
115+
# Command line arguments override values from config file
116+
if assistant := args.get("assistant"):
117+
config["template_id"] = TemplateOptions.get(assistant)
113118

114119
return ConversationConfig(**config)
115120

speechmatics_flow/cli_parser.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import argparse
66
import logging
77

8+
from speechmatics_flow.templates import TemplateOptions
9+
810
LOGGER = logging.getLogger(__name__)
911

1012

@@ -110,6 +112,21 @@ def get_arg_parser():
110112
"plaintext messages."
111113
),
112114
)
115+
parser.add_argument(
116+
"--config-file",
117+
dest="config_file",
118+
type=str,
119+
default=None,
120+
help="Read the conversation config from a file."
121+
" If you provide this, all other config options work as overrides.",
122+
)
123+
parser.add_argument(
124+
"--assistant",
125+
default=None,
126+
type=str,
127+
choices=[k for k in TemplateOptions.keys()],
128+
help="Choose your assistant.",
129+
)
113130

114131
return parser
115132

speechmatics_flow/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ async def _consumer(self, message, from_cli: False):
198198
if message_type is None:
199199
return
200200

201+
if message_type not in self.event_handlers:
202+
LOGGER.warning(f"Unknown message type {message_type!r}")
203+
return
204+
201205
for handler in self.event_handlers[message_type]:
202206
try:
203207
handler(copy.deepcopy(message))

speechmatics_flow/models.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import ssl
88
from dataclasses import asdict, dataclass, field
99
from enum import Enum
10-
from typing import Callable, Dict, Optional, Literal
10+
from typing import Callable, Dict, Optional
11+
12+
from speechmatics_flow.templates import TemplateID
1113

1214

1315
@dataclass
@@ -62,9 +64,7 @@ class ConnectionSettings:
6264
class ConversationConfig:
6365
"""Defines configuration parameters for conversation requests."""
6466

65-
template_id: Literal[
66-
"default", "flow-service-assistant-amelia", "flow-service-assistant-humphrey"
67-
] = "default"
67+
template_id: TemplateID = "default"
6868
"""Name of a predefined template."""
6969

7070
template_variables: Optional[Dict[str, str]] = None
@@ -160,7 +160,30 @@ class ServerMessageType(str, Enum):
160160

161161
@dataclass
162162
class Interaction:
163-
"""Defines various interactions between client and server."""
163+
"""
164+
Defines a single interaction between a client and a server, typically
165+
used to handle non-continuous streams such as an audio file. This class
166+
enables the server to respond after the stream has finished or based
167+
on the specified callback function, allowing flexibility in connection
168+
handling after streaming.
169+
170+
Attributes:
171+
stream (io.BufferedReader): The audio stream to be sent to the server.
172+
callback (Optional[Callable]): An optional function to be executed when
173+
the audio stream ends. This can be used to delay connection closure
174+
or perform additional actions upon stream completion.
175+
176+
Examples:
177+
Keep the connection open for an additional 2 seconds after streaming
178+
an audio file, allowing time for the server to respond.
179+
180+
```python
181+
Interaction(audio_stream, callback=lambda x: time.sleep(2))
182+
```
183+
"""
164184

165185
stream: io.BufferedReader
186+
"""The audio stream to be sent to the server."""
187+
166188
callback: Optional[Callable] = None
189+
"""An optional function to be executed when the audio stream ends."""

speechmatics_flow/templates.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Pre-configured Template IDs to start a Flow conversation."""
2+
3+
from enum import Enum
4+
from typing import Literal
5+
6+
7+
class Template(Enum):
8+
default = "default"
9+
amelia = "flow-service-assistant-amelia"
10+
humphrey = "flow-service-assistant-humphrey"
11+
12+
13+
TemplateID = Literal[
14+
Template.default.value,
15+
Template.amelia.value,
16+
Template.humphrey.value,
17+
]
18+
19+
# Map user-friendly name to full TemplateID
20+
TemplateOptions = {t.name: t.value for t in Template}

tests/data/conversation_config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"template_id": "flow-service-assistant-humphrey",
3+
"template_variables": {
4+
"persona": "You are an English butler named Humphrey.",
5+
"style": "Be charming but unpredictable.",
6+
"context": "You are taking a customer's order at a fast food restaurant."
7+
}
8+
}

tests/test_cli.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from pytest import mark, param
2+
3+
from speechmatics_flow import cli
4+
from speechmatics_flow.templates import Template
5+
6+
TEMPLATE_VARS = {
7+
"persona": "You are an English butler named Humphrey.",
8+
"style": "Be charming but unpredictable.",
9+
"context": "You are taking a customer's order at a fast food restaurant.",
10+
}
11+
12+
13+
@mark.parametrize(
14+
"args, exp_values",
15+
[
16+
param(
17+
[],
18+
{"template_id": Template.default.value},
19+
id="default assistant",
20+
),
21+
param(
22+
["--assistant=amelia"],
23+
{"template_id": Template.amelia.value},
24+
id="assistant amelia",
25+
),
26+
param(
27+
["--assistant=humphrey"],
28+
{"template_id": Template.humphrey.value},
29+
id="assistant humphrey",
30+
),
31+
param(
32+
["--config-file=tests/data/conversation_config.json"],
33+
{
34+
"template_id": "flow-service-assistant-humphrey",
35+
"template_variables": TEMPLATE_VARS,
36+
},
37+
id="params from config file",
38+
),
39+
param(
40+
["--assistant=amelia", "--config-file=tests/data/conversation_config.json"],
41+
{
42+
"template_id": "flow-service-assistant-amelia",
43+
"template_variables": TEMPLATE_VARS,
44+
},
45+
id="params from config file with assistant override from cli",
46+
),
47+
],
48+
)
49+
def test_get_conversation_config(args, exp_values):
50+
test_values = vars(cli.parse_args(args=args))
51+
config = cli.get_conversation_config(test_values)
52+
assert config.asdict() == exp_values, "Expecting {} but got {}".format(
53+
exp_values, config.asdict()
54+
)

0 commit comments

Comments
 (0)