eliza-core
Version:
A rendition of ELIZA program engine by Weizenbaum sharable for all javascript environments
410 lines (409 loc) • 17.4 kB
JavaScript
import * as tslib_1 from "tslib";
import { of, concat } from 'rxjs';
import { scan, tap, concatMap, filter } from 'rxjs/operators';
import { Decomp } from './decompo';
import { Reassemble } from './Reassemble';
import * as estring from './estring';
import * as mention from './mention-router';
import { Key } from './key';
import { GotoKey } from './key-goto';
import { buildKeyStack } from './key-stack';
import * as PrePostUtil from './pre-post-utils';
import * as printers from './printers';
import { notEmpty } from './utils';
import { UnexpectedNumberException, UnknownRuleException, GotoLostException, ScriptInterpretingError, DuplicateAnnotateException, InvalidStringException, } from './exceptions';
var printSynonyms = true;
var printKeys = true;
var PRINT_PRE_POST = true;
var PRINT_INITIAL_FINAL = true;
export function loadEliza(script$, keyFilter) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var eliza;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
eliza = new ElizaImpl(keyFilter || (function (key) { return true; }));
return [4, eliza.readScript(script$).toPromise()];
case 1:
_a.sent();
return [2, eliza];
}
});
});
}
export function loadElizaInEnglish(script$) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var eliza;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
eliza = new ElizaImpl(function (key) { return true; }, function (sentence) { return sentence.split(' '); });
return [4, eliza.readScript(script$).toPromise()];
case 1:
_a.sent();
return [2, eliza];
}
});
});
}
var ElizaImpl = (function () {
function ElizaImpl(keyFilter, tokenizer) {
this.keyFilter = keyFilter;
this.tokenizer = tokenizer;
this.mem = [];
this.keys = [];
this.synonyms = [];
this.preList = [];
this.postList = [];
this.annotates = [];
this.tweakList = [];
this.initialStr = 'Hello.';
this.finalStr = 'Goodbye.';
this.quitList = [];
this.lastDecomp = [];
this.lastReasemb = [];
this.finished = false;
}
ElizaImpl.prototype.getInitialStr = function () {
return this.initialStr;
};
ElizaImpl.prototype.isFinished = function () {
return this.finished;
};
ElizaImpl.prototype.collect = function (s) {
var _this = this;
var matchedPattern = [
{
tryMatch: function (testStr) { return estring.match(testStr, '*reasmb: *'); },
onMatched: function (matchedParts) {
if (!_this.lastReasemb) {
throw new ScriptInterpretingError('A Reasmb rule missing decomp rule');
}
_this.lastReasemb.push(new Reassemble(matchedParts[1], _this.lastDecomp[_this.lastDecomp.length - 1]));
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*decomp: *'); },
onMatched: function (matchedParts) {
if (!_this.lastDecomp) {
throw new ScriptInterpretingError('A Decomp rule missing Key Initialization');
}
_this.lastReasemb = [];
var temp = matchedParts[1];
var newMatch = estring.match(temp, '$ *');
_this.lastDecomp.push(new Decomp(newMatch ? newMatch[0] : temp, newMatch ? true : false, _this.lastReasemb));
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*key: * #*'); },
onMatched: function (matchedParts) {
_this.lastDecomp = [];
_this.lastReasemb = null;
var n = 0;
if (matchedParts[2].length > 0) {
n = parseInt(matchedParts[2], 10);
if (isNaN(n)) {
throw new UnexpectedNumberException(matchedParts[2], 'key');
}
}
_this.keys.push(new Key(matchedParts[1], n, _this.lastDecomp));
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*key: *'); },
onMatched: function (matchedParts) {
_this.lastDecomp = [];
_this.lastReasemb = null;
_this.keys.push(new Key(matchedParts[1], 0, _this.lastDecomp));
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*mention: *@* *'); },
onMatched: function (matchedParts) {
var words = JSON.parse("[" + matchedParts[3] + "]");
_this.synonyms.push({
tag: matchedParts[2],
words: words.slice(1),
});
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*annotate: *'); },
onMatched: function (matchedParts) {
var tag = matchedParts[1].trim();
if (tag.length < 1 || _this.annotates.find(function (w) { return w === tag; })) {
throw new DuplicateAnnotateException(tag);
}
_this.annotates.push(tag);
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*pre: * => *'); },
onMatched: function (matchedParts) {
_this.preList.push({ src: JSON.parse(matchedParts[1]), dest: JSON.parse(matchedParts[2]) });
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*post: * => *'); },
onMatched: function (matchedParts) {
_this.postList.push({ src: JSON.parse(matchedParts[1]), dest: JSON.parse(matchedParts[2]) });
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*tweak: * => *'); },
onMatched: function (matchedParts) {
_this.tweakList.push({ src: JSON.parse(matchedParts[1]), dest: JSON.parse(matchedParts[2]) });
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*initial: *'); },
onMatched: function (matchedParts) {
_this.initialStr = matchedParts[1];
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*final: *'); },
onMatched: function (matchedParts) {
_this.finalStr = matchedParts[1];
},
},
{
tryMatch: function (testStr) { return estring.match(testStr, '*quit: *'); },
onMatched: function (matchedParts) {
_this.quitList.push(" " + matchedParts[1] + " ");
},
},
{
tryMatch: function (testStr) {
var parts = estring.match(testStr, '*: *');
if (parts && _this.annotates.indexOf(estring.trim(parts[0])) > -1) {
return parts;
}
return null;
},
onMatched: function (matchedParts) {
var instruction = estring.trim(matchedParts[0]);
if (!_this.lastReasemb) {
throw new ScriptInterpretingError('An annotated Reasmb rule missing decomp rule');
}
_this.lastReasemb.push(new Reassemble(matchedParts[1], _this.lastDecomp[_this.lastDecomp.length - 1], instruction));
},
},
].find(function (_a) {
var tryMatch = _a.tryMatch, onMatched = _a.onMatched;
var matchedParts = tryMatch(s);
if (matchedParts) {
onMatched(matchedParts);
return true;
}
return false;
});
if (!matchedPattern) {
throw new UnknownRuleException(s);
}
};
ElizaImpl.prototype.toJson = function () {
var toPrint = {};
if (printKeys) {
toPrint.keys = this.keys.map(function (k) { return printers.snapshotKey(k); });
}
if (printSynonyms) {
toPrint.mentionRoutes = this.synonyms;
}
if (PRINT_PRE_POST) {
toPrint.preList = this.preList;
toPrint.postList = this.postList;
}
if (PRINT_INITIAL_FINAL) {
toPrint.initial = this.initialStr;
toPrint.final = this.finalStr;
toPrint.quitList = this.quitList.join(' ');
}
return toPrint;
};
ElizaImpl.prototype.processHyperInput = function (s) {
var matchedParts = estring.match(s, '*.*');
while (matchedParts) {
var reply = this.sentence(matchedParts[0]);
if (reply) {
return reply;
}
s = estring.trim(matchedParts[1]);
matchedParts = estring.match(s, '*.*');
}
if (s.length > 0) {
var reply = this.sentence(s);
if (reply) {
return reply;
}
}
var m = this.mem.shift();
if (m) {
return { assembled: { reassembled: m } };
}
var key = this.keys.find(function (k) { return k.getKey() === 'xnone'; });
if (key) {
var context = this.fullyDecompose(key, s);
if (context && context.assembled) {
return context;
}
}
return null;
};
ElizaImpl.prototype.processInput = function (s) {
if (typeof s !== 'string') {
throw new InvalidStringException(s);
}
s = estring.replaceAll(s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
s = estring.replaceAll(s, '@#$%^&*()_-+=~`{[}]|:;<>\\"', ' ');
s = estring.replaceAll(s, ',?!', '...');
s = estring.compress(s);
return this.processHyperInput(s);
};
ElizaImpl.prototype.readScript = function (script$) {
var _this = this;
return concat(script$, of('\n')).pipe(scan(function (_a, chunk) {
var buffer = _a.buffer;
var split = (buffer + chunk).split('\n');
var rest = split.pop();
return { buffer: rest || '', lines: split };
}, { buffer: '', lines: [''] }), concatMap(function (pack) { return pack.lines; }), filter(function (line) { return line.trim().length > 0 && !line.startsWith('%'); }), tap(function (line) { return _this.collect(line); }));
};
ElizaImpl.prototype.sentence = function (s) {
var _this = this;
s = PrePostUtil.translate(this.preList, s).trim();
s = estring.pad(s);
if (this.quitList.indexOf(s) >= 0) {
this.finished = true;
return { assembled: { reassembled: this.finalStr } };
}
if (this.tokenizer) {
var result_1 = null;
return buildKeyStack(this.keys.filter(function (k) { return _this.keyFilter(k.getKey()); }), this.tokenizer(s))
.find(function (key) {
var ctx = _this.fullyDecompose(key, s);
if (ctx) {
result_1 = ctx;
return true;
}
return false;
}) ? result_1 : null;
}
var sortedReplyContexts = this.keys.filter(function (k) { return _this.keyFilter(k.getKey()); })
.map(function (key) { return _this.fullyDecompose(key, s); })
.filter(notEmpty).sort(function (ctxA, ctxB) {
return (ctxA.matches ||
{ slottedTokens: [{ token: s, scopes: {} }] })
.slottedTokens.filter(function (t) { return Object.keys(t.scopes).length < 1; })
.map(function (t) { return t.token; }).join('').length
- (ctxB.matches ||
{ slottedTokens: [{ token: s, scopes: {} }] })
.slottedTokens.filter(function (t) { return Object.keys(t.scopes).length < 1; })
.map(function (t) { return t.token; }).join('').length;
});
return sortedReplyContexts[0] || null;
};
ElizaImpl.prototype.decompose = function (key, s) {
var _this = this;
var ASSEMBLE_FUNC = this.assemble;
return (key.getDecomp() || []).map(function (decomposition) {
var matches = mention.matchDecomposition(_this.synonyms, s, decomposition.getPattern());
if (!matches) {
return null;
}
return {
decomposition: decomposition, matches: matches,
assembled: null,
};
}).filter(notEmpty).sort(function (ctxA, ctxB) {
return (ctxA.matches ||
{ slottedTokens: [{ token: s, scopes: {} }] })
.slottedTokens.filter(function (t) { return Object.keys(t.scopes).length < 1; })
.map(function (t) { return t.token; }).join('').length
- (ctxB.matches ||
{ slottedTokens: [{ token: s, scopes: {} }] })
.slottedTokens.filter(function (t) { return Object.keys(t.scopes).length < 1; })
.map(function (t) { return t.token; }).join('').length;
})
.find(function (ctx) {
ctx.assembled = _this.assemble(ctx.decomposition, ctx.matches.slottedTokens);
if (!ctx.assembled) {
return false;
}
if (ctx.assembled instanceof GotoKey) {
if (ctx.assembled.getKey()) {
return true;
}
}
else {
return true;
}
return false;
}) || null;
};
ElizaImpl.prototype.fullyDecompose = function (key, s) {
var _this = this;
var decomposeCtx = this.decompose(key, s);
var _loop_1 = function () {
if (decomposeCtx.assembled instanceof GotoKey) {
var gotoKey = decomposeCtx.assembled;
decomposeCtx = this_1.decompose(gotoKey, s);
return "continue";
}
if (!decomposeCtx.assembled) {
return { value: null };
}
var assembledResult = decomposeCtx.assembled;
assembledResult.reassembled = PrePostUtil
.translate(this_1.tweakList, assembledResult.reassembled);
Object.keys(assembledResult.annotations).forEach(function (k) {
assembledResult.annotations[k] = PrePostUtil
.translate(_this.tweakList, assembledResult.annotations[k]);
});
return { value: {
decomposition: decomposeCtx.decomposition,
matches: decomposeCtx.matches,
assembled: assembledResult,
} };
};
var this_1 = this;
while (decomposeCtx) {
var state_1 = _loop_1();
if (typeof state_1 === "object")
return state_1.value;
}
return null;
};
ElizaImpl.prototype.assemble = function (d, decomposedSlots) {
var _this = this;
var decomposedTokens = decomposedSlots.map(function (s) { return s.token; });
var rule = d.nextRule();
var assembledResult = rule.assemble(decomposedTokens.map(function (token) { return PrePostUtil.translate(_this.postList, token).trim(); }));
if (assembledResult.gotoKey && assembledResult.gotoKey.length > 0) {
var gotoKey = this.keys.find(function (k) { return k.getKey() === assembledResult.gotoKey; });
if (gotoKey && gotoKey.getKey()) {
return new GotoKey(gotoKey);
}
throw new GotoLostException(rule.getTemplate());
}
if (!assembledResult.result) {
return null;
}
if (d.isMemoryKey()) {
this.mem.push(assembledResult.result);
return null;
}
return {
reassembled: assembledResult.result,
annotations: d.getAnnotates()
.map(function (annotate) { return annotate.assemble(decomposedTokens); })
.reduce(function (reduced, current) {
if (current.annotation) {
reduced[current.annotation] = current.result + '';
}
return reduced;
}, {}),
};
};
return ElizaImpl;
}());