Skip to content

Commit

Permalink
[4.3] KZOO-76: added support for IBM speech to text (#6669)
Browse files Browse the repository at this point in the history
* [4.3] KZOO-76: added support for IBM speech to text

* remove author from copy-paste

* KZOO-76: address CI findings

* KZOO-76: more ci fixes
  • Loading branch information
k-anderson authored Nov 23, 2020
1 parent 379fb89 commit 32a8793
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
23 changes: 23 additions & 0 deletions applications/crossbar/priv/api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -35384,6 +35384,29 @@
],
"type": "object"
},
"system_config.speech.ibm": {
"description": "Schema for speech.ibm system_config",
"properties": {
"asr_api_key": {
"description": "speech ibm asr_api_key",
"type": "string"
},
"asr_model": {
"default": "en-US_NarrowbandModel",
"description": "speech ibm asr_model",
"type": "string"
},
"asr_profanity_filter": {
"description": "speech ibm asr_profanity_filter",
"type": "boolean"
},
"asr_url": {
"description": "speech ibm asr_url",
"type": "string"
}
},
"type": "object"
},
"system_config.stepswitch": {
"description": "Schema for stepswitch system_config",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"_id": "system_config.speech.ibm",
"description": "Schema for speech.ibm system_config",
"properties": {
"asr_api_key": {
"description": "speech ibm asr_api_key",
"type": "string"
},
"asr_model": {
"default": "en-US_NarrowbandModel",
"description": "speech ibm asr_model",
"type": "string"
},
"asr_profanity_filter": {
"description": "speech ibm asr_profanity_filter",
"type": "boolean"
},
"asr_url": {
"description": "speech ibm asr_url",
"type": "string"
}
},
"type": "object"
}
152 changes: 152 additions & 0 deletions core/kazoo_speech/src/asr/kazoo_asr_ibm.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
%%%-----------------------------------------------------------------------------
%%% @copyright (C) 2018-2020, 2600Hz
%%% @doc This Source Code Form is subject to the terms of the Mozilla Public
%%% License, v. 2.0. If a copy of the MPL was not distributed with this
%%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%%
%%% @end
%%%-----------------------------------------------------------------------------
-module(kazoo_asr_ibm).
-behaviour(gen_asr_provider).

-export([available/0]).
-export([preferred_content_type/0]).
-export([accepted_content_types/0]).
-export([freeform/4]).
-export([commands/5]).
-export([set_api_key/1]).

-include("kazoo_speech.hrl").

-define(IBM_CONFIG_CAT, <<(?MOD_CONFIG_CAT)/binary, ".ibm">>).
-define(IBM_ASR_URL, kapps_config:get_binary(?IBM_CONFIG_CAT, <<"asr_url">>)).
-define(IBM_ASR_KEY, kapps_config:get_binary(?IBM_CONFIG_CAT, <<"asr_api_key">>)).
-define(IBM_ASR_PROFANITY_FILTER, kapps_config:get_is_true(?IBM_CONFIG_CAT, <<"asr_profanity_filter">>)).
-define(IBM_ASR_MODEL, kapps_config:get_binary(?IBM_CONFIG_CAT, <<"asr_model">>, <<"en-US_NarrowbandModel">>)).
-define(IBM_ASR_PREFERRED_CONTENT_TYPE, <<"application/mpeg">>).
-define(IBM_ASR_ACCEPTED_CONTENT_TYPES, [<<"audio/mpeg">>]).

%%%------------------------------------------------------------------------------
%%% @doc Return true if IBM ASR is configured / available otherwise false.
%%% @end
%%%------------------------------------------------------------------------------
-spec available() -> boolean().
available() ->
kz_term:is_not_empty(?IBM_ASR_KEY).

%%%-----------------------------------------------------------------------------
%%% @doc Return or set the preferred asr content type for the ASR provider
%%% @end
%%%-----------------------------------------------------------------------------
-spec preferred_content_type() -> kz_term:ne_binary().
preferred_content_type() ->
?IBM_ASR_PREFERRED_CONTENT_TYPE.

%%%-----------------------------------------------------------------------------
%%% @doc Return list of supported Content Types by ASR provider
%%% @end
%%%-----------------------------------------------------------------------------
-spec accepted_content_types() -> kz_term:ne_binaries().
accepted_content_types() ->
?IBM_ASR_ACCEPTED_CONTENT_TYPES.

%%%-----------------------------------------------------------------------------
%%% @doc
%%% @end
%%%-----------------------------------------------------------------------------
-spec commands(kz_term:ne_binary(), kz_term:ne_binaries(), kz_term:ne_binary(), kz_term:ne_binary(), kz_term:proplist()) -> provider_return().
commands(_Bin, _Commands, _ContentType, _Locale, _Opts) ->
{'error', 'asr_provider_failure', <<"not implemented">>}.

%%%-----------------------------------------------------------------------------
%%% @doc Callback for API request to ASR Provider and handle transcription response.
%%% @end
%%%-----------------------------------------------------------------------------
-spec freeform(binary(), kz_term:ne_binary(), kz_term:ne_binary(), kz_term:proplist()) -> asr_resp().
freeform(Content, ContentType, Locale, Options) ->
case kazoo_asr_util:maybe_convert_content(Content, ContentType, accepted_content_types(), preferred_content_type()) of
{'error', _}=E -> E;
{Content1, ContentType1} -> exec_freeform(Content1, ContentType1, Locale, Options)
end.

-spec exec_freeform(binary(), kz_term:ne_binary(), kz_term:ne_binary(), kz_term:proplist()) ->
asr_resp().
exec_freeform(Content, ContentType, _Locale, Options) ->
URL = build_url(),
Headers = req_headers(ContentType),
OptionsWithAuth = [{'basic_auth', {<<"apikey">>, ?IBM_ASR_KEY}} | Options],
lager:debug("sending request to ~s with headers ~p", [URL, Headers]),
handle_response(make_request(URL, Headers, Content, OptionsWithAuth)).

-spec build_url() -> kz_term:binary().
build_url() ->
URL = <<(?IBM_ASR_URL)/binary, "/v1/recognize">>,
case kz_http_util:props_to_querystring(
props:filter_undefined(
[{<<"profanity_filter">>, ?IBM_ASR_PROFANITY_FILTER}
,{<<"model">>, ?IBM_ASR_MODEL}
])
)
of
[] -> URL;
QueryString ->
<<URL/binary, "?", (kz_term:to_binary(QueryString))/binary>>
end.

-spec req_headers(kz_term:ne_binary()) -> kz_http:headers().
req_headers(ContentType) ->
[{"Content-Type", ContentType}
,{"User-Agent", kz_term:to_list(node())}
].

%%%-----------------------------------------------------------------------------
%%% @doc Execute API request to ASR Provider and handle transcription response.
%%% @end
%%%-----------------------------------------------------------------------------
-spec make_request(kz_term:ne_binary(), kz_term:proplist(), iodata(), kz_term:proplist()) ->
kz_http:ret().
make_request(BaseUrl, Headers, Body, Opts) ->
case props:get_value('receiver', Opts) of
Pid when is_pid(Pid) ->
HTTPOptions = props:delete('receiver', Opts),
lager:debug("streaming response to ~p", [Pid]),
kz_http:async_req(Pid, 'post', kz_term:to_list(BaseUrl), Headers, Body, HTTPOptions);
_ ->
HTTPOptions = props:delete('receiver', Opts),
kz_http:post(kz_term:to_list(BaseUrl), Headers, Body, HTTPOptions)
end.

-spec handle_response(kz_http:ret()) -> asr_resp().
handle_response({'error', _R}=E) ->
lager:debug("asr failed with error ~p", [_R]),
E;
handle_response({'http_req_id', ReqID}) ->
lager:debug("streaming response ~p to provided receiver", [ReqID]),
{'ok', ReqID};
handle_response({'ok', 200, _Headers, Content2}) ->
lager:debug("ASR of media succeeded: ~s", [Content2]),
Results = kz_json:get_list_value(<<"results">>, kz_json:decode(Content2), []),
Sentences = [get_sentence(Alternative) || Alternative <- Results],

Props = [{<<"result">>, <<"success">>}
,{<<"text">>, list_to_binary(Sentences)}
],
{'ok', kz_json:from_list(Props)};
handle_response({'ok', _Code, _Hdrs, Content2}) ->
lager:debug("asr of media failed with code ~p", [_Code]),
lager:debug("resp: ~s", [Content2]),
{'error', 'asr_provider_failure', Content2}.

-spec get_sentence(kz_json:object()) -> kz_term:ne_binary().
get_sentence(Alternative) ->
[Sentence|_] = kz_json:get_list_value(<<"alternatives">>, Alternative),
kz_json:get_value(<<"transcript">>, Sentence).

%%%-----------------------------------------------------------------------------
%%% @doc Set the asr key
%%% @end
%%%-----------------------------------------------------------------------------
-spec set_api_key(kz_term:ne_binary()) -> 'ok'.
set_api_key(Key) ->
{'ok', _} = kapps_config:set_default(?IBM_CONFIG_CAT, <<"asr_api_key">>, Key),
'ok'.

0 comments on commit 32a8793

Please sign in to comment.