How can I exract a full sentence using Apache NLPCraft? - nlp

In my model file I am using a macro with a regex extract any space-separated alpha-numeric words to capture an user-input sentence i.e.
macros:
- name: "<GENERIC_INPUT>"
macro: "{//[a-zA-Z0-9 ]+//}"
Then I am trying to capture it as following in the element:
elements:
- id: "prop:title"
description: Set title
synonyms:
- "{set|add} title <GENERIC_INPUT>"
The intent term is as following:
intents:
- "intent=myIntent term(createStory)~{tok_id() == 'prop:createStory'} term(title)~{tok_id() == 'prop:title'}?"
In the Java Model I am correctly capturing the title property:
public NCResult onMatch(
NCIntentMatch ctx,
#NCIntentTerm("createStory") NCToken createStory,
#NCIntentTerm("title") Optional<NCToken> titleList,
{
...
When I run a query against the REST API service the probe is deployed in, I only get the first word of the last element <GENERIC_INPUT> (the regular expression) of the synonym defined as {set|add} title <GENERIC_INPUT> i.e.
HTTP 200 [235ms]
{
"status": "API_OK",
"state": {
"resType": "json",
"mdlId": "Create Story",
"txt": "set title this is my story",
"resMeta": {},
"srvReqId": "GKDY-QLBM-B6TQ-7KYO-KMR8",
"status": "QRY_READY",
"resBody": {
"title": "set title this",
"createStory": true,
},
"usrId": 1,
"intentId": "myIntent"
}
}
In the resBody.title I get set title this rather than the whole string as it should be allowed by the regex i.e. set title this is my story
Any idea why? How can I get it to extract the whole title?
Many thanks

Regex <GENERIC_INPUT> can catch individual token, but not group of tokens.
Please try such way
elements:
- id: "prop:title"
description: "Set title"
synonyms:
- "{set|add} title"
- id: "prop:any"
description: "Set any"
synonyms:
- "//[a-zA-Z0-9 ]+//"
intents:
- "intent=test term(title)={# == 'prop:title'} term(any)={# == 'prop:any'}*"
Callback
#NCIntentRef("test")
#NCIntentSample({
"Set title 1 2",
"Set title a b c"
})
NCResult x(
NCIntentMatch ctx,
#NCIntentTerm("title") NCToken title,
#NCIntentTerm("any") List<NCToken> any) {
System.out.println("title=" + title.getNormalizedText());
System.out.println("any=" + any.stream().map(NCToken::getNormalizedText).collect(Collectors.joining("|")));
return NCResult.text("OK");
}
It should work.
But also please try to drop regex here. It can work too slow and you will have many garbage variants.
You can use one element in intent and extract following words in the callback
Model:
elements:
- id: "prop:title"
description: "Set title"
synonyms:
- "{set|add} title"
intents:
- "intent=test term(title)={# == 'prop:title'}"
Callback:
#NCIntentRef("test")
#NCIntentSample({
"Set title 1 2",
"Set title a b c"
})
NCResult x(
NCIntentMatch ctx,
#NCIntentTerm("title") NCToken title) {
System.out.println("title=" + title.getNormalizedText());
System.out.println("any after=" +
Stream.concat(
ctx.getVariant().getFreeTokens().stream(),
ctx.getVariant().getStopWordTokens().stream()
).sorted(Comparator.comparingInt(NCToken::getStartCharIndex)).
filter(p -> p.getStartCharIndex() > title.getStartCharIndex()).
map(NCToken::getNormalizedText).
collect(Collectors.joining("|"))
);
return NCResult.text("OK");
}
Same result, but without regex.

do you know if the apache nlpcraft provides a built-in method to extract as >>well quoted sentences i.e. 'some sentence like this one'?
There are few workarounds for such request, some of the seem like hacks.
I guess that most straight solution is following:
Make NCCustomParser
public class QuotedSentenceParser implements NCCustomParser {
#Override
public List<NCCustomElement> parse(NCRequest req, NCModelView mdl, List<NCCustomWord> words, List<NCCustomElement> elements) {
String txt = req.getNormalizedText();
if (
txt.charAt(0) == '\'' &&
txt.charAt(txt.length() - 1) == '\'' &&
!txt.substring(1, txt.length() - 1).contains("'")
)
return words.stream().map(
w -> new NCCustomElement() {
#Override
public String getElementId() {
return "qElem";
}
#Override
public List<NCCustomWord> getWords() {
return Collections.singletonList(w);
}
#Override
public Map<String, Object> getMetadata() {
return Collections.emptyMap();
}
}
).collect(Collectors.toList());
return null;
}
}
add configuration (Note, that you have to add qElem dummy element here.. It seems like some bug or unclear feature, I am pretty sure that dynamic definition of this element ID in QuotedSentenceParser must be enough)
elements:
- id: "qElem"
description: "Set title"
synonyms:
- "-"
intents:
- "intent=test term(qElem)={# == 'qElem'}*"
parsers:
- "org.apache.nlpcraft.examples.lightswitch.QuotedSentenceParser"
Usage
#NCIntentRef("test")
#NCIntentSample({
"'Set title a b c'"
})
NCResult x(NCIntentMatch ctx, #NCIntentTerm("qElem") List<NCToken> qElems) {
System.out.println(qElems.stream().map(p -> p.getNormalizedText()).collect(Collectors.joining("|")));
return NCResult.text("OK");
}

Related

Issue in giving plurals in Bixby dialogs

I had a question regarding plurals in dialog.
Let's say I have a structure
structure (MyStructure) {
property (MyConcept) {
type {EnumConcept} max (Many)
}
}
And a Value dialog for it:
dialog (Value) {
match: MyConcept(this)
if (this == 'ABC') {
switch(plural(this)) {
case (One) { template("single1") }
default { template ("plural1") }
}
}
if (this == 'DEF') {
switch(plural(this)) {
case (One) { template("single2") }
default { template ("plural2") }
}
}
}
By using
Code:
#{value(myStructure.myConcept.plural('Many'))}
I am able to get "plural1" or "plural2" when myStructure has below values and size of myConcept is 1:
myStructure = [
{ myConcept: ABC },
{ myConcept: ABC },
{ myConcept: ABC },
{ myConcept: ABC }
]
When size of myConcept is 2 and myStructure has below values,
myStructure = [
{ myConcept: ABC },
{ myConcept: ABC },
{ myConcept: DEF },
{ myConcept: DEF }
]
using the Code:
#{value(myStructure.myConcept.plural('Many'))}
is giving NLG as
"single1 and single2"
What I want in the NLG:
"plural1 and plural2"
Can someone please help us in giving proper plural NLG for each element of the unique "myConcept" present in the list of "myStructure"?
What I want is to apply plurality to each individual value of an array.
size(myStructure.myConcept) = 2.
I want to apply plural to both the values of myConcept.
I do not think in dialogs we have an for-each kind of thing available.
It's a little hard to tell what's going on in the code above. If this answer isn't helpful, consider sharing the full source code somewhere for live debugging.
You can try something like
#{value(myStructure.myConcept.plural(plural(myStructure.myConcept))}
The docs has an example like:
#{concept(restaurantStyle.plural(plural(restaurant)))}
Source: https://bixbydevelopers.com/dev/docs/reference/ref-topics/el-ref#node-evaluation-functions in the plural(node) section.

How do I pass Javascript Object to Bixby's Action Output?

I am building an app that search for anime quotes. So, I have the following data, which is 331 Objects (which I call them 'Quote Cards') long and so it will also be updated in future as I add more and more quotes. The problem I having is in creating concepts for JS's array items such as keywords, and imageTag. and also the the character names which are listed as property. I am also willing to change the output as long as I can keep category, and keywords array items. Those are necessary for my quote cards
[
{
$id: "Cold_Souls_1",
animeTitle: "Fullmetal Alchemist Brotherhood",
animePoster:{
referenceImage: 'https://qph.fs.quoracdn.net/main-qimg-da58c837c7197acf364cb2ada34fc5fb.webp',
imageTags: ["Grey","Yellow","Blue","Metal Body","Machine", "Robot","Yellow Hair Boy"],
},
animeCharacters:{
"Edward Elric": [
{
quote: "A lesson without pain is meaningless. For you cannot gain something without sacrificing something else in return. But once you have recovered it and made it your own... You will gain an irreplaceable Fullmetal heart.",
keywords: ["lesson", "pain", "return", "meaningless", "gain","sacrificing", "recover"],
category: "Life Lesson"
}
]
}
},....................
]
In Bixby, you would model a structure that represents the JSON response.
structure (Anime) {
description (The output of your action)
property (title) {
type(viv.core.Text)
visibility (Private)
}
property (poster){
type(AnimePoster)
visibility (Private)
}
property (characters) {
type (AnimeCharacter)
max (Many)
visibility (Private)
}
}
structure (AnimePoster) {
property (referenceImage) {
type (viv.core.Text)
visibility (Private)
}
property (imageTags) {
type (viv.core.Text)
max (Many)
visibility (Private)
}
}
structure (AnimeCharacter) {
property (name) {
type (viv.core.Text)
visibility (Private)
}
property (quote) {
type (viv.core.Text)
visibility (Private)
}
property (keywords) {
type (viv.core.Text)
max (Many)
visibility (Private)
}
property (category) {
type (viv.core.Text)
visibility (Private)
}
}
In your javascript file, you process the JSON structure of animes
// listOfAnimes is the JSON object described in the question
var animes = [];
listOfAnimes.forEach((anime) => {
var characterNames = Object.keys(anime.animeCharacters);
var characters = [];
Object.keys(anime.animeCharacters).forEach((key) => {
characters.push({
name: key,
quote: anime.animeCharacters[key][0].quote, // * warning, can be many
category: anime.animeCharacters[key][0].category// * warning, can be many
});
});
animes.push( {
$id: anime.$id,
title: anime.title,
characters: characters,
poster: {
referenceImage: anime.animePoster.referenceImage,
imageTags: anime.animePoster.imageTags
},
});
});

How can I pass collected values to a replanned action?

When the user says "read john 3:100", I have a ReadBibleVerse action that matches book:john, chapter:3, verse:100. The endpoint will return a 404, since there is no verse 100.
I want that action to capture the error and replan to a "read chapter" request, passing along book:john and chapter:3.
What I have...
action (ReadBibleVerse) {
collect {
input (book) {…}
input (chapter) { type (ChapterNum) … }
input (verse) {…}
}
output (Scripture) {
throws {
unknown-error {
on-catch {
replan {
dialog ("Unknown verse, trying the chapter.")
intent {
goal: Scripture
route: ReadBibleChapter
}}}}}}}
…what I get is "Unknown verse, trying the chapter. I need a book to continue."
I'm clearly hitting the error and, I believe, being "replanned" to ReadBibleChapter, but I'm also getting "I need a book to continue." because I need to explicitly pass along book and chapter?
I found intent.value, which appears to solve my problem, except I can't seem to find the correct format:
value: ChapterNum
value: ChapterNum (chapter)
value: [namespace].ChapterNum { $expr(chapter) }
more various nonsense
…
This should work value {$expr(chapter)}

How to add a Multi Checkbox List to the editor of a Form-based Element?

I'm trying to build a custom Typeahead Form Element. I have predefined multiple datasets to draw autocomplete suggestions from. When users add my field to a form, I want them to be able to select one or more of these datasets through checkboxes.
Orchard does not seem to have any Shape like this out of the box. By looking at other Form Elements in Orchard.DynamicForms and the SelectList Shape defined in Orchard.Forms.Shapes.EditorShapes.cs, I was able to piece together this working code for the Driver:
public class TypeaheadFieldElementDriver : FormsElementDriver<TypeaheadField> {
private readonly ITokenizer _tokenizer;
private readonly IEnumerable<IDataSet> _dataSets;
public TypeaheadFieldElementDriver(IFormsBasedElementServices formsServices, ITokenizer tokenizer, IEnumerable<IDataSet> _dataSets)
: base(formsServices)
{
_tokenizer = tokenizer;
this._dataSets = _dataSets;
}
...
protected override void DescribeForm(DescribeContext context) {
context.Form("TypeaheadField", factory =>
{
var shape = (dynamic)factory;
var form = shape.Fieldset(
Id: "TypeaheadField",
_Value: shape.Textbox(
Id: "Value",
Name: "Value",
Title: "Value",
Classes: new[] { "text", "medium", "tokenized" },
Description: T("The value of this typeahead field.")),
_DataSetIds: shape.SelectList(
Id: "DataSetIds",
Name: "DataSetIds",
Title: "Remote Datasets",
Multiple: true,
Description: T("The remote datasets to fetch suggestions from.")));
foreach (var dataSet in _dataSets)
{
form._DataSetIds.Items.Add(new SelectListItem { Text = T(dataSet.Description).Text, Value = dataSet.Id });
}
return form;
});
...
}
}
This successfully renders a multiple select field and sort of works for my purposes but I'm not able to completely unselect every option because the one that was selected keeps getting posted. For this reason and just general ease of use, I would prefer to render a list of checkboxes instead but I'm not sure how to proceed.

jqgrid, autocomplete under several input boxes

For jqGrid, how to do autocomplete under several input boxes, namely A, B, C. After the input A, autocomplete values provided by B need to have a reference from input A.
For the dataInit at input B, I can only get the original content of input A, not the current input one.
Any idea or link so that I can pay attention to. Thanks
B/R
Gene Leung
Here is the code:
...
{ name:'order_no',
index:'order_no',
align:"center",
width:80,
editable:true,
editoptions:
{
dataInit: function (elem) {
myAutocomplete(elem, "./autoComplete.php?id=sales_no");
},
dataEvents: [
{ type: 'change',
fn: function(e) {
savedSalesNo = $(this).val();
//console.log( savedSalesNo );
}
}
]
}
},
{ name:'item_no',
index:'item_no',
width:120,
editable:true,
editoptions:
{
dataInit: function (elem) {
myAutocomplete(elem, "./autoComplete.php?id=sales_items&vchr_no=" + savedSalesNo);
}
}
},
... php code: ...
if isset($_GET["term"]))
$maskTP = $_GET['term'];
else
$maskTP = "";
$sWhere = "WHERE 1=1";
if($maskTP!='') {
switch ($_GET["id"]) {
case "sales_no":
$sWhere.= " AND name LIKE '%$maskTP%'";
$sSQL = "SELECT name AS order_no FROM sale_order ". $sWhere ." ORDER BY name";
break;
case "sales_items":
$sWhere.= " AND name LIKE '%$maskTP%'";
$sSQL = "SELECT name AS order_no FROM sale_order ". $sWhere ." ORDER BY name";
break;
}
}
$result = $db->Execute( $sSQL );
Can you post some code snippet it will be helpful.
But looking at your question what i understand is you need to autocomplete in B based on A and so on.
So what you can do is while making ajax request for the B autocomplete check the value A and pass it in your call and perform your business logic.

Resources