Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
articulate-nlg
Advanced tools
A natural language generator (NLG) that articulates concepts as words, phrases, and sentences.
A natural language generator (NLG) that articulates concepts as words, phrases, and sentences.
This TypeScript project is available in JavaScript via npm as a CommonJS import.
$ npm i articulate-nlg
CommonJS import:
const Persona = require("articulate-nlg").default;
You can either use a ready-to-use persona, or create your own.
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!
If you're the kind of person who likes to get their hands dirty, this lets you dive in right away.
After you run the Quick Start, I suggest you read the short tutorial below to learn how everything works.
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):
$ 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:
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 max = new Persona(dogCore);
console.log(max.articulate("welcome-home"));
Run it!
$ node main.js
Woof! Welcome home, my favorite human! 🐩
The following are personas that you can use immediately via npm.
Contributions are encouraged! We'd love to see what you create.
If you decide to create a persona, share it with others via npm!
Here's how to go about it:
package.json
containing your articulate-nlg
version dependency. I recommend TypeScript but plain JavaScript is welcome too!Read this tutorial if:
Go ahead, read on. You know you want to!
With Articulate NLG, you can define personas that can articulate concepts (represented as strings) as speech text.
"greet"
as "hello"
, "hi"
, or "hey"
."oy!"
, "yo!"
, or "sup!"
.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!
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:
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 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:
{ do: { text: "hello" }, weight: 1 }
So if you expand the example above out, it could look like this (and would produce the same results!):
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:
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:
weight
to each resolver.do
.With me so far? :) Great, let's continue.
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!
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:
let generator = {
text: "Well hi there. Welcome home!"
};
This is where things get fun! Instead of specifying text, you can reference another concept that exists in the core.
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
:
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:
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...
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:
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.
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.
let generator = {
contextProp: "name"
};
You can also specify a contextDefault
in case it's not found...
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:
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"));
// Woof! Welcome home, bringer of food! 👅
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>
.
do
Items For ResolversWhen specifying a resolver's do
, you can either specify a string, a generator, or you can specify a list of generators/strings.
We've already seen an example above, for the dog's "welcome-home"
resolver.
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.
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:
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
Seed examples/ideas:
You can call articulate("--help")
or articulateHelp()
to generate some helpful text containing all concept names.
console.log(max.articulate("--help"));
// OR
console.log(max.articulateHelp());
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:
let spanishCore = {
conceptResolvers: { hola: ["hola", "aló", "oye"] },
helpText: "Puedo articular los siguientes conceptos:"
};
let gabriela = new Persona(spanishCore);
console.log(gabriela.articulate("--help"));
Puedo articular los siguientes conceptos:
- hola
Of course, for your own needs, you can always write your own help text as a separate concept.
For your convenience/flexibility:
persona.getConceptNames()
. They are pulled directly from the core using Object.keys()
.persona.getCore()
and persona.setCore(core)
. Erase memories or drop in whole new personalities if you want!This is a TypeScript project, so type definitions are available in: dist/index.d.ts
. These help a lot when building cores, but feel free to use vanilla JS if you want.
Copyright 2019 Justin Mahar
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
FAQs
A natural language generator (NLG) that articulates concepts as words, phrases, and sentences.
The npm package articulate-nlg receives a total of 9 weekly downloads. As such, articulate-nlg popularity was classified as not popular.
We found that articulate-nlg demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.