Skip to content

Commit 9aa3de3

Browse files
committed
probabililty operator as % and weighted, allow alias arguments
1 parent f5457ed commit 9aa3de3

6 files changed

Lines changed: 490 additions & 85 deletions

File tree

parser/chatito.pegjs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Start = (ImportFile/TopLevelStatement/CommentLine)+
44
TopLevelStatement = od:(IntentDefinition/SlotDefinition/AliasDefinition) { return od; }
55

66
// ============= Probability operator =============
7-
ProbabilityOperatorDefinition = "*[" probability:BasicKeywordLiteral "]" { return probability; }
7+
ProbabilityOperatorDefinition = "*[" probability:Number percent:"%"? "]" { return `${probability}${percent || ''}`; }
88
// ============= Entities =============
99
EntityOpt = "?"
1010
EntityBody = "[" value:EntityKeywordLiteral "]" { return value }
@@ -45,11 +45,12 @@ SlotDefinition = EOL? o:EntitySlotDefinition EOL
4545
{ return { type: o.type, key: o.value, args: o.args, location: o.location, inner: s, variation: o.variation } }
4646

4747
// Alias
48-
EntityAliasDefinition = "~" value:EntityBody { return { value: value, type: "AliasDefinition", location: location() } }
48+
EntityAliasDefinition = "~" value:EntityBody args:EntityArguments?
49+
{ return { value: value, type: "AliasDefinition", location: location(), args: args } }
4950
OptionalAlias = "~" op:EntityOptionalBody { return { value: op.value, type: "Alias", opt: op.opt } }
5051
AliasDefinition = EOL? o:EntityAliasDefinition EOL
5152
Indent s:IntentAndSlotInnerStatements Dedent
52-
{ return { type: o.type, key: o.value, location: o.location, inner: s } }
53+
{ return { type: o.type, key: o.value, location: o.location, inner: s, args: o.args } }
5354

