Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More β†’
Socket
Sign inDemoInstall
Socket

articulate-nlg

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

articulate-nlg - npm Package Compare versions

Comparing version 1.0.3 to 1.0.4

.travis.yml

145

dist/index.d.ts

@@ -1,143 +0,6 @@

/**
* A Persona can articulate concepts by generating speech as strings containing text. This speech logic is defined by
* a "core" that's provided to the Persona on construction.
*/
export default class Persona {
core: ICore;
/**
* Construct a new Persona using the provided core.
*
* @param core The core for this persona. A core contains all concepts and the resolvers that generate text for them.
*/
constructor(core: ICore);
/**
* Generates text using the generator provided. Don't call this directly on a persona.
*
* @param generator The generator to create text from.
* @param context Optional context object used by the generator to pull text from.
*/
private generateTextForGenerator;
/**
* Articulates the provided concept, returning the generated speech as a string containing text. Providing `--help` articulates
* all concept names this persona can articulate. You can call `articulateHelp()` directly as well.
*
* The concept string represents a "thought" that is being
* articulated by the persona, and the resulting string returned is the generated text that represents the concept.
*
* A concept may be articulated in a variety of ways, so the returned string
* is expected to vary, but will still convey the concept in one way or another.
*
* A context object can be provided for personas that expect one. These can contain properties
* to be expressed during articulation. For instance, a particular persona may expect
* a firstName property, a zodiacSign property, birthDate property, etc. In cases where
* such an expectation exists, check the core's documentation.
*
* @param conceptName The name of the concept to articulate as a string.
* @param context An optional context Object containing properties expected by a persona, such as firstName.
*
* @returns The speech text articulated by this persona for the concept provided. This can (and most
* likely will) be different every time for a particular concept.
*/
articulate: (conceptName: string, context?: any, seed?: any) => string;
/**
* Articulates the names of the concepts this persona can articulate.
*/
articulateHelp: () => string;
/**
* Returns the names of all concepts this persona can articulate.
*
* @return A string array containing the names of all concepts this persona can articulate.
*/
getConceptNames: () => string[];
/**
* Returns the persona's core. Handle with care :)
*/
getCore: () => ICore;
/**
* Sets the persona's core.
* @param core The new core.
*/
setCore: (core: ICore) => void;
vocab: Object;
core: Object;
constructor(vocab?: Object, core?: Object);
articulate: (template: string, params?: {}) => string;
}
/**
* A core contains all concepts for a Persona. Think of it as the brain!
*
* Each concept name in the conceptResolvers property maps to either a single resolver, a single string as the generated text,
* or a list of resolvers/text. Resolvers provide the generators that are used to generate text.
*
* Resolvers/text for a particular concept are randomly chosen during articulation. The selection is based on
* weights for each resolver provided for that concept. Weights default to 1, and if no weights are provided, all resolvers for that
* concept are equally likely to occur. If strings are provided, the weights for those default to 1 as well. If only
* one resolver (or just a string of text) is provided, it's always selected.
*
* For instance, the "greet" concept might map to a list, `["hello", "hi", "hey"]`. Since these are just strings,
* articulating "greet" would generate text by randomly selecting one of those three strings, and all would be
* equally likely to occur since their weights are all 1 by default (1/3 chance each).
*
* If the list were something like: `[{do: {text: "hello"}, weight: 18}, "hi", "hey"]`, then "hello" would have
* an 18/20 chance of being selected, while "hi" and "hey" would each have a 1/20 chance.
*
* There is a resolver that articulates other concepts, making it possible to reuse them. For instance, a `greet`
* concept could be reused in a `how-was-your-day` concept, which could start by articulating `greet` and
* then asking how someone's day was. The `greet` concept might resolve in a dozen different ways, making the greeting
* different every time. Layer your concepts like this and your speech will seem much more organic!
*/
export interface ICore {
/** Concept names mapped to their resolvers, or to strings representing generated text. */
conceptResolvers: {
[conceptName: string]: (IResolver | string)[] | IResolver | string;
};
/**
* Used when articulating `--help` or when calling `articulateHelp()`. This text will be generated
* followed by a list of concept names that can be articulated. Specify this if your core is not written in English.
*/
helpText?: string;
}
/**
* A resolver generates text for a concept using generators.
*
* Each resolver contains a `do` property mapped to a single generator, or list of generators, to use when articulating a concept.
* If a list of generators is provided, each one generates text and the results of each are concatenated.
*
* The value of `do` may also just be a string which will itself will become the text generated.
*
* A resolver also contains an optional weight that determines how likely it is that this resolver
* is chosen when it is adjacent to other resolvers in a list. If a weight is not provided, the weight defaults to 1.
*/
export interface IResolver {
/**
* A generator that resolves the text, a list of generators or strings to concatenate text from, or a string containing the text itself.
*/
do: (IGenerator | string)[] | IGenerator | string;
/** When paired with other resolvers, a higher weight makes this resolver more likely to be chosen. Defaults to 1 if not provided. */
weight?: number;
}
/**
* A generator directs the actual text used during speech generation.
*
* - `text?` - The text generated. Overrides `articulate` and `contextProp`.
* - `articulate?` - Articulates another concept and uses the text it generates. Be careful to avoid infinite referential loops. Overrides `contextProp`.
* - `contextProp?` - Value of a property specified in the context object (passed in when calling `articulate()`).
* - `contextDefault?` - Fallback value used when `contextProp` property is undefined in the context object. If a default is not specifed and a context property is missing, the name of the property is generated between angle brackets, like so: `<contextProp>`.
*
* If none of the above are provided, the generator creates the text `<unknown generator>`.
*
* Options:
*
* - `capitalize?` - When true, the first letter of the text will be capitalized. Default to false.
*/
export interface IGenerator {
/** Generates the text provided. */
text?: string;
/** Generates speech for another concept. */
articulate?: string;
/** Generates the value of the provided context property. */
contextProp?: string;
/**
* Default when a `contextProp` is specified but undefined.
* For instance, if a `name` context property is missing, you could default it to "friend".
*/
contextDefault?: string;
/** Capitalizes the first letter of the generated text. Default false. */
capitalize?: boolean;
}
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var mustache_1 = __importDefault(require("mustache"));
var chooser = require("random-seed-weighted-chooser").default;
/**
* A Persona can articulate concepts by generating speech as strings containing text. This speech logic is defined by
* a "core" that's provided to the Persona on construction.
*/
var Persona = /** @class */ (function () {
/**
* Construct a new Persona using the provided core.
*
* @param core The core for this persona. A core contains all concepts and the resolvers that generate text for them.
*/
function Persona(core) {
var _this = this;
this.core = core;
/**
* Generates text using the generator provided. Don't call this directly on a persona.
*
* @param generator The generator to create text from.
* @param context Optional context object used by the generator to pull text from.
*/
this.generateTextForGenerator = function (generator, context, seed) {
if (context === void 0) { context = {}; }
if (seed === void 0) { seed = Math.random(); }
var text = "<unknown generator>";
if (generator.text) {
text = generator.text;
}
else if (generator.articulate) {
text = _this.articulate(generator.articulate, context, seed);
}
else if (generator.contextProp) {
var value = context[generator.contextProp];
value = value ? value : generator.contextDefault;
text = value ? value : "<" + generator.contextProp + ">";
}
if (generator.capitalize) {
text = text.charAt(0).toUpperCase() + text.slice(1);
}
return text;
var defaultCore = {
capitalize: function () {
return function (text, render) {
var renderedText = render(text);
return renderedText.charAt(0).toUpperCase() + renderedText.slice(1);
};
/**
* Articulates the provided concept, returning the generated speech as a string containing text. Providing `--help` articulates
* all concept names this persona can articulate. You can call `articulateHelp()` directly as well.
*
* The concept string represents a "thought" that is being
* articulated by the persona, and the resulting string returned is the generated text that represents the concept.
*
* A concept may be articulated in a variety of ways, so the returned string
* is expected to vary, but will still convey the concept in one way or another.
*
* A context object can be provided for personas that expect one. These can contain properties
* to be expressed during articulation. For instance, a particular persona may expect
* a firstName property, a zodiacSign property, birthDate property, etc. In cases where
* such an expectation exists, check the core's documentation.
*
* @param conceptName The name of the concept to articulate as a string.
* @param context An optional context Object containing properties expected by a persona, such as firstName.
*
* @returns The speech text articulated by this persona for the concept provided. This can (and most
* likely will) be different every time for a particular concept.
*/
this.articulate = function (conceptName, context, seed) {
if (context === void 0) { context = {}; }
if (seed === void 0) { seed = Math.random(); }
var text = "<" + conceptName + ">";
if (_this.core.conceptResolvers[conceptName]) {
var arrayOrResolverOrString = _this.core.conceptResolvers[conceptName];
var selectedResolverOrString = undefined;
// If the concept maps to an array of possibilities...
if (Array.isArray(arrayOrResolverOrString)) {
// We want to select a random item based on the weights.
var weights = arrayOrResolverOrString.map(function (stringOrResolver) {
return typeof stringOrResolver !== "string" && stringOrResolver.weight
? stringOrResolver.weight
: 1;
},
choose: function () {
return function (text, render) {
var segments = text.split("|");
var segmentsWithWeights = [];
var regex = /(.*)[=](\d+)/;
segments.forEach(function (segment) {
var match = segment.match(regex);
if (match !== null && match.length >= 2) {
segmentsWithWeights.push({
value: match[1],
weight: parseInt(match[2])
});
var selectedIndex = chooser.chooseWeightedIndex(weights, seed);
selectedResolverOrString = arrayOrResolverOrString[selectedIndex];
}
// Otherwise it's a resolver or a string!
else {
selectedResolverOrString = arrayOrResolverOrString;
segmentsWithWeights.push({ value: segment, weight: 1 });
}
if (selectedResolverOrString) {
// If it's a string, that's a shortcut for text generation
// using that string.
if (typeof selectedResolverOrString === "string") {
text = selectedResolverOrString;
}
// Otherwise, it's a resolver! This has a `do` property that contains generators
// or text to be concatenated together. We're almost there...
else {
var textOrArrayOfGenerators = selectedResolverOrString.do;
// If it's an array, concatenate all the generated text together.
if (Array.isArray(textOrArrayOfGenerators)) {
text = textOrArrayOfGenerators.reduce(function (cumulative, currentItem) {
if (typeof currentItem === "string") {
return cumulative + currentItem;
}
else {
return (cumulative +
_this.generateTextForGenerator(currentItem, context, seed));
}
}, "");
}
// If it's a string, use this as the generated text.
else if (typeof textOrArrayOfGenerators === "string") {
text = textOrArrayOfGenerators;
}
// Otherwise, use the generator to generate the text.
else {
text = _this.generateTextForGenerator(textOrArrayOfGenerators, context, seed);
}
}
});
var chosen = chooser.chooseWeightedObject(segmentsWithWeights);
var renderedText = render(chosen.value);
return renderedText;
};
}
};
var Persona = /** @class */ (function () {
function Persona(vocab, core) {
var _this = this;
if (vocab === void 0) { vocab = {}; }
if (core === void 0) { core = defaultCore; }
this.vocab = vocab;
this.core = core;
this.articulate = function (template, params) {
if (params === void 0) { params = {}; }
var coreToUse = __assign({}, _this.core, { params: __assign({}, params) });
var vocabToUse = _this.vocab;
var result = mustache_1.default.render(template, coreToUse, vocabToUse);
// See if they just provided the name of a partial with no curly braces.
// If so, wrap it in curly braces and attempt to render the partial.
if (result === template &&
result.indexOf("{{") < 0 &&
result.indexOf("}}") < 0) {
var partial = "{{>" + template + "}}";
var resultUsingPartial = mustache_1.default.render("{{>" + template + "}}", coreToUse, vocabToUse);
if (resultUsingPartial !== "" && resultUsingPartial !== partial) {
result = resultUsingPartial;
}
}
else if (conceptName === "--help") {
text = _this.articulateHelp();
}
return text;
return result;
};
/**
* Articulates the names of the concepts this persona can articulate.
*/
this.articulateHelp = function () {
var keys = Object.keys(_this.core.conceptResolvers);
var text = _this.core.helpText
? "🀷"
: "My core is empty! I can't articulate any concepts. 🀷";
if (keys.length > 0) {
text =
(_this.core.helpText
? _this.core.helpText
: "I can articulate the following concepts:") +
"\n" +
keys.map(function (key) { return "- " + key; }).join("\n");
}
return text;
};
/**
* Returns the names of all concepts this persona can articulate.
*
* @return A string array containing the names of all concepts this persona can articulate.
*/
this.getConceptNames = function () { return Object.keys(_this.core.conceptResolvers); };
/**
* Returns the persona's core. Handle with care :)
*/
this.getCore = function () { return _this.core; };
/**
* Sets the persona's core.
* @param core The new core.
*/
this.setCore = function (core) {
_this.core = core;
};
}

@@ -160,0 +75,0 @@ return Persona;

{
"name": "articulate-nlg",
"version": "1.0.3",
"version": "1.0.4",
"description": "A natural language generator (NLG) that articulates concepts as words, phrases, and sentences.",

@@ -9,5 +9,6 @@ "main": "./dist/index.js",

"deploy": "npm run build && npm publish && git push",
"build": "tsc",
"build": "rm -rf ./dist && tsc",
"watch": "tsc --watch",
"test": "node ./dist/test.js"
"test": "jest",
"report-coverage": "codecov"
},

@@ -40,8 +41,14 @@ "repository": {

"devDependencies": {
"typescript": "^3.4.1",
"@types/node": "^11.13.0"
"@types/jest": "^24.0.11",
"@types/mustache": "^0.8.32",
"@types/node": "^11.13.7",
"codecov": "^3.3.0",
"jest": "^24.7.1",
"ts-jest": "^24.0.2",
"typescript": "^3.4.1"
},
"dependencies": {
"random-seed-weighted-chooser": "^1.0.9"
"mustache": "^3.0.1",
"random-seed-weighted-chooser": "^1.0.12"
}
}

@@ -0,1 +1,3 @@

[![Build Status](https://travis-ci.org/justinmahar/articulate-nlg.svg?branch=master)](https://travis-ci.org/justinmahar/articulate-nlg) [![codecov](https://codecov.io/gh/justinmahar/articulate-nlg/branch/master/graph/badge.svg)](https://codecov.io/gh/justinmahar/articulate-nlg)
# Articulate NLG

@@ -7,30 +9,2 @@

## Table Of Contents
- [Articulate NLG](#articulate-nlg)
- [Table Of Contents](#table-of-contents)
- [Installation](#installation)
- [Usage](#usage)
- [Quick Start](#quick-start)
- [Ready-To-Use Personas Via npm](#ready-to-use-personas-via-npm)
- [Contributing](#contributing)
- [Developing On This Project:](#developing-on-this-project)
- [Sharing Your Personas:](#sharing-your-personas)
- [Tutorial](#tutorial)
- [Overview](#overview)
- [Personas and Cores](#personas-and-cores)
- [Resolvers](#resolvers)
- [`do`](#do)
- [`weight`](#weight)
- [Generators](#generators)
- [Text Generators](#text-generators)
- [Articulate Generators](#articulate-generators)
- [Context Generators](#context-generators)
- [Multiple `do` Items For Resolvers](#multiple-do-items-for-resolvers)
- [Seeding The Pseudorandom Number Generator (PRNG)](#seeding-the-pseudorandom-number-generator-prng)
- [Built-in Help Text For Concept Names](#built-in-help-text-for-concept-names)
- [Miscellaneous](#miscellaneous)
- [TypeScript Support](#typescript-support)
- [ISC License](#isc-license)
## Installation

@@ -52,460 +26,72 @@

You can either use a [ready-to-use persona](#ready-to-use-personas-via-npm) that can articulate concepts, or create your own.
A persona requires a vocabulary, which defines the text that can be generated.
To get started creating your own persona, use the Quick Start core below. It uses most of the features you'll likely need to get moving!
Vocabularies for a persona are defined as key value string pairs in a JS object. The underlying templating engine is [mustache.js](https://github.com/janl/mustache.js/), and all keys are Mustache partials. They can be cross-referenced, just make sure you avoid circular references, which will cause an infinite loop.
If you're the kind of person who likes to get their hands dirty, this lets you dive in right away.
One you construct a `Persona`, call `articulate(template, params)` on the persona to generate text!
After you run the Quick Start, I suggest you read the short [tutorial](#tutorial) below to learn how everything works.
See the example below:
### Quick Start
In a terminal, create a new directory, install Articulate NLG, and open a new JS file in your editor of choice (I recommend [VS Code](https://code.visualstudio.com/)):
```bash
$ md cnlg-starter
$ cd cnlg-starter
$ npm i articulate-nlg # Installs Articulate NLG as a node module. Requires Node.js.
$ code main.js # Opens main.js in VS code; use any editor
```
Paste and save the following:
```js
const Persona = require("articulate-nlg").default;
let dogCore = {
conceptResolvers: {
greet: ["woof", "bark", "sniff sniff", "wag tail"],
master: ["master", "best friend", "my favorite human"],
emoji: ["πŸ‘…", "🐢", "🐾", "πŸ’©", "🐩", "πŸ•β€"],
"welcome-home": {
do: [
{ articulate: "greet", capitalize: true },
"! Welcome home, ",
{ articulate: "master" },
"! ",
{ articulate: "emoji" }
]
}
}
let dogVocab = {
greet: "{{#choose}}woof|bark|sniff sniff|wag tail{{/choose}}",
master:
"{{#params.name}}{{#capitalize}}{{params.name}}{{/capitalize}}{{/params.name}}{{^params.name}}bringer of food{{/params.name}}",
emoji: "{{#choose}}πŸ‘…|🐢|🐾|πŸ’©|🐩|πŸ•β€{{/choose}}",
"welcome-home":
"{{#capitalize}}{{>greet}}{{/capitalize}}! Welcome home, {{>master}}! {{>emoji}}"
};
let max = new Persona(dogCore);
console.log(max.articulate("welcome-home"));
```
let max = new Persona(dogVocab);
Run it!
```bash
$ node main.js
Woof! Welcome home, my favorite human! 🐩
```
## Ready-To-Use Personas Via npm
The following are personas that you can use immediately via npm.
- TBD (this project is brand new and I'm still creating them!)
## Contributing
Contributions are encouraged! We'd love to see what you create.
### Developing On This Project:
- If you catch a bug, would like to request a feature, or would like to start a discussion, [add an issue to the GitHub project](https://github.com/justinmahar/articulate-nlg/issues).
- If you think you can fix it or implement it, [fork the project](https://github.com/justinmahar/articulate-nlg/fork) and then send me a pull request when you're done. :)
### Sharing Your Personas:
If you decide to create a persona, share it with others via npm!
Here's how to go about it:
- Make a project on GitHub with a `package.json` containing your `articulate-nlg` version dependency. I recommend [TypeScript](https://www.typescriptlang.org/) but plain JavaScript is welcome too!
- Create an npm module. For your npm module, use CommonJS module format.
- **Your default export should be a persona instance that contains your core.**
- This ensures your persona always works for everyone even if the articulate-nlg project's specs change over time.
- This also allows people to import your persona and use it right away.
- Be sure to test importing and using your persona!
- If you'd like to share a persona you've made in the [Ready-To-Use Personas](#ready-to-use-personas-via-npm) section of this README, [add it to this README](https://github.com/justinmahar/articulate-nlg/fork) and send me a pull request. That section is only for personas than can be used via npm out of the box.
---
## Tutorial
Read this tutorial if:
- You want a better understanding of how Articulate NLG works.
- You want to create your own persona to generate speech text with.
- You're curious and just want to know more.
Go ahead, read on. You know you want to!
### Overview
> articulate | Γ€r-ˈti-kyΙ™-ˌlāt
>
> to give clear and effective utterance to : to put into words
With Articulate NLG, you can define personas that can articulate concepts (represented as strings) as speech text. Articulation is putting those concepts into words.
- For instance, a particular persona might articulate the concept `"greet"` as `"hello"`, `"hi"`, or `"hey"`.
- Another persona might articulate that same concept as `"oy!"`, `"yo!"`, or `"sup!"`.
- When a persona articulates a concept, they can string together pieces of text and can even articulate other concepts inline to build meaningful phrases and sentences!
The generated text for a concept is typically random and, as mentioned, can even reference other concepts. This allows you to define hundreds or even thousands of permutations of speech elegantly.
Articulate NLG also allows you to vary the randomness so you can bend speech patterns to your liking. You can make certain text more or less likely to generate, or you can use your own pseudorandom number generator (PRNG) seed for ultimate control.
To personalize the generated text, you can also define a "context" object that your persona can reference, like a mail merge.
Let's go deeper!
### Personas and Cores
A persona is powered by a core, which contains all the concept definitions and text that will be generated.
Think of the core as the brain for a persona. It has all of their possible ideas ("concepts") and how they'd articulate them as speech ("resolvers").
When you want to generate speech for a concept, you call `articulate(conceptName)` on the persona.
For example:
```js
let core = { conceptResolvers: { greet: ["hello", "hi", "hey"] } };
let brianna = new Persona(core);
console.log(brianna.articulate("greet")); // hello, hi, or hey
```
So, how does Brianna know which greeting to say? Well, the core contains concept names (such as "greet") which are mapped to resolvers, such as "hello", "hi", and "hey".
If more than one resolver is specified, one of them is randomly chosen.
And finally, if there's no such concept called `"greet"`, then it will just create the text `<greet>`.
But things can get more advanced than that! Keep reading...
### Resolvers
Resolvers can be more complicated than just providing strings. In fact, providing a string (as shown above with "hello", "hi", or "hey") is actually shorthand for creating a resolver, like so:
```js
{ do: { text: "hello" }, weight: 1 }
```
So if you expand the example above out, it could look like this (and would produce the same results!):
```js
let core = {
conceptResolvers: {
greet: [
{ do: { text: "hello" }, weight: 1 },
{ do: { text: "hi" }, weight: 1 },
{ do: { text: "hey" }, weight: 1 }
]
}
};
let brianna = new Persona(core);
console.log(brianna.articulate("greet")); // hello, hi, or hey
```
What's going on here?
Well, `greet` is being mapped to a list of objects, and each one of those is what's called a **resolver**. When articulating, the persona picks one of these resolvers to resolve the concept as speech text.
#### `do`
Each resolver has two properties: The first is `do`, which specifies either a single generator (which creates text, more on that in a second), a list of generators/text, or just some text.
The way `do` works is it collects text from all generators and simply concatenates them together in the order they appear. Using `do` and lists of generators (or text), you can form phrases and sentences.
If this seems confusing, we'll clear it up later with some examples!
#### `weight`
The second resolver property is `weight`, which defaults to `1`. The weight determines how likely it is for the persona to use a particular resolver when articulating speech.
In the example above, all text has an equal chance of being picked.
Let's change the weight values a bit:
```js
let core = {
conceptResolvers: {
greet: [
{ do: { text: "hello" }, weight: 5 },
{ do: { text: "hi" }, weight: 94 },
{ do: { text: "hey" }, weight: 1 }
]
}
};
let brianna = new Persona(core);
console.log(brianna.articulate("greet"));
// 5% chance of "hello"
// 94% chance of "hi"
// 1% chance of "hey"
```
If we shift the weights around a bit, we can make certain resolvers more or less likely to be chosen.
So to summarize so far:
- A persona has a core, which acts as the brain.
- That core has concepts that map to resolvers, which are picked randomly each time a persona articulates a particular concept.
- The randomness of resolver selection can be tweaked by applying a different `weight` to each resolver.
- Resolvers generate text by specifying text or generators to `do`.
With me so far? :) Great, let's continue.
### Generators
Once chosen, a resolver's job is to generate text. To do this, it uses generators.
There are a few kinds of generators, one of which we've already seen. Let's go through each one below!
#### Text Generators
We've already seen a text generator, so nothing new here.
A text generator will simply generate the text you specify in the `text` property:
```js
let generator = {
text: "Well hi there. Welcome home!"
};
```
#### Articulate Generators
This is where things get fun! Instead of specifying text, you can reference another concept that exists in the core.
```js
let generator = {
articulate: "greet"
};
```
This generator would articulate the concept `"greet"` and produce the text for the resolver chosen.
Optionally, you can also capitalize it by providing `capitalize: true`:
```js
let generator = {
articulate: "greet",
capitalize: true
};
```
This would capitalize the first letter of the text generated by articulating the "greet" concept.
For example, let's say we have the following core for a dog:
```js
let dogCore = {
conceptResolvers: {
greet: ["woof", "bark", "sniff sniff", "wag tail"],
master: ["master", "best friend", "my favorite human"],
emoji: ["πŸ‘…", "🐢", "🐾", "πŸ’©", "🐩", "πŸ•β€"]
}
};
```
With this core we can generate cute dog greetings, have a name for a dog's master, and can make dog emojis. The strings have an equal chance of being picked when articulating the concepts.
But let's go one step further and combine them to create the `"welcome-home"` concept...
```js
let dogCore = {
conceptResolvers: {
greet: ["woof", "bark", "sniff sniff", "wag tail"],
master: ["master", "best friend", "my favorite human"],
emoji: ["πŸ‘…", "🐢", "🐾", "πŸ’©", "🐩", "πŸ•β€"],
"welcome-home": {
do: [
{ articulate: "greet", capitalize: true },
"! Welcome home, ",
{ articulate: "master" },
"! ",
{ articulate: "emoji" }
]
}
}
};
let max = new Persona(dogCore);
console.log(max.articulate("welcome-home"));
```
Calling `max.articulate("welcome-home")` generates speech such as:
```bash
Bark! Welcome home, best friend! 🐾
Wag tail! Welcome home, my favorite human! 🐩
Sniff sniff! Welcome home, best friend! 🐢
Wag tail! Welcome home, master! πŸ•β€
Sniff sniff! Welcome home, master! πŸ’©
Woof! Welcome home, my favorite human! 🐩
```
Now we're talking! _Slaps knee_.
Just a few concept definitions later and we have a best friend to welcome us home in many different ways, sometimes with poop.
#### Context Generators
In the real world, we operate on the context of any given situation. For instance, if we know someone's name, we might use it in a sentence while articulating a thought.
Context generators allow us to do just that. You can reference information about a particular situation and weave it into your generated text, like a mail merge.
```js
let generator = {
contextProp: "name"
};
```
You can also specify a `contextDefault` in case it's not found...
```js
let generator = {
contextProp: "name",
contextDefault: "bringer of food"
};
```
To use your context generator, you pass a `context` parameter into `articulate()` that contains the property whose value you want to use.
Like so:
```js
let dogCore = {
conceptResolvers: {
greet: ["woof", "bark", "sniff sniff", "wag tail"],
master: { do: { contextProp: "name", contextDefault: "bringer of food" } },
emoji: ["πŸ‘…", "🐢", "🐾", "πŸ’©", "🐩", "πŸ•β€"],
"welcome-home": {
do: [
{ articulate: "greet", capitalize: true },
"! Welcome home, ",
{ articulate: "master" },
"! ",
{ articulate: "emoji" }
]
}
}
};
let max = new Persona(dogCore);
let context = { name: "Brianna" };
console.log(max.articulate("welcome-home", context));
// Bark! Welcome home, Brianna! 🐢
// Because we specified a contextDefault, if we leave the context out, this is what we get:
console.log(max.articulate("welcome-home"));
// Will generate text like following:
// Sniff sniff! Welcome home, bringer of food! 🐾
// Woof! Welcome home, bringer of food! πŸ‘…
```
// Wag tail! Welcome home, bringer of food! πŸ•β€
// Etc.
If you don't specify a `contextDefault` and it's not found, it will generate text containing the property name in angle brackets, like so: `<name>`.
// This will find the "greet" partial and render it.
console.log(max.articulate("greet"));
// "woof", "bark", "sniff sniff", "wag tail"
#### Multiple `do` Items For Resolvers
// The above is equivalent to using a partial, like so:
console.log(max.articulate("{{>greet}}"));
// "woof", "bark", "sniff sniff", "wag tail"
When specifying a resolver's `do`, you can either specify a string, a generator, or you can specify a list of generators/strings.
// However, if you don't explicitly use a partial and it's not found, you'll see the text you provided:
console.log(max.articulate("missing"));
// "missing"
We've already seen an example above, for the dog's `"welcome-home"` resolver.
// Whereas if you use a partial that's not found, you'll just get an empty string back:
console.log(max.articulate("{{>missing}}"));
// ""
Again, the way `do` works is it takes text from all generators and simply concatenates them together. Using `do` and lists of generators or text, you can form phrases and sentences.
// You can pass parameters, too. These are referenced using: {{params.keyName}}
console.log(max.articulate("master", { "name": "justin" }));
// "Justin"
console.log(max.articulate("{{>master}}", { "name": "justin" }));
// "Justin"
### Seeding The Pseudorandom Number Generator (PRNG)
If you'd like more control over the randomized resolver selection process, you can specify a seed for the pseurdorandom number generator used under the hood.
A seed can be any value. Specifying a seed will cause the text generated to be to same every time for a given seed.
When you want to generate something different, just change the seed.
For instance:
```js
let core = { conceptResolvers: { greet: ["hello", "hi", "hey"] } };
let brianna = new Persona(core);
// Until the seed changes, the result will always be the same.
let seed = 123;
console.log(brianna.articulate("greet", {}, seed)); // hello
console.log(brianna.articulate("greet", {}, seed)); // hello
console.log(brianna.articulate("greet", {}, seed)); // hello
seed = 345;
console.log(brianna.articulate("greet", {}, seed)); // hi
console.log(brianna.articulate("greet", {}, seed)); // hi
console.log(brianna.articulate("greet", {}, seed)); // hi
seed = "February";
console.log(brianna.articulate("greet", {}, seed)); // hey
console.log(brianna.articulate("greet", {}, seed)); // hey
console.log(brianna.articulate("greet", {}, seed)); // hey
// To be more explicit but still keep things random,
// you can use Math.random() as the seed, if you want.
console.log(brianna.articulate("greet", {}, Math.random())); // hello
console.log(brianna.articulate("greet", {}, Math.random())); // hey
console.log(brianna.articulate("greet", {}, Math.random())); // hey
console.log(brianna.articulate("greet", {}, Math.random())); // hi
console.log(brianna.articulate("greet", {}, Math.random())); // hello
// You can use your own mustache, too. Note no name was found here, so it used the default defined in the vocabulary.
console.log(max.articulate("{{#capitalize}}{{>master}}{{/capitalize}}"));
// "Bringer of food"
```
Seed examples/ideas:
## Function Wrappers
- Name-based - Use someone's name as the seed to keep a consistent message for that person.
- Time-based - Use the date as the seed to have a different message each day.
- Variety Cap - Generate different speech until a limit is reached, then keep it the same, or vice versa.
- Combinations - Combine names and times to create unique messages that change at your desired pace (once per hour, day, etc).
As shown in the example above, there are a few function wrappers available for your vocabulary:
### Built-in Help Text For Concept Names
- `capitalize`: Capitalizes the first letter of the contents after rendering it.
- `{{#capitalize}}hello{{/capitalize}}` -> `Hello`
- `choose`: Chooses one of the items at random.
- `{{#choose}}apple|orange|starfruit{{/choose}}` -> Randomly selects `apple`, `orange`, `starfruit`.
- Items are separated by `|` pipes.
- Each item is rendered, meaning you can nest additional partials `{{> partialName }}`.
- `{{#choose}}apple|orange|{{>meat}}{{/choose}}` -> Randomly selects `apple`, `orange`, or whatever `meat` renders as.
- You can specify weights using `=weight` where `weight` is the value.
- For example, `greet: "{{#choose}}woof=5|bark=95{{/choose}}"` would mean a 5% chance of `woof` and a 95% chance of `bark`.
You can call `articulate("--help")` or `articulateHelp()` to generate some helpful text containing all concept names.
```js
console.log(max.articulate("--help"));
// OR
console.log(max.articulateHelp());
```
```bash
I can articulate the following concepts:
- greet
- master
- emoji
- welcome-home
```
If your core is written in another language, you can override the default help message by specifing `helpText` in the core:
```js
let spanishCore = {
conceptResolvers: { hola: ["hola", "alΓ³", "oye"] },
helpText: "Puedo articular los siguientes conceptos:"
};
let gabriela = new Persona(spanishCore);
console.log(gabriela.articulate("--help"));
```
```bash
Puedo articular los siguientes conceptos:
- hola
```
Of course, for your own needs, you can always write your own help text as a separate concept.
### Miscellaneous
For your convenience/flexibility:
- You can get a list of all concept names using `persona.getConceptNames()`. They are pulled directly from the core using `Object.keys()`.
- For some neuralyzer level voodoo, you can get/set the whole core on a persona using `persona.getCore()` and `persona.setCore(core)`. Erase memories or drop in whole new personalities if you want!
## TypeScript Support

@@ -512,0 +98,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚑️ by Socket Inc