Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All 0.3 dev odiya #119

Open
wants to merge 5 commits into
base: release-2.0-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/mongodb/config/language/or.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var or_config = {
vowel: ["ା", "ି", "ୀ", "ୁ", "ୂ", "ୃ", "ୄ", "େ", "ୈ", "ୋ", "ୌ", "ઃ"],
language_code: "or"
};

export default or_config;
2 changes: 1 addition & 1 deletion src/mongodb/config/language/ta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ var ta_config = {
language_code: "ta"
};

export default ta_config;
export default ta_config;
290 changes: 290 additions & 0 deletions src/mongodb/scores.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import { AxiosError } from 'axios';
import ta_config from "./config/language/ta";
import en_config from "./config/language/en"
import gu_config from './config/language/gu';
<<<<<<< HEAD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please merge code properly

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved

import hi_config from './config/language/hi';
=======
import or_config from './config/language/or';
>>>>>>> all-0.3-odiya

@ApiTags('scores')
@Controller('scores')
Expand Down Expand Up @@ -615,6 +619,292 @@ export class ScoresController {
}
}

@ApiBody({
description: 'Request body for storing data to the learner profile',
schema: {
type: 'object',
properties: {
original_text: { type: 'string', example: 'ବିଲେଇ' },
audio: { type: 'string', example: 'Add odiya Wav file base64 string here' },
user_id: { type: 'string', example: '8819167684' },
session_id: { type: 'string', example: 'IYmeBW1g3GpJb1AE0fOpHCPhKxJG4zq6' },
language: { type: 'string', example: 'or' },
date: { type: 'string', format: 'date-time', example: '2024-05-07T12:24:51.779Z' },
sub_session_id: { type: 'string', example: '4TsVQ28LWibb8Yi2uJg4DtLK3svIbIHe' },
contentId: { type: 'string', example: 'b70af0e5-0d74-4287-9548-4d491c714b0d' },
contentType: { type: 'string', example: 'word' },
},
},
})
@ApiResponse({
status: 201,
description: 'Success message when data is stored to the learner profile',
schema: {
type: 'object',
properties: {
status: { type: 'string', example: 'success' },
msg: { type: 'string', example: 'Successfully stored data to learner profile' },
responseText: { type: 'string', example: 'ବିଲେଇ' },
subsessionTargetsCount: { type: 'number', example: 17 },
subsessionFluency: { type: 'number', example: 1.54 },
},
},
})
@ApiResponse({
status: 500,
description: 'Error while data is being stored to the learner profile',
schema: {
type: 'object',
properties: {
status: { type: 'string', example: 'error' },
msg: { type: 'string', example: 'Server error - error message' },
},
},
})
@ApiForbiddenResponse({ description: 'Forbidden.' })
@ApiOperation({
summary:
'Store students learner ai profile, from the ASR output for a given wav file. This API will work for odiya',
})
@Post('/updateLearnerProfile/or')
async updateLearnerProfileOr(
@Res() response: FastifyReply,
@Body() CreateLearnerProfileDto: CreateLearnerProfileDto,
) {
try {
const vowelSignArr = or_config.vowel;
const language = or_config.language_code;

let createScoreData;

let asrOutDenoised;
let asrOutBeforeDenoised;

let nonDenoisedresponseText = "";
let DenoisedresponseText = "";

let similarityNonDenoisedText = 0;
let similarityDenoisedText = 0;

let constructTokenArr = [];
let correctTokens = [];
let missingTokens = [];

let reptitionCount = 0;

let confidence_scoresArr = [];
let missing_token_scoresArr = [];
let anomaly_scoreArr = [];

const originalText = CreateLearnerProfileDto.original_text;

let originalTokenArr = await this.scoresService.getSyllablesFromString(originalText, vowelSignArr, language);
let responseText = '';
let constructText = '';

let pause_count = 0;

/* Condition to check whether content type is char or not. If content type is char
dont process it from ASR and other processing related with text evalution matrices and scoring mechanism
*/
if (CreateLearnerProfileDto['contentType'].toLowerCase() !== 'char') {
let audioFile;

if (
CreateLearnerProfileDto['output'] === undefined &&
CreateLearnerProfileDto.audio !== undefined
) {
audioFile = CreateLearnerProfileDto.audio;
const decoded = audioFile.toString('base64');

// Send Audio file to ASR to process and provide vector with char and score
let audioOutput = await this.scoresService.audioFileToAsrOutput(decoded, CreateLearnerProfileDto.language, CreateLearnerProfileDto['contentType']);

asrOutDenoised = audioOutput.asrOutDenoisedOutput?.output || "";
asrOutBeforeDenoised = audioOutput.asrOutBeforeDenoised?.output || "";
pause_count = audioOutput.pause_count || 0;

similarityDenoisedText = await this.scoresService.getTextSimilarity(originalText, asrOutDenoised[0]?.source || "");
similarityNonDenoisedText = await this.scoresService.getTextSimilarity(originalText, asrOutBeforeDenoised[0]?.source || "");

if (similarityDenoisedText <= similarityNonDenoisedText) {
CreateLearnerProfileDto['output'] = asrOutBeforeDenoised;
DenoisedresponseText = asrOutDenoised[0]?.source;
nonDenoisedresponseText = asrOutBeforeDenoised[0]?.source;
} else {
CreateLearnerProfileDto['output'] = asrOutDenoised;
DenoisedresponseText = asrOutDenoised[0]?.source;
nonDenoisedresponseText = asrOutBeforeDenoised[0]?.source;
}

if (CreateLearnerProfileDto.output[0].source === '') {
return response.status(HttpStatus.BAD_REQUEST).send({
status: 'error',
message:
'Audio to Text functionality Responded Empty Response. Please check audio file or speak Loudly',
});
}
}

responseText = CreateLearnerProfileDto.output[0].source;

// Get All hexcode for this selected language
const tokenHexcodeDataArr = await this.scoresService.gethexcodeMapping(language);

// Constructed Logic starts from here
let constructedTextRepCountData = await this.scoresService.getConstructedText(originalText, responseText);
constructText = constructedTextRepCountData.constructText;
reptitionCount = constructedTextRepCountData.reptitionCount;
constructTokenArr = await this.scoresService.getSyllablesFromString(constructText, vowelSignArr, language);

// Comparison Logic for identify correct and missing tokens
for (const originalTokenArrEle of originalTokenArr) {
if (constructTokenArr.includes(originalTokenArrEle)) {
correctTokens.push(originalTokenArrEle);
} else {
missingTokens.push(originalTokenArrEle);
}
}
const missingTokenSet = new Set(missingTokens);
missingTokens = Array.from(missingTokenSet);

let identifyTokens = await this.scoresService.identifyTokens(CreateLearnerProfileDto.output[0].nBestTokens, correctTokens, missingTokens, tokenHexcodeDataArr, vowelSignArr);

confidence_scoresArr = identifyTokens.confidence_scoresArr;
missing_token_scoresArr = identifyTokens.missing_token_scoresArr;
anomaly_scoreArr = identifyTokens.anomaly_scoreArr;

// Send a call to text eval serivce
const textEvalMatrices = await this.scoresService.getTextMetrics(originalText, constructText, language)

if (process.env.denoiserEnabled === "true") {
let improved = false;

let similarityScoreNonDenoisedResText = similarityNonDenoisedText;
let similarityScoreDenoisedResText = similarityDenoisedText;

if (similarityScoreDenoisedResText > similarityScoreNonDenoisedResText) {
improved = true;
}

let createDenoiserOutputLog = {
user_id: CreateLearnerProfileDto.user_id,
session_id: CreateLearnerProfileDto.session_id,
sub_session_id: CreateLearnerProfileDto.sub_session_id || "",
contentType: CreateLearnerProfileDto.contentType,
contentId: CreateLearnerProfileDto.contentId || "",
language: language,
original_text: originalText,
response_text: nonDenoisedresponseText,
denoised_response_text: DenoisedresponseText,
improved: improved,
comment: ""
}

await this.scoresService.addDenoisedOutputLog(createDenoiserOutputLog);
}

// calculate fluencyScore
let fluencyScore = await this.scoresService.getCalculatedFluency(textEvalMatrices, reptitionCount, originalText, responseText, pause_count);

let createdAt = new Date().toISOString().replace('Z', '+00:00')

createScoreData = {
user_id: CreateLearnerProfileDto.user_id, // userid sent by client
session: {
session_id: CreateLearnerProfileDto.session_id, // working logged in session id
sub_session_id: CreateLearnerProfileDto.sub_session_id || '', // used to club set recorded data within session
contentType: CreateLearnerProfileDto.contentType, // contentType could be Char, Word, Sentence and Paragraph
contentId: CreateLearnerProfileDto.contentId || '', // contentId of original text content shown to user to speak
createdAt: createdAt,
language: language, // content language
original_text: CreateLearnerProfileDto.original_text, // content text shown to speak
response_text: responseText, // text return by ai after converting audio to text
construct_text: constructText, // this will be constructed by matching response text with original text.
confidence_scores: confidence_scoresArr, // confidence score array will include char's has identified by ai and has score
anamolydata_scores: anomaly_scoreArr, // this char's recognise as noise in audio
missing_token_scores: missing_token_scoresArr, // this char's missed to spoke or recognise by ai
error_rate: {
character: textEvalMatrices.cer,
word: textEvalMatrices.wer,
},
count_diff: {
character: Math.abs(
CreateLearnerProfileDto.original_text.length -
CreateLearnerProfileDto.output[0].source.length,
),
word: Math.abs(
CreateLearnerProfileDto.original_text.split(' ').length -
CreateLearnerProfileDto.output[0].source.split(' ').length,
),
},
eucledian_distance: {
insertions: {
chars: textEvalMatrices.insertion,
count: textEvalMatrices.insertion.length,
},
deletions: {
chars: textEvalMatrices.deletion,
count: textEvalMatrices.deletion.length,
},
substitutions: {
chars: textEvalMatrices.substitution,
count: textEvalMatrices.substitution.length,
},
},
fluencyScore: fluencyScore.toFixed(3),
silence_Pause: {
total_duration: 0,
count: pause_count,
},
reptitionsCount: reptitionCount,
asrOutput: JSON.stringify(CreateLearnerProfileDto.output),
isRetry: false,
},
};

// For retry attempt detection
const retryAttempt = await this.scoresService.getRetryStatus(
CreateLearnerProfileDto.user_id,
CreateLearnerProfileDto.contentId,
);

// Store Array to DB
const data = await this.scoresService.create(createScoreData);
}

// Cal the subsessionWise and content_id wise target.
let targets = await this.scoresService.getTargetsBysubSession(
CreateLearnerProfileDto.sub_session_id,
CreateLearnerProfileDto.language,
);

let originalTextSyllables = [];
originalTextSyllables = await this.scoresService.getSubsessionOriginalTextSyllables(CreateLearnerProfileDto.sub_session_id);
targets = targets.filter((targetsEle) => { return originalTextSyllables.includes(targetsEle.character) });
const totalTargets = targets.length;

const fluency = await this.scoresService.getFluencyBysubSession(
CreateLearnerProfileDto.sub_session_id,
CreateLearnerProfileDto.language,
);

return response.status(HttpStatus.CREATED).send({
status: 'success',
msg: 'Successfully stored data to learner profile',
originalText: originalText,
responseText: responseText,
subsessionTargetsCount: totalTargets,
subsessionFluency: parseFloat(fluency.toFixed(2))
});
} catch (err) {
return response.status(HttpStatus.INTERNAL_SERVER_ERROR).send({
status: 'error',
message: 'Server error - ' + err,
});
}
}


@ApiBody({
description: 'Request body for storing data to the learner profile',
Expand Down
7 changes: 5 additions & 2 deletions src/mongodb/scores.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ export class ScoresService {
case "te":
serviceId = "ai4bharat/conformer-multilingual-dravidian--gpu-t4";
break;
case 'or':
serviceId = 'ai4bharat/conformer-multilingual-indo-aryan--gpu-t4';
break;
default:
serviceId = `ai4bharat/conformer-${language}-gpu--t4`;
}
Expand Down Expand Up @@ -2293,13 +2296,13 @@ export class ScoresService {

async getTextMetrics(original_text: string, response_text: string, language: string) {
const url = process.env.ALL_TEXT_EVAL_API + "/getTextMatrices";

const textData = {
reference: original_text,
hypothesis: response_text,
language: language,
};

const textEvalMatrices = await lastValueFrom(
this.httpService
.post(url, JSON.stringify(textData), {
Expand Down