5455
// ============= Identation =============
5556
Samedent "correct indentation" = s:" "* &{ return s.length === level * STEP; }
@@ -69,7 +70,15 @@ BasicKeywordLiteral "entity name" = v:(t:((!"\r\n")(!"\n")(!"]") .) { return t.j
6970
EntityKeywordLiteral "entity name" = v:(t:((!"\r\n")(!"\n")(!"]")(!"?") .) { return t.join(""); })+ { return v.join(""); }
7071
SlotKeywordLiteral "entity name" = v:(t:((!"\r\n")(!"\n")(!"#")(!"]")(!"?") .) { return t.join(""); })+ { return v.join(""); }
7172

72-
Integer "integer" = [0-9]+ { return parseInt(text(), 10); }
73+
// Number
74+
Number "number" = int frac? { return parseFloat(text()); }
75+
DecimalPoint = "."
76+
Digit1_9 = [1-9]
77+
Digit0_9 = [0-9]
78+
frac = DecimalPoint Digit0_9+
79+
int = zero / (Digit1_9 Digit0_9*)
80+
zero = "0"
81+
7382
EOS "end of sentence" = EOL / EOF
7483
EOL "end of line "= (EOLNonWindows/EOLWindows)+
7584
EOLNonWindows "non windows end of line" = "\n"

spec.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ From the output perspective, a slot is the tag that is added the relevant words
126126
```
127127

128128
Slot entities referenced within sentences, can have `?` symbol at the end of the reference name. (e.g.: @[name?]).
129-
In that context, the `?` symbol means that the slot combination is optional, and could be omitted at generation.
129+
In that context, the `?` symbol means that the slot combination is optional, and could be omitted at generation. The probabilities of being omitted are defined by the number of sentence definitions at the entity. If the entity defines only one sentence, then the probabilities of empty string will be 50%, if the sentences defines 2 sentences, the probabilities of being omitted are 33.3333%, and so on.
130130

131131
Slots provide a particular property at their definitions called variations.
132132

@@ -166,8 +166,7 @@ Alias are just variations of a word and does not generate any tag. By default if
166166
hey
167167
```
168168

169-
Same as with slots, alias references can contain a `?` symbol at the end of the reference name. (e.g.: ~[hi?]).
170-
In that context, the `?` symbol means that the alias combination is optional, and could be omitted at generation.
169+
Same as with slots, alias references can be ommited using a `?` symbol at the end of the reference name. (e.g.: ~[hi?]).
171170

172171
When an alias is referenced inside a slot definition, and it is the only token of the slot sentence, by default the generator will tag the generated alias value as a `synonym` of the alias key name.
173172

@@ -259,7 +258,7 @@ For `even` distribution using the previous example:
259258

260259
#### 2.2.1 - Sentence probability operator
261260

262-
The sentence probability operator is defined by the `*[` symbol at the start of a sentence following by the probability value and `]`. The probability value may be expressed in two ways, as a plain number (considered as weighted probabilty, e.g.: `1`) or as a percentage value (a number ending with `%`, e.g.: `33.3333%`), but once an entity defines a probabilty as either weight or percentage, then all the other sentences for that entity should use the same type. Inconsistencies declaring entity sentence probabilty values should be considered an input error.
261+
The sentence probability operator is defined by the `*[` symbol at the start of a sentence following by the probability value and `]`. The probability value may be expressed in two ways, as a plain number (considered as weighted probabilty, e.g.: `1`) or as a percentage value (a number ending with `%`, e.g.: `33.3333%`), but once an entity defines a probabilty as either weight or percentage, then all the other sentences for that entity should use the same type. Inconsistencies declaring entity sentence probabilty values should be considered an input error and if the value is not a valid integer, float or percentual value, the input shouuld be considered as simple text and not as a sentence probability definition.
263262

264263
NOTE: If the probabilty value is a percentage type, then and the sum of all sentence probabilty operators declared inside the entity definition should never exceed 100.
265264

@@ -281,7 +280,7 @@ The previous example, declares `20%` probabilties for the first sentence. This w
281280
| sentence 3 | 400 | 40% | 35.5556% (400*80/900) |
282281

283282

284-
Here is the same example for probabilty value is a weight:
283+
When probabilty value is a weight with regular distribution, multiply that value with the maximum combinations for that sentence, if distribution is even, that value is the actual weighted probability. E.g.:
285284

286285
```
287286
%[intent with a maximum of 1k combinations]

src/main.ts

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const getVariationsFromEntity = async <T>(
7070
const variationKey = ed.variation ? `#${ed.variation}` : '';
7171
const cacheKey = `${ed.type}-${ed.key}${variationKey}`;
7272
let cacheStats = cache.get(cacheKey) as IStatCache;
73+
let probabilityTypeDefined: 'w' | '%' | null = null;
7374
if (!cacheStats) {
7475
// if the entity is not cache, create an empty cache for it
7576
const counts: IChatitoCache[] = [];
@@ -89,18 +90,32 @@ const getVariationsFromEntity = async <T>(
8990
indexesOfSentencesWithNullProbability.push(definedSentenceProbabilities.length);
9091
definedSentenceProbabilities.push(null);
9192
} else {
92-
const prob = parseInt(c.probability || '', 10);
93-
if (!Number.isInteger(prob)) {
94-
throw new Error(`Probability "${c.probability}" must be an integer value. At ${cacheKey}`);
93+
const p = c.probability || '';
94+
const isPercent = p.slice(-1) === '%';
95+
const setenceProbabilityType = isPercent ? '%' : 'w';
96+
if (probabilityTypeDefined === null) {
97+
probabilityTypeDefined = setenceProbabilityType;
98+
} else if (setenceProbabilityType !== probabilityTypeDefined) {
99+
throw new Error(`All probability definitions for "${cacheKey}" must be of the same type.`);
95100
}
96-
if (prob < 1 || prob > 100) {
97-
throw new Error(`Probability "${c.probability}" must be from 1 to 100. At ${cacheKey}`);
101+
const prob = parseFloat(isPercent ? p.slice(0, -1) : p);
102+
if (isPercent) {
103+
if (!Number.isInteger(prob)) {
104+
throw new Error(`Probability "${p}" must be an integer or float number. At ${cacheKey}`);
105+
}
106+
if (prob <= 0 || prob > 100) {
107+
throw new Error(`Probability "${p}" must be greater than 0 up to 100. At ${cacheKey}`);
108+
}
109+
} else if (setenceProbabilityType === 'w') {
110+
if (prob <= 0) {
111+
throw new Error(`Probability weight "${p}" must be greater than 0. At ${cacheKey}`);
112+
}
98113
}
99114
sumOfTotalProbabilitiesDefined += prob;
100115
definedSentenceProbabilities.push(prob);
101116
}
102117
}
103-
if (sumOfTotalProbabilitiesDefined && sumOfTotalProbabilitiesDefined > 100) {
118+
if (probabilityTypeDefined === '%' && sumOfTotalProbabilitiesDefined && sumOfTotalProbabilitiesDefined > 100) {
104119
throw new Error(
105120
`The sum of sentence probabilities (${sumOfTotalProbabilitiesDefined}) for an entity can't be higher than 100%. At ${cacheKey}`
106121
);
@@ -110,11 +125,20 @@ const getVariationsFromEntity = async <T>(
110125
indexesOfSentencesWithNullProbability.map(i => maxCounts[i]).reduce((p, n) => (p || 0) + (n || 0), 0) || 0;
111126
// calculate the split of remaining probability for sentences that don't define them
112127
// const realProbabilities = maxCounts.map(m => (m * 100) / sumOfTotalMax);
113-
const probabilities = definedSentenceProbabilities.map((p, i) =>
114-
p === null
115-
? (((maxCounts[i] * 100) / totalMaxCountsToShareBetweenNullProbSent) * (100 - sumOfTotalProbabilitiesDefined)) / 100
116-
: p
117-
);
128+
let probabilities: number[];
129+
if (probabilityTypeDefined === '%') {
130+
// if probabilityTypeDefined is percentual, then calculate each sentence chances in percent
131+
probabilities = definedSentenceProbabilities.map((p, i) =>
132+
p === null
133+
? (((maxCounts[i] * 100) / totalMaxCountsToShareBetweenNullProbSent) * (100 - sumOfTotalProbabilitiesDefined)) / 100
134+
: p
135+
);
136+
} else if (probabilityTypeDefined === 'w') {
137+
// if probabilityTypeDefined is weighted, then multiply the weight by max counts
138+
probabilities = definedSentenceProbabilities.map((p, i) => (p === null ? maxCounts[i] : maxCounts[i] * p));
139+
} else {
140+
probabilities = maxCounts;
141+
}
118142
const currentEntityCache: IStatCache = { counts, maxCounts, optional, probabilities };
119143
cache.set(cacheKey, currentEntityCache);
120144
cacheStats = cache.get(cacheKey) as IStatCache;
@@ -228,10 +252,9 @@ export const datasetFromAST = async (
228252
entity = operatorDefinitions.Slot;
229253
} else if (od.type === 'AliasDefinition') {
230254
entity = operatorDefinitions.Alias;
231-
} else if (od.type === 'Comment' || od.type === 'ImportFile') {
232-
return; // skip comments
233255
} else {
234-
throw new Error(`Unknown definition definition for ${od.type}`);
256+
// type is 'Comment' or 'ImportFile'
257+
return; // skip comments
235258
}
236259
const odKey = od.variation ? `${od.key}#${od.variation}` : od.key;
237260
if (entity[odKey]) {

0 commit comments

Comments
 (0)