Remote Procedure Call (RPC) sub-package

Dragonfly’s remote procedure call (RPC) sub-package allows for interaction with a dragonfly speech recognition engine running in a remote process. This is useful for building responsive GUI applications without having to integrate them into a loaded grammar or grammar rule.

Some use cases for this framework include:

  • Listing available commands and grammars in the current context.
  • Integrating and displaying documentation.
  • Guiding the user through complex commands.
  • A GUI for building dragonfly/third-party grammars.

RPC server

Dragonfly’s RPC server handles requests by processing each method through the current engine’s multiplexing timer interface. This allows engines to handle requests safely and keeps engine-specific implementation details out of the dragonfly.rpc sub-package.

Security tokens

The RPC server uses mandatory security tokens to authenticate client requests. This avoids some security issues where, for example, malicious web pages could send POST requests to the server, even if running on localhost.

If sending requests over an open network, please ensure that the connection is secure by using TLS or SSH port forwarding.

If the server’s security_token constructor parameter is not specified, a new token will be generated and printed to the console. Clients must specify the security token either as the last positional argument or as the security_token keyword argument.

Errors will be raised if clients send no security token or a token that doesn’t match the server’s.

Class reference

exception PermissionDeniedError[source]

Error raised if clients send security tokens that don’t match the server’s.

class RPCServer(address='127.0.0.1', port=50051, ssl_context=None, threaded=True, security_token=None)[source]

RPC server class.

This class will run a local web server on port 50051 by default. The server expects requests using JSON-RPC 2.0.

Constructor arguments:

  • address – address to use (str, default: “127.0.0.1”)
  • port – port to use (int, default: 50051)
  • ssl_context – SSL context object to pass to werkzeug.serving.run_simple (SSLContext, default: None).
  • threaded – whether to use a separate thread to process each request (bool, default: True).
  • security_token – security token for authenticating clients (str, default: None). A new token will be generated and printed if this parameter is unspecified.

The ssl_context parameter is explained in more detail in Werkzeug’s SSL serving documentation.

Secure connections can also be set up using OpenSSH port forwarding with a command such as:

$ ssh -NTf -L 50051:127.0.0.1:50051 <system-with-rpc-server>

Minor note: using an IP address instead of a hostname for the address parameter should increase performance somewhat, e.g. “127.0.0.1” instead of “localhost”.

Warning

Do not send requests to the server from the main engine thread; thread deadlocks will occur if you do this because the main thread cannot call timer functions and wait for a response at the same time. The RPC framework was designed to be used from remote processes.

Requests will not be processed if the engine is not connected and processing speech.

add_method(method, name=None)[source]

Add an RPC method to the server.

Restarting the server is not required for the new method to be available.

This can be used to override method implementations if that is desirable.

This method can also be used as a decorator.

Parameters:
  • method (callable) – the implementation of the RPC method to add.
  • name (str) – optional name of the RPC method to add. If this is None, then method.__name__ will be used instead.
remove_method(name)[source]

Remove an RPC method from the server. This will not raise an error if the method does not exist.

Restarting the server is not required for the change to take effect.

Parameters:name (str) – the name of the RPC method to remove.
send_request(method, params, id=0)[source]

Utility method to send a JSON-RPC request to the server. This will block the current thread until a response is received.

This method is mostly used for testing. If called from the engine’s main thread, a deadlock will occur.

This will raise an error if the request fails with an error or if the server is unreachable.

The server’s security token will automatically be added to the params list/dictionary.

Parameters:
  • method (str) – name of the RPC method to call.
  • params (list | dict) – parameters of the RPC method to call.
  • id (int) – ID of the JSON-RPC request (default: 0).
Returns:

JSON-RPC response

Return type:

dict

Raises:

RuntimeError

start()[source]

Start the server.

This method is non-blocking, the RPC server will run on a separate daemon thread. This way it is not necessary to call stop() before the main thread terminates.

stop()[source]

Stop the server if it is running.

url

The URL to send JSON-RPC requests to.

RPC methods

For RPC methods to work they must be added to the server with add_method(). For example:

from dragonfly.engines import get_engine
from dragonfly.rpc import RPCServer

# Initialise and start the server.
server = RPCServer()
server.start()

# Add the RPC method via decoration.
@server.add_method
def get_engine_language():
    return get_engine().language

# add_method() can also be used normally.
server.add_method(get_engine_language)

Sending requests

Requests can be sent to the server using, the send_rpc_request() function from Python:

send_rpc_request(
    server.url, method="get_engine_language",
    params=[server.security_token], id=0
)

Other tools such as curl can also be used.

Using positional arguments:
$ curl --data-binary '{"jsonrpc":"2.0","id": "0","method": "speak","params": ["hello world", "<security-token>"]}' -H 'content-type:text/json;' http://127.0.0.1:50051

Using key word arguments:
$ curl --data-binary '{"jsonrpc":"2.0","id": "0","method": "speak","params": {"text": "hello world", "security_token": "<security-token>"}}' -H 'content-type:text/json;' http://127.0.0.1:50051

Built-in RPC methods

get_engine_language()[source]

Get the current engine’s language.

Returns:language code
Return type:str
get_recognition_history()[source]

Get the recognition history if an observer is registered.

The register_history() method must be called to register the observer first.

Returns:history
Return type:list
is_in_speech()[source]

Whether the user is currently speaking.

The register_history() method must be called to register the observer first.

Return type:bool
list_grammars()[source]

Get a list of grammars loaded into the current engine.

This includes grammar rules and attributes.

mimic(words)[source]

Mimic the given words.

Parameters:words – string or list of words to mimic
Returns:whether the mimic was a success
Return type:bool
register_history(length=10, record_failures=False)[source]

Register an internal recognition observer.

Parameters:
  • length (int) – length to initialize the RecognitionHistory instance with (default 10).
  • record_failures (bool) – whether to record recognition failures (default False).
speak(text)[source]

Speak the given text using text-to-speech using engine.speak().

Parameters:text (str) – text to speak using text-to-speech
unregister_history()[source]

Unregister the internal recognition observer.

RPC utility functions

send_rpc_request(url, method, params, id=0)[source]

Utility function to send a JSON-RPC request to a server.

This will raise an error if the request fails with an error or if the server is unreachable.

Parameters:
  • url (str) – the URL to send the JSON-RPC request to.
  • method (str) – name of the RPC method to call.
  • params (list) – parameters of the RPC method to call.
  • id (int) – ID of the JSON-RPC request (default: 0).
Returns:

JSON-RPC response

Return type:

dict

Raises:

RuntimeError