augnitoambientsdk
Version:
Use this typescript SDK to integrate Augnito’s Ambient Tech within your EMR. To get access credentials or know more about how Augnito Ambient can benefit you, please visit our website and connect with our sales team: https://augnito.ai/
921 lines (868 loc) • 32.3 kB
text/typescript
import AmbientConfig from "./config/AmbientConfig";
import { socketConfig } from "./config/socketConfig";
import { AmbientRestAPI } from "./api/AmbientRestAPI";
import { Logger } from "./utils/Logger";
import { Guard } from "./utils/Guard";
import { AugnitoRecorder } from "augnitorecorder";
import { AugnitoSocketResponse } from "./support/AugnitoSocketResponse";
import { AmbientPACEAPI } from "./api/AmbientPACEAPI";
import { SettingsConfigType, UserConfigMap } from "./config/SettingsConfig";
import { NoteTypeConfig } from "./config/NoteTypeConfig";
import { HttpCodedError, NoteOutputStyleConfig, NoteVerbosityConfig, PreferenceConfig } from "./config";
import { ErrorCodes } from "./config/ErrorCodes";
import { ErrorMessages } from "./utils/Constants";
import ErrorInfo from "./config/ErrorInfo";
import NetworkMonitor from "./support/NetworkMonitor";
/**
* Augnito Ambient Manager
* @description Handles the connection with the Ambient API Server.
*/
export class AugnitoAmbient {
private _logTag = "Augnito-Ambient";
private _ambientRestAPI: AmbientRestAPI;
private _ambientPaceAPI: AmbientPACEAPI;
private config: socketConfig | null | undefined;
private enableLog: boolean | undefined;
private shouldReadAudioIntensity: boolean | undefined;
private recorderIns: AugnitoRecorder | undefined;
private forceStopAudio: boolean = false;
private _isRecording: boolean = false;
private echoCancellation: boolean | { exact: boolean } | undefined;
private noiseSuppression: boolean | { exact: boolean } | undefined;
private autoGainControl: boolean | { exact: boolean } | undefined;
constructor(config: AmbientConfig) {
this._ambientRestAPI = new AmbientRestAPI(this.validateConfig(config));
this._ambientPaceAPI = new AmbientPACEAPI(config);
this.config = this.createSocketConfig(config);
this.enableLog = config.enableLogs;
this.shouldReadAudioIntensity = config.shouldReadAudioIntensity;
this.echoCancellation = config.echoCancellation;
this.noiseSuppression = config.noiseSuppression;
this.autoGainControl = config.autoGainControl;
}
/**
* @description Callback to receive the JOb Id
*/
public onJobCreated?: (text: string) => void;
/**
* @description Callback for status changed
*/
public onStateChanged?: (isRecording: boolean) => void;
/**
* Callback triggered when an error occurs within the SDK
*/
public onError?: (error: ErrorInfo) => void;
public onOtherResult?: (text: string) => void;
public onIntensityValue?: (intensity: number) => void;
public onIdleMic?: () => void;
public onSoapNoteGenerated?: (soapNote: string) => void;
public onTranscriptGenerated?: (transcript: string) => void;
public onCodesGenerated?: (codes: string) => void;
public onJobStatus?: (statusResponse: string) => void;
private initRecorder(url: string) {
// console.log(_filetype, _noteparams);
this.recorderIns = new AugnitoRecorder({
// "{\"Region\": 801, \"Specialty\": 200, \"NoteType\": 40, \"Gender\": 0}"
serverURL: url,
enableLogs: this.enableLog ?? false,
isDebug: this.enableLog ?? false,
bufferInterval: 15,
EOS_Message: `{"JobAction": "EOS","Status": 0,"Type": "meta"}`,
socketTimeoutInterval: 20000,
shouldSendAudioDataSequence: true,
shouldReadIntensity: this.shouldReadAudioIntensity,
echoCancellation: this.echoCancellation,
noiseSuppression: this.noiseSuppression,
autoGainControl: this.autoGainControl
});
this.recorderIns.echoCancellation = this.echoCancellation ?? false;
this.recorderIns.noiseSuppression = this.noiseSuppression ?? false;
this.recorderIns.autoGainControl = this.autoGainControl ?? false;
this.recorderIns.onError = this.onErrorCallback.bind(this);
this.recorderIns.onStateChanged = this.onStateChangeCallback.bind(this);
this.recorderIns.onSessionEvent = this.onSessionEventCallback.bind(this);
this.recorderIns.onOtherResults = this.onOtherResultsCallback.bind(this);
this.recorderIns.onIntensity = this.onIntensityCallback.bind(this);
this.recorderIns.onSpeechResponse = this.onSpeechResponseCallback.bind(this);
this.recorderIns.onJobStatus = this.onJobStatusEventCallback.bind(this);
}
// #region Public Methods
/**
* Returns the Note parameters which need be use while doing toggle listeing
* @returns JSON object of Note parameters
*/
public async getNoteParams(): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientPaceAPI.getNoteParams();
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param PageSize number of notes to fetch
* @param PageID is optional parameter, send start page id to fetch
* @returns list of notes for the user
*/
public async getAllNotes(PageSize: number, PageID?: string): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientRestAPI.ListJobs(PageSize, PageID);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId to retrieve output for a specific audio file
* @returns JSON object contains both Transcript and Note on sucess else fail resonse
*/
public async getSummarizedNote(JobId: string): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientRestAPI.FetchJob(JobId);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId needs to pass to store the final Note for that audio
* @param NoteDate This key will store the final edited Note that user wants to send.
* @returns suceess response on successful submit of note else fail response
*/
public async sendSummarizedNote(
JobId: string,
NoteDate: string
): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
return await this._ambientRestAPI.SendFinalNote(JobId, NoteDate);
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param filetype Type of file being uploaded, wav, mp3, etc. Ex: “filetype=wav“
* @param noteparams Qualifiers to determine the type of clinical note to be generated for that audio file.
* @returns Callback triggers to reurn the Job id on meta message
*/
public toggleListening(
_filetype: string,
_noteparams: string,
jobName?: string,
jobId?: string,
recordedDuration?: number
): void {
// console.log("toggleListening:", _filetype, _noteparams);
var serverUrl =
this.config?.prepareWSSURL(_filetype, _noteparams, jobName, jobId) || "";
if (!this.recorderIns) {
this.initRecorder(serverUrl);
}
this.recorderIns?.toggleStartStopAudioStream(recordedDuration, serverUrl);
}
/**
* Method called to pause or resume audio recording accordingly
* @param filetype Type of file being uploaded, wav, mp3, etc. Ex: “filetype=wav“
* @param noteparams Qualifiers to determine the type of clinical note to be generated for that audio file.
* @returns Callback triggers to reurn the Job id on meta message
*/
public togglePauseResumeListening(
_filetype: string,
_noteparams: string,
jobName?: string,
jobId?: string,
recordedDuration?: number
): void {
// console.log("togglePauseResumeListening:", _filetype, _noteparams);
var serverUrl =
this.config?.prepareWSSURL(_filetype, _noteparams, jobName, jobId) || "";
if (!this.recorderIns) {
this.initRecorder(serverUrl);
}
this.recorderIns?.togglePauseResumeAudioStream(recordedDuration, serverUrl);
}
/**
* Method called to start audio recording
*/
public startListening(): void {
this.recorderIns?.startAudio();
}
/**
* Method called to stop audio recording and clean up resources
*/
public stopListening(): void {
this.recorderIns?.stopAudio(true, true);
}
/**
* @param JobIds to delete one or more notes
* @returns success response on successful delete of notes else fail response
*/
public async deleteNotes(JobIds: string[]): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
// if (!await this.isInternetAvailable()) {
// this.onErrorCallback(ErrorMessages.noInternetConnection);
// return;
// }
var responseJson = await this._ambientRestAPI.DeleteNotes(JobIds);
if (responseJson) {
if (responseJson.Status === 200) {
this.recorderIns?.stopAudio(false, true);
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId to rename note title
* @param NewJobName new title for the note
* @returns success response on successful rename of note else fail response
*/
public async renameNoteTitle(
JobId: string,
NewJobName: string
): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
// if (!await this.isInternetAvailable()) {
// this.onErrorCallback(ErrorMessages.noInternetConnection);
// return;
// }
var responseJson = await this._ambientRestAPI.RenameNoteTitle(
JobId,
NewJobName
);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId to retrieve pdf for a specific note
* @param NoteType takes a NoteTypeConfig value
* @param SpecialityId a numberic value which identifies the department to populate in the PDF
* @param Language, optional parameter, to specify the language in which to generate PDF, default value being English
* @param PersonnelInfo, optional parameter, to give details to fill into header of PDF. Default json string is '{"PatientName":"","PatientID":"","Age":"","Gender":"","ReceivingDoctor":"","DoctorName":"","DoctorDesignation":"","DoctorEmail":""}'
* @param RegenerateNote, optional parameter, to indicate if PDF has to be re-generated especially after an update is made to the note
* @returns json response with base64 string of the pdf output to be read from Data.PDF of the response
*/
public async getNotePDF(JobId: string, NoteType: NoteTypeConfig, SpecialityId: number, Language?: string, PersonnelInfo?: string, RegenerateNote?: boolean): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
// if (!await this.isInternetAvailable()) {
// this.onErrorCallback(ErrorMessages.noInternetConnection);
// return;
// }
var responseJson = await this._ambientRestAPI.DownloadNotePDF(JobId, NoteType, SpecialityId, Language, PersonnelInfo, RegenerateNote);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId to end note forcefully for paused job
* @returns success response on end of job else fail response
*/
public async endPausedJob(JobId: string): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientRestAPI.EndJob(JobId);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId needs to be passed to generate output
* @param Regenerate a bool to be set to true if CDI suggestions should be re-generated after an update is made to the note else set to false
* @param SoapNote is the structered soap note used to generate suggestions
* @param Codes structured medical codes used to generate suggestions
* @param PatientDetails json string which contains details such as age, gender and history of patient
* @returns JSON object contains suggestion Items on success else fail response
*/
public async generateCDISuggestions(
JobId: string,
Regenerate: boolean,
SoapNote?: string,
Codes?: string,
PatientDetails?: string,
): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var cdiVersion = 1;
var versionRes = await this._ambientPaceAPI.getOrgConfigMapping(SettingsConfigType.CDIVersion);
if (versionRes?.Items && versionRes.Items.length > 0 && versionRes.Items[0].Settings) {
var setting = versionRes.Items[0].Settings.find((x: { ID: number; }) => x.ID === 209);
if (setting)
cdiVersion = Number.parseInt(setting.ConfigValue);
}
var responseJson = await this._ambientRestAPI.GenerateSuggestions(
JobId,
Regenerate,
SoapNote,
PatientDetails,
Codes,
cdiVersion
);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId for which patient context needs to be updated
* @param Context a text string which gives context/history of patient useful to generate note
* @param Replace to be set to true if an update of context needs to be saved
* @returns success response on update of context else fail response
*/
public async updatePatientContext(JobId: string, Context: string,
Replace: boolean) {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
// if (!await this.isInternetAvailable()) {
// this.onErrorCallback(ErrorMessages.noInternetConnection);
// return;
// }
var responseJson = await this._ambientRestAPI.UpdatePatientContext(
JobId,
Context,
Replace
);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId for which note such as patient/referral/CDI suggestions needs to be updated
* @param NoteType takes a NoteTypeConfig value
* @param NewNote to update note json with specific values modified such as to submit feedback for CDI suggestions
* @returns success response on update of new note else fail response
*/
public async modifyGeneratedNote(JobId: string, NoteType: NoteTypeConfig, NewNote?: string): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientRestAPI.ModifyGeneratedNote(
JobId,
NoteType,
NewNote
);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId to retrieve transcript for a specific audio file
* @returns JSON object contains Time in seconds, Text spoken at that time and also array of MedicalData to highlight on success else fail response.
*/
public async fetchTranscriptionForNote(JobId: string) {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var preference = await this._ambientPaceAPI.getUserPreferences();
if (preference.Items[0].AutomaticTranscriptionEnabledForOrg) {
var responseJson = await this._ambientRestAPI.FetchTranscription(
JobId
);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} else {
this.onErrorCallback(ErrorMessages.transcriptionNotEnabled)
}
} catch (e: any) {
this.handleException(e)
}
}
/**
* @param JobId the job identifier for which feedback is being submitted
* @param Ratings a numeric rating from 1 to 5, value representing user satisfaction with the generated note
* @param Comment optional text comment providing additional feedback details
* @param Type optional string to specify the feedback category, defaults to 'SOAPFeedBack'
* @returns success response on successful submission of feedback else fail response
*/
public async sendFeedback(JobId: string, Ratings: number, Comment?: string, Type?: string): Promise<any> {
try {
if (!this._ambientRestAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var preference = await this._ambientPaceAPI.getUserPreferences();
if (preference.Items[0]?.FeedbackEnabledForOrg && preference.Items[0]?.FeedbackEnabled) {
var responseJson = await this._ambientRestAPI.SendFeedback(
JobId,
Ratings,
Comment ?? '',
Type ?? 'SOAPFeedBack'
);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} else {
this.onErrorCallback(ErrorMessages.feedbackNotEnabled)
}
} catch (e: any) {
this.handleException(e)
}
}
// /**
// * @param ConfigId to retrieve master config list for speciality type, visit type etc
// * @returns a list of all available type
// */
// public async getMasterConfiguration(
// ConfigId: SettingsConfigType
// ): Promise<any> {
// try {
// if (!this._ambientPaceAPI) {
// Logger.error("SDK not initialized", this._logTag);
// return;
// }
// var responseJson = await this._ambientPaceAPI.getSettingsConfigMaster(
// ConfigId
// );
// if (responseJson) {
// if (responseJson.Status === 200) {
// return responseJson;
// } else {
// this.onErrorCallback(responseJson.ErrorMessage);
// }
// } else {
// this.onErrorCallback("Unknown Error!");
// }
// } catch (e: any) {
// if (e instanceof Error) {
// this.onErrorCallback(e.message);
// }
// }
// }
/**
* @param ConfigId to retrieve user config list for speciality type, visit type etc
* @returns a list of user config for a particular type
*/
public async getUserConfiguration(
ConfigId: SettingsConfigType
): Promise<any> {
try {
if (!this._ambientPaceAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientPaceAPI.getUserConfigSettings(
ConfigId
);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e);
}
}
/**
* @param ConfigMap a list of userconfig settings to update selected/unselected speciality type, visit type etc
* @returns success response on successful update else fail response
*/
public async updateUserConfiguration(
ConfigMap: UserConfigMap[]
): Promise<any> {
try {
if (!this._ambientPaceAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientPaceAPI.updateUserConfig(ConfigMap);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e);
}
}
/**
* @returns a list of user preference
*/
public async getUserPreferenceSettings(): Promise<any> {
try {
if (!this._ambientPaceAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientPaceAPI.getUserPreferences();
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e);
}
}
/**
* @param NewUserPreference a list of preference of type PreferenceConfig to be updated
* @returns success response on successful update else fail response
*/
public async updateUserPreferenceSettings(
NewUserPreference: PreferenceConfig
): Promise<any> {
try {
if (!this._ambientPaceAPI) {
Logger.error("SDK not initialized", this._logTag);
return;
}
var responseJson = await this._ambientPaceAPI.updateUserPreference(NewUserPreference.AutomaticallyGenerateTranscript ?? false, NewUserPreference.PreferredTranscriptionLanguage ?? 'English', NewUserPreference.HighlightMedicalTerms ?? false, NewUserPreference.DisplayPatientContext ?? false, NewUserPreference.NoteOutputStyle ?? NoteOutputStyleConfig.DashFormat, NewUserPreference.NoteOutputVerbosity ?? NoteVerbosityConfig.Detailed);
if (responseJson) {
if (responseJson.Status === 200) {
return responseJson;
} else {
this.onErrorCallback(responseJson.ServerMessage);
}
} else {
this.onErrorCallback("Unknown Error!");
}
} catch (e: any) {
this.handleException(e);
}
}
/**
* @param options an object which is optional and takes custom url as string and timeout in milliseconds
* @returns true if connected else false and onError callback triggered with SDK12
*/
public async isInternetAvailable(options?: { url: null; timeoutMs?: null; } | undefined) {
const online = await NetworkMonitor.isOnline(options);
if (!online) {
this.onErrorCallback(ErrorMessages.noInternetConnection);
}
return online;
}
// #endregion
// #region client callbacks
private onEventCallback(data: string): void {
if (this.onJobCreated) {
this.onJobCreated(data);
}
}
private onStateChangeCallback(isRecording: boolean): void {
if (this.forceStopAudio) {
this.recorderIns?.stopAudio(false, true);
this.forceStopAudio = false;
return;
}
this._isRecording = isRecording;
if (this.onStateChanged) {
this.onStateChanged(isRecording);
}
}
private handleException(e: Error) {
if (e instanceof TypeError) {
this.onErrorCallback(e.message)
}
else if (e instanceof HttpCodedError) {
throw e;
} else {
Logger.error(e.message, this._logTag);
}
}
private onErrorCallback(errorMessage: string, errorCode?: string): void {
var errorInfo: ErrorInfo = {
ErrorCode: errorCode ?? ErrorCodes.ERRUNKNOWN,
ErrorMessage: errorMessage
}
try {
var error = JSON.parse(errorMessage);
errorInfo.ErrorMessage = error.Data;
if (error.Data === ErrorMessages.timeoutExceeded) {
this.stopListening();
Logger.error("Recording time limit exceeded", this._logTag);
errorInfo.ErrorCode = ErrorCodes.WSJOB05
} else if (error.Data === ErrorMessages.invalidUserDetails) {
if (this._isRecording) {
this.recorderIns?.stopAudio(false, true);
} else {
this.forceStopAudio = true; //required to stop mic if there is delay in starting microphone
}
errorInfo.ErrorCode = ErrorCodes.WSAUTH03
} else if (error.Data.includes(ErrorMessages.unableTofetchAccount)) {
errorInfo.ErrorCode = ErrorCodes.WSAUTH04
} else if (error.Data.includes(ErrorMessages.unableToFetchJob)) {
errorInfo.ErrorCode = ErrorCodes.WSJOB01
} else if (error.Data.includes(ErrorMessages.jobNotConnectable)) {
errorInfo.ErrorCode = ErrorCodes.WSJOB02
} else if (error.Data.includes(ErrorMessages.rateLimitExceeded)) {
errorInfo.ErrorCode = ErrorCodes.WSACC01
} else if (error.Data.includes(ErrorMessages.weakAudioSignal)) {
errorInfo.ErrorCode = ErrorCodes.WSAUD01
}
} catch (ex) { }
if (errorInfo.ErrorMessage?.includes(ErrorMessages.jobIdEmpty)) {
errorInfo.ErrorCode = ErrorCodes.SDK01
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.pageSizeEmpty)) {
errorInfo.ErrorCode = ErrorCodes.SDK02
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.noteDataEmpty)) {
errorInfo.ErrorCode = ErrorCodes.SDK03
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.jobNameEmpty)) {
errorInfo.ErrorCode = ErrorCodes.SDK04
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.patientContextEmpty)) {
errorInfo.ErrorCode = ErrorCodes.SDK05
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.micPermissionDenied)) {
errorInfo.ErrorCode = ErrorCodes.SDK06
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.micMuted)) {
errorInfo.ErrorCode = ErrorCodes.SDK07
this.recorderIns?.stopAudio(false, true);
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.audioUnplugged)) {
errorInfo.ErrorCode = ErrorCodes.SDK08
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.invalidSubscription) || errorInfo.ErrorMessage?.includes(ErrorMessages.invalidRequest)) {
errorInfo.ErrorCode = ErrorCodes.AUTH01
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.activeSeatNotAvailable)) {
errorInfo.ErrorCode = ErrorCodes.AUTH02
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.noInternetConnection)) {
errorInfo.ErrorCode = ErrorCodes.SDK12
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.transcriptionNotEnabled)) {
errorInfo.ErrorCode = ErrorCodes.SDK13
} else if (errorInfo.ErrorMessage?.includes(ErrorMessages.feedbackNotEnabled)) {
errorInfo.ErrorCode = ErrorCodes.SDK14
}
if (this.onError) {
this.onError(errorInfo);
}
}
private onOtherResultsCallback(data: string): void {
if (this.onOtherResult) {
this.onOtherResult(data);
}
}
private onIntensityCallback(intensity: number): void {
if (this.onIntensityValue) {
this.onIntensityValue(intensity);
}
}
private onIdleMicCallback(): void {
if (this.onIdleMic) {
this.onIdleMic();
}
}
private onSpeechResponseCallback(data: string): void {
var json = JSON.parse(data);
if (json.Type.toLowerCase() === "note" && this.onSoapNoteGenerated) {
this.onSoapNoteGenerated(json.Data);
}
else if (json.Type.toLowerCase() === "transcript" && this.onTranscriptGenerated) {
this.onTranscriptGenerated(json.Data);
} else if (json.Type.toLowerCase() === "codes" && this.onCodesGenerated) {
this.onCodesGenerated(json.Data);
}
}
private onJobStatusEventCallback(status: string): void {
// can add callbacks for job status events such as job completion, job in progress etc here
if (this.onJobStatus) {
this.onJobStatus(status);
}
}
private onSessionEventCallback(data: string | AugnitoSocketResponse): void {
if (!data) {
return;
}
let json: any;
try {
json = typeof data === "string" ? JSON.parse(data) : data;
} catch (error) {
Logger.error(`Error parsing session event data: ${error}`, this._logTag);
return;
}
if (!json || !("Type" in json)) {
return;
}
if (json.Type.toLowerCase() === "meta" && this.config?.onMetaEvent) {
if (!json.JobID) {
Logger.error(`JobID is missing in meta event`, this._logTag);
return;
}
this.config.onMetaEvent(json.JobID);
} else if (json.Type.toLowerCase() === "error" && json.Data) {
this.onErrorCallback(json.Data);
}
if (typeof json.Event !== "object" || json.Event === null) {
return;
}
const { Type: eventType, Value: eventValue } = json.Event;
if (!eventType) {
return;
}
if (eventType === "SESSION_CREATED" && eventValue) {
Logger.log(`session Token ${eventValue}`, this._logTag);
} else if (eventType === "SERVICE_DOWN") {
Logger.error(eventType, this._logTag);
} else if (eventType === "NO_DICTATION_STOP_MIC") {
Logger.log("NO_DICTATION_STOP_MIC", this._logTag);
this.onIdleMicCallback();
} else if (eventType === "INVALID_AUTH_CREDENTIALS") {
Logger.error("INVALID_AUTH_CREDENTIALS", this._logTag);
} else if (eventType === "LOW_BANDWIDTH") {
Logger.log("LOW_BANDWIDTH: Check internet connection", this._logTag);
}
}
// #endregion
/**
* Validates the Ambient config has all the mandatory fields
* @param config The config sent by the client application
*/
validateConfig(config: AmbientConfig): AmbientConfig {
Guard.Against.NullOrEmpty(config.server, "Server");
Guard.Against.NullOrEmpty(config.subscriptionCode, "SubscriptionCode");
Guard.Against.NullOrEmpty(config.accessKey, "AccessKey");
Guard.Against.NullOrEmpty(config.userTag, "UserTag");
return config;
}
private createSocketConfig(config: AmbientConfig): socketConfig {
const _socketConfig = new socketConfig(config);
// _socketConfig.onStartOfRecording =
// this.onStateChangeCallback.bind(this);
// _socketConfig.onStopOfRecording = this.onStateChangeCallback.bind(this);
_socketConfig.onError = this.onErrorCallback.bind(this);
_socketConfig.onMetaEvent = this.onEventCallback.bind(this);
return _socketConfig;
}
}