ai.api.AIDataService Maven / Gradle / Ivy
package ai.api;
/***********************************************************************************************************************
*
* API.AI Android SDK - client-side libraries for API.AI
* =================================================
*
* Copyright (C) 2015 by Speaktoit, Inc. (https://www.speaktoit.com)
* https://www.api.ai
*
* *********************************************************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
***********************************************************************************************************************/
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import ai.api.http.HttpClient;
import ai.api.model.AIContext;
import ai.api.model.AIRequest;
import ai.api.model.AIResponse;
import ai.api.model.Entity;
import ai.api.model.Status;
/**
* Do simple requests to the AI Service
*/
public class AIDataService {
public static final String TAG = AIDataService.class.getName();
@NonNull
private final Context context;
@NonNull
private final AIConfiguration config;
@NonNull
private final String sessionId;
@NonNull
private final Gson gson = GsonFactory.getGson();
public AIDataService(@NonNull final Context context, @NonNull final AIConfiguration config) {
this.context = context;
this.config = config;
sessionId = SessionIdStorage.getSessionId(context);
}
public AIResponse request(@NonNull final AIRequest request) throws AIServiceException {
return request(request, null);
}
/**
* Make request to the ai service. This method must not be called in the UI Thread
*
* @param request request object to the service
* @return response object from service
*/
@NonNull
public AIResponse request(@NonNull final AIRequest request, @Nullable final RequestExtras requestExtras) throws AIServiceException {
if (request == null) {
throw new IllegalArgumentException("Request argument must not be null");
}
Log.d(TAG, "Start request");
try {
request.setLanguage(config.getApiAiLanguage());
request.setSessionId(sessionId);
request.setTimezone(Calendar.getInstance().getTimeZone().getID());
Map additionalHeaders = null;
if (requestExtras != null) {
fillRequest(request, requestExtras);
additionalHeaders = requestExtras.getAdditionalHeaders();
}
final String queryData = gson.toJson(request);
final String response = doTextRequest(config.getQuestionUrl(), queryData, additionalHeaders);
if (TextUtils.isEmpty(response)) {
throw new AIServiceException("Empty response from ai service. Please check configuration and Internet connection.");
}
Log.d(TAG, "Response json: " + response.replaceAll("[\r\n]+", " "));
final AIResponse aiResponse = gson.fromJson(response, AIResponse.class);
if (aiResponse == null) {
throw new AIServiceException("API.AI response parsed as null. Check debug log for details.");
}
if (aiResponse.isError()) {
throw new AIServiceException(aiResponse);
}
aiResponse.cleanup();
return aiResponse;
} catch (final MalformedURLException e) {
Log.e(TAG, "Malformed url should not be raised", e);
throw new AIServiceException("Wrong configuration. Please, connect to API.AI Service support", e);
} catch (final JsonSyntaxException je) {
throw new AIServiceException("Wrong service answer format. Please, connect to API.AI Service support", je);
}
}
private void fillRequest(@NonNull final AIRequest request, @NonNull final RequestExtras requestExtras) {
if (requestExtras.hasContexts()) {
request.setContexts(requestExtras.getContexts());
}
if (requestExtras.hasEntities()) {
request.setEntities(requestExtras.getEntities());
}
if (requestExtras.getLocation() != null) {
request.setLocation(requestExtras.getLocation());
}
}
/**
* Make requests to the ai service with voice data. This method must not be called in the UI Thread.
*
* @param voiceStream voice data stream for recognition
* @return response object from service
* @throws AIServiceException
*/
@NonNull
public AIResponse voiceRequest(@NonNull final InputStream voiceStream) throws AIServiceException {
return voiceRequest(voiceStream, new RequestExtras());
}
/**
* Make requests to the ai service with voice data. This method must not be called in the UI Thread.
*
* @param voiceStream voice data stream for recognition
* @param aiContexts additional contexts for request
* @return response object from service
* @throws AIServiceException
*/
@NonNull
public AIResponse voiceRequest(@NonNull final InputStream voiceStream, @Nullable final List aiContexts) throws AIServiceException {
return voiceRequest(voiceStream, new RequestExtras(aiContexts, null));
}
/**
* Make requests to the ai service with voice data. This method must not be called in the UI Thread.
*
* @param voiceStream voice data stream for recognition
* @param requestExtras object that can hold additional contexts and entities
* @return response object from service
* @throws AIServiceException
*/
@NonNull
public AIResponse voiceRequest(@NonNull final InputStream voiceStream, @Nullable final RequestExtras requestExtras) throws AIServiceException {
Log.d(TAG, "Start voice request");
try {
final AIRequest request = new AIRequest();
request.setLanguage(config.getApiAiLanguage());
request.setSessionId(sessionId);
request.setTimezone(Calendar.getInstance().getTimeZone().getID());
Map additionalHeaders = null;
if (requestExtras != null) {
fillRequest(request, requestExtras);
additionalHeaders = requestExtras.getAdditionalHeaders();
}
final String queryData = gson.toJson(request);
Log.d(TAG, "Request json: " + queryData);
final String response = doSoundRequest(voiceStream, queryData, additionalHeaders);
if (TextUtils.isEmpty(response)) {
throw new AIServiceException("Empty response from ai service. Please check configuration.");
}
Log.d(TAG, "Response json: " + response);
final AIResponse aiResponse = gson.fromJson(response, AIResponse.class);
if (aiResponse == null) {
throw new AIServiceException("API.AI response parsed as null. Check debug log for details.");
}
if (aiResponse.isError()) {
throw new AIServiceException(aiResponse);
}
aiResponse.cleanup();
return aiResponse;
} catch (final MalformedURLException e) {
Log.e(TAG, "Malformed url should not be raised", e);
throw new AIServiceException("Wrong configuration. Please, connect to AI Service support", e);
} catch (final JsonSyntaxException je) {
throw new AIServiceException("Wrong service answer format. Please, connect to API.AI Service support", je);
}
}
/**
* Forget all old contexts
*
* @return true if operation succeed, false otherwise
*/
public boolean resetContexts() {
final AIRequest cleanRequest = new AIRequest();
cleanRequest.setQuery("empty_query_for_resetting_contexts"); // TODO remove it after protocol fix
cleanRequest.setResetContexts(true);
try {
final AIResponse response = request(cleanRequest);
return !response.isError();
} catch (final AIServiceException e) {
Log.e(TAG, "Exception while contexts clean.", e);
return false;
}
}
public AIResponse uploadUserEntity(final Entity userEntity) throws AIServiceException {
return uploadUserEntities(Collections.singleton(userEntity));
}
public AIResponse uploadUserEntities(final Collection userEntities) throws AIServiceException {
if (userEntities == null || userEntities.size() == 0) {
throw new AIServiceException("Empty entities list");
}
final String requestData = gson.toJson(userEntities);
try {
final String response = doTextRequest(config.getUserEntitiesEndpoint(sessionId), requestData);
if (TextUtils.isEmpty(response)) {
throw new AIServiceException("Empty response from ai service. Please check configuration and Internet connection.");
}
Log.d(TAG, "Response json: " + response);
final AIResponse aiResponse = gson.fromJson(response, AIResponse.class);
if (aiResponse == null) {
throw new AIServiceException("API.AI response parsed as null. Check debug log for details.");
}
if (aiResponse.isError()) {
throw new AIServiceException(aiResponse);
}
aiResponse.cleanup();
return aiResponse;
} catch (final MalformedURLException e) {
Log.e(TAG, "Malformed url should not be raised", e);
throw new AIServiceException("Wrong configuration. Please, connect to AI Service support", e);
} catch (final JsonSyntaxException je) {
throw new AIServiceException("Wrong service answer format. Please, connect to API.AI Service support", je);
}
}
protected String doTextRequest(final String requestJson) throws MalformedURLException, AIServiceException {
return doTextRequest(config.getQuestionUrl(), requestJson);
}
protected String doTextRequest(final String endpoint, final String requestJson) throws MalformedURLException, AIServiceException {
return doTextRequest(endpoint, requestJson, null);
}
protected String doTextRequest(@NonNull final String endpoint,
@NonNull final String requestJson,
@Nullable final Map additionalHeaders) throws MalformedURLException, AIServiceException {
HttpURLConnection connection = null;
try {
final URL url = new URL(endpoint);
final String queryData = requestJson;
Log.d(TAG, "Request json: " + queryData);
if (config.getProxy() != null) {
connection = (HttpURLConnection) url.openConnection(config.getProxy());
} else {
connection = (HttpURLConnection) url.openConnection();
}
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.addRequestProperty("Authorization", "Bearer " + config.getApiKey());
connection.addRequestProperty("Content-Type", "application/json; charset=utf-8");
connection.addRequestProperty("Accept", "application/json");
if (additionalHeaders != null) {
for (final Map.Entry entry : additionalHeaders.entrySet()) {
connection.addRequestProperty(entry.getKey(), entry.getValue());
}
}
connection.connect();
final BufferedOutputStream outputStream = new BufferedOutputStream(connection.getOutputStream());
IOUtils.write(queryData, outputStream, Charsets.UTF_8);
outputStream.close();
final InputStream inputStream = new BufferedInputStream(connection.getInputStream());
final String response = IOUtils.toString(inputStream, Charsets.UTF_8);
inputStream.close();
return response;
} catch (final IOException e) {
if (connection != null) {
try {
final InputStream errorStream = connection.getErrorStream();
if (errorStream != null) {
final String errorString = IOUtils.toString(errorStream, Charsets.UTF_8);
Log.d(TAG, "" + errorString);
return errorString;
}
else {
throw new AIServiceException("Can't connect to the api.ai service.", e);
}
} catch (final IOException ex) {
Log.w(TAG, "Can't read error response", ex);
}
}
Log.e(TAG, "Can't make request to the API.AI service. Please, check connection settings and API access token.", e);
throw new AIServiceException("Can't make request to the API.AI service. Please, check connection settings and API access token.", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
protected String doSoundRequest(@NonNull final InputStream voiceStream, @NonNull final String queryData) throws MalformedURLException, AIServiceException {
return doSoundRequest(voiceStream, queryData, null);
}
/**
* Method extracted for testing purposes
*/
protected String doSoundRequest(@NonNull final InputStream voiceStream,
@NonNull final String queryData,
@Nullable final Map additionalHeaders) throws MalformedURLException, AIServiceException {
HttpURLConnection connection = null;
HttpClient httpClient = null;
try {
final URL url = new URL(config.getQuestionUrl());
if (config.getProxy() != null) {
connection = (HttpURLConnection) url.openConnection(config.getProxy());
} else {
connection = (HttpURLConnection) url.openConnection();
}
connection.addRequestProperty("Authorization", "Bearer " + config.getApiKey());
connection.addRequestProperty("Accept", "application/json");
if (additionalHeaders != null) {
for (final Map.Entry entry : additionalHeaders.entrySet()) {
connection.addRequestProperty(entry.getKey(), entry.getValue());
}
}
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
httpClient = new HttpClient(connection);
httpClient.setWriteSoundLog(config.isWriteSoundLog());
httpClient.connectForMultipart();
httpClient.addFormPart("request", queryData);
httpClient.addFilePart("voiceData", "voice.wav", voiceStream);
httpClient.finishMultipart();
final String response = httpClient.getResponse();
return response;
} catch (final IOException e) {
if (httpClient != null) {
final String errorString = httpClient.getErrorString();
Log.d(TAG, "" + errorString);
if (!TextUtils.isEmpty(errorString)) {
return errorString;
} else if (e instanceof HttpRetryException) {
final AIResponse response = new AIResponse();
final int code = ((HttpRetryException) e).responseCode();
final Status status = Status.fromResponseCode(code);
status.setErrorDetails(((HttpRetryException) e).getReason());
response.setStatus(status);
throw new AIServiceException(response);
}
}
Log.e(TAG, "Can't make request to the API.AI service. Please, check connection settings and API.AI keys.", e);
throw new AIServiceException("Can't make request to the API.AI service. Please, check connection settings and API.AI keys.", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy