Skip to content

Commit

Permalink
resolves #1271 case insensitive model
Browse files Browse the repository at this point in the history
Signed-off-by: Julien Buret <jburet@voyages-sncf.com>
  • Loading branch information
vsct-jburet committed Aug 28, 2021
1 parent 3eccd0c commit 55cd46c
Show file tree
Hide file tree
Showing 22 changed files with 325 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class NewBotComponent implements OnInit {
validate() {
const locale = this.firstFormGroup.value.firstCtrl;
const channel = this.channel.value;
const newApp = new Application("new_assistant", "new_assistant", this.state.user.organization, [], [locale], StateService.DEFAULT_ENGINE, true, true, false, 0.0);
const newApp = new Application("new_assistant", "new_assistant", this.state.user.organization, [], [locale], StateService.DEFAULT_ENGINE, true, true, false, 0.0, false);
this.applicationService.saveApplication(newApp)
.subscribe(app => {
this.applicationService.refreshCurrentApplication(app);
Expand Down
5 changes: 3 additions & 2 deletions nlp/admin/server/src/main/kotlin/AdminVerticle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,13 @@ open class AdminVerticle : WebVerticle() {
if (existingApp != null && existingApp.name != application.name) {
badRequest("Application name cannot be changed")
}
val newApp = saveApplication(
val newApp = saveApplication(
existingApp,
application.toApplication().copy(name = application.name.lowercase())
)
// trigger a full rebuild if nlp engine change
if (appWithSameName?.nlpEngineType != newApp.nlpEngineType) {
if (appWithSameName?.nlpEngineType != newApp.nlpEngineType
|| appWithSameName.caseInsensitive != newApp.caseInsensitive) {
front.triggerBuild(ModelBuildTrigger(newApp._id, true))
}
ApplicationWithIntents(newApp, front.getIntentsByApplicationId(newApp._id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ data class ApplicationWithIntents(
* Unknown intent threshold level.
*/
val unknownIntentThreshold: Double = 0.0,
/**
* Case insensitive model - sentences are persisted with lower case.
*/
val caseInsensitive: Boolean = false,
/**
* The id of the app.
*/
Expand All @@ -85,6 +89,7 @@ data class ApplicationWithIntents(
application.useEntityModels,
application.supportSubEntities,
application.unknownIntentThreshold,
application.caseInsensitive,
application._id
)

Expand All @@ -101,6 +106,7 @@ data class ApplicationWithIntents(
useEntityModels,
supportSubEntities,
unknownIntentThreshold,
caseInsensitive,
_id ?: newId()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
</mat-form-field>
</p>
<p>
<nb-checkbox class="separator" [(ngModel)]="application.caseInsensitive"
nbTooltip="If selected, this option uses case insensitive models">
Case insensitive models
</nb-checkbox>
<nb-checkbox class="separator" [(ngModel)]="application.mergeEngineTypes"
nbTooltip="If selected, this option uses built-in entity models (like dates) in conjunction with standard NER">
Use entity models
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class ApplicationComponent implements OnInit {
}
} else {
this.newApplication = true;
this.application = new Application("", "", this.state.user.organization, [], [], StateService.DEFAULT_ENGINE, true, true, false, 0.0);
this.application = new Application("", "", this.state.user.organization, [], [], StateService.DEFAULT_ENGINE, true, true, false, 0.0, false);
}
this.nlpEngineType = this.application.nlpEngineType.name;
if (this.application) {
Expand Down
2 changes: 2 additions & 0 deletions nlp/admin/web/src/app/model/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class Application {
public useEntityModels: boolean,
public supportSubEntities: boolean,
public unknownIntentThreshold: number,
public caseInsensitive: boolean,
public _id?: string) {
}

Expand All @@ -44,6 +45,7 @@ export class Application {
this.useEntityModels,
this.supportSubEntities,
this.unknownIntentThreshold,
this.caseInsensitive,
this._id)
}

Expand Down
30 changes: 19 additions & 11 deletions nlp/core/service/src/main/kotlin/ModelCoreService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import ai.tock.nlp.core.quality.TestContext
import ai.tock.nlp.core.quality.TestModelReport
import ai.tock.nlp.core.sample.SampleEntity
import ai.tock.nlp.core.sample.SampleExpression
import ai.tock.nlp.core.service.ModelCoreService.formatExpressions
import ai.tock.nlp.model.EntityBuildContext
import ai.tock.nlp.model.EntityBuildContextForIntent
import ai.tock.nlp.model.EntityBuildContextForSubEntities
Expand Down Expand Up @@ -72,7 +73,7 @@ internal object ModelCoreService : ModelCore {
override fun updateIntentModel(context: BuildContext, expressions: List<SampleExpression>) {
val nlpContext = IntentContext(context)
if (!context.onlyIfNotExists || !nlpClassifier.isIntentModelExist(nlpContext)) {
nlpClassifier.buildAndSaveIntentModel(nlpContext, expressions)
nlpClassifier.buildAndSaveIntentModel(nlpContext, context.formatExpressions(expressions))
}
}

Expand Down Expand Up @@ -102,7 +103,7 @@ internal object ModelCoreService : ModelCore {
if (!context.onlyIfNotExists ||
!nlpClassifier.isEntityModelExist(nlpContext)
) {
nlpClassifier.buildAndSaveEntityModel(nlpContext, expressions)
nlpClassifier.buildAndSaveEntityModel(nlpContext, context.formatExpressions(expressions))
}
}

Expand All @@ -123,16 +124,15 @@ internal object ModelCoreService : ModelCore {
val startDate = Instant.now()

val intentContext = IntentContext(context)
val intentModel = nlpClassifier.buildIntentModel(intentContext, modelExpressions)
val intentModel = nlpClassifier.buildIntentModel(intentContext, context.formatExpressions(modelExpressions))
val entityModels = modelExpressions
.asSequence()
.groupBy { it.intent }
.mapNotNull { (intent, expressions)
->
try {
intent to nlpClassifier.buildEntityModel(
EntityBuildContextForIntent(context, intent),
expressions
context.formatExpressions(expressions)
)
} catch (e: Exception) {
logger.error { "entity model build fail for $intent " }
Expand Down Expand Up @@ -185,13 +185,13 @@ internal object ModelCoreService : ModelCore {
)
}
} ||
entities.any {
expectedEntities.none { e ->
it.role == e.definition.role && it.entityType == e.definition.entityType && it.isSameRange(
e
)
entities.any {
expectedEntities.none { e ->
it.role == e.definition.role && it.entityType == e.definition.entityType && it.isSameRange(
e
)
}
}
}
}

override fun getCurrentModelConfiguration(
Expand All @@ -205,4 +205,12 @@ internal object ModelCoreService : ModelCore {
engineType: NlpEngineType,
configuration: NlpApplicationConfiguration
) = nlpClassifier.updateModelConfiguration(applicationName, engineType, configuration)

private fun BuildContext.formatExpressions(expressions: List<SampleExpression>): List<SampleExpression> =
if (application.caseInsensitive) expressions.map { e -> e.copy(text = e.text.lowercase(language)) }
else expressions

private fun TestContext.formatExpressions(expressions: List<SampleExpression>): List<SampleExpression> =
if (callContext.application.caseInsensitive) expressions.map { e -> e.copy(text = e.text.lowercase(callContext.language)) }
else expressions
}
32 changes: 22 additions & 10 deletions nlp/core/service/src/main/kotlin/NlpCoreService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ internal object NlpCoreService : NlpCore {
text: String,
intentSelector: IntentSelector
): ParsingResult {
val t = checkMaxLengthAllowed(text)
val t = context.prepareText(checkMaxLengthAllowed(text))
return parse(
context,
t,
Expand All @@ -83,16 +83,17 @@ internal object NlpCoreService : NlpCore {
intentModelHolder: ModelHolder,
entityModelHolders: Map<Intent, ModelHolder?>
): ParsingResult {
val t = context.prepareText(text)
return parse(
context.callContext,
text,
{ nlpClassifier.classifyIntent(IntentContext(context), intentModelHolder, text) },
t,
{ nlpClassifier.classifyIntent(IntentContext(context), intentModelHolder, t) },
{ intent ->
entityModelHolders[intent]?.let { entityModel ->
nlpClassifier.classifyEntities(
EntityCallContextForIntent(context, intent),
entityModel,
text
t
)
} ?: emptyList()
},
Expand All @@ -109,7 +110,7 @@ internal object NlpCoreService : NlpCore {
): ParsingResult {
try {
val intents = intentClassifier.invoke()
val (intent, probability) = intentSelector.selectIntent(intents) ?: null to null
val (intent, probability) = intentSelector.selectIntent(intents) ?: (null to null)

if (intent == null || probability == null) {
return unknownResult
Expand Down Expand Up @@ -158,14 +159,20 @@ internal object NlpCoreService : NlpCore {
if (classifiedEntityTypes.isNotEmpty()) {
if (context.evaluationContext.mergeEntityTypes) {
val result =
entityMerge.mergeEntityTypes(context, text, intent, evaluatedEntities, classifiedEntityTypes)
entityMerge.mergeEntityTypes(
context,
text,
intent,
evaluatedEntities,
classifiedEntityTypes
)
result to
(evaluatedEntities + classifiedEntityTypes.map { it.toEntityRecognition(it.entityType.name) })
.subtract(result).toList()
(evaluatedEntities + classifiedEntityTypes.map { it.toEntityRecognition(it.entityType.name) })
.subtract(result).toList()
} else {
evaluatedEntities to
classifiedEntityTypes.map { it.toEntityRecognition(it.entityType.name) }
.subtract(evaluatedEntities).toList()
classifiedEntityTypes.map { it.toEntityRecognition(it.entityType.name) }
.subtract(evaluatedEntities).toList()
}
} else {
evaluatedEntities to emptyList()
Expand Down Expand Up @@ -204,4 +211,9 @@ internal object NlpCoreService : NlpCore {
override fun healthcheck(): Boolean {
return entityCore.healthcheck()
}

private fun CallContext.prepareText(text: String): String =
if (application.caseInsensitive) text.lowercase(language) else text

private fun TestContext.prepareText(text: String): String = callContext.prepareText(text)
}
3 changes: 2 additions & 1 deletion nlp/core/shared/src/main/kotlin/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import java.util.Locale
data class Application(
val name: String,
val intents: List<Intent>,
val supportedLocales: Set<Locale>
val supportedLocales: Set<Locale>,
val caseInsensitive: Boolean = false,
) {

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ object ApplicationConfigurationService :
UserNamespaceDAO by userNamespaceDAO,
ApplicationConfiguration {

override fun save(application: ApplicationDefinition): ApplicationDefinition {
if(application.caseInsensitive) {
sentenceDAO.updateCaseInsensitiveSentences(application._id)
}
return applicationDAO.save(application)
}

override fun save(sentence: ClassifiedSentence, user: UserLogin?) {
sentenceDAO.save(sentence.copy(qualifier = user))
}
Expand Down
7 changes: 6 additions & 1 deletion nlp/front/service/src/main/kotlin/ConfigurationRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,12 @@ internal object ConfigurationRepository {
it.entitiesRegexp.mapValues { r -> LinkedHashSet(r.value.map { v -> EntitiesRegexp(v.regexp) }) }
)
}
return Application(applicationDefinition.qualifiedName, intents, applicationDefinition.supportedLocales)
return Application(
applicationDefinition.qualifiedName,
intents,
applicationDefinition.supportedLocales,
applicationDefinition.caseInsensitive
)
}

private fun toEntityWithEntityTypesTree(e: EntityDefinition): Entity? {
Expand Down
3 changes: 2 additions & 1 deletion nlp/front/service/src/main/kotlin/ParserService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ object ParserService : Parser {
language,
search = q,
status = setOf(validated, model),
onlyExactMatch = true
onlyExactMatch = true,
caseInsensitiveExactMatch = application.caseInsensitive
)
)
.sentences
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import java.util.Locale
*/
interface ClassifiedSentenceDAO {

fun updateCaseInsensitiveSentences(applicationId: Id<ApplicationDefinition>)

fun getSentences(
intents: Set<Id<IntentDefinition>>?,
language: Locale?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ data class ApplicationDefinition(
* Unknown intent threshold level.
*/
val unknownIntentThreshold: Double = 0.0,
/**
* Case insensitive model - sentences are persisted with lower case.
*/
val caseInsensitive: Boolean = false,
/**
* The id of the app.
*/
Expand Down
3 changes: 2 additions & 1 deletion nlp/front/shared/src/main/kotlin/config/SentencesQuery.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@ data class SentencesQuery(
*/
val allButUser: String? = null,
val maxIntentProbability: Float = 1f,
val minIntentProbability: Float = 0f
val minIntentProbability: Float = 0f,
val caseInsensitiveExactMatch: Boolean = false,
)
Loading

0 comments on commit 55cd46c

Please sign in to comment.