A concept describes any "thing." It could represent a concrete object, such as coffee, flowers, or an airport. A concept can also be abstract, such as a day, flight, or order. Well-modeled concepts are essential to Bixby because the planner uses concept models as inputs and goals when executing plans. Concepts are comparable to data types and data structures in a programming language.
Primitive concepts represent simple types, like text or numbers. Structure concepts are more complex, representing records with named properties. Each property is itself a concept. A property is usually a primitive concept, but could be a structure concept itself.
Concepts can also model inheritance relationships. For example, a restaurant inherits the properties of a business, as shown in the following snippet from a structure:
structure (restaurant.Restaurant) {
description (A business who prepares and serves food to customers)
extends (business.Business)
...
As you start to build out your concepts, make sure that you have read Bixby's Design Guidelines so that your models follow a consistent user experience.
Primitive concepts store simple data like text or numbers. There are seven data types for primitive concepts:
boolean
: A binary variable with two possible values, true
and false
.decimal
: A floating-point number, positive or negative. This type allows a decimal part.enum
: An enumeration of a fixed set of possible values (symbols). Use this when it makes sense to list all the possible values in advance. (Also see structure enums.)If there is no vocab file, the enums will not be recognized by natural language.
integer
: A whole number, positive or negative, with no decimals.name
: A Unicode string that is available to Bixby. Use this when the string might be part of natural language input, dialog, or vocabulary. Examples include business names, menu items, and movie titles.qualified
: A name
that matches a regular expression. This is a good choice when you want to validate a string, and want it to be visible to Bixby, but can't enumerate every possible value in advance. The regex also makes it easier for Bixby to recognize valid values.text
: A Unicode string that is not available for use with vocabulary, although it can be displayed or passed to external services. This is good for URLs, blocks of XML or JSON, and large blocks of text such as movie or restaurant reviews.Here are a few examples:
qualified (PhoneNumber) {
description (A string representing a phone number.)
regex-pattern ("\\+?1? ?-? ?((\\(\\d\\d\\d\\))|\\d\\d\\d)? ?-? ?\\d\\d\\d ?-? ?\\d\\d\\d\\d")
}
boolean (Confirmation) {
extends (core.Confirmation)
}
decimal (PlanetSize) {
description (A planet's diameter in miles)
}
enum (Type) {
description (Type of a shoe.)
symbol (Athletic)
symbol (Boot)
symbol (Casual)
symbol (Dance)
symbol (Formal)
symbol (Sandals)
}
integer (RollConcept) {
description (The result of a dice roll.)
}
name (PersonName) {
description ("The person's name. Ex: Jackie Chan")
}
text (Answer) {
description (The user's answer)
}
Well-modeled concepts use the right type for the right job. When primitive concepts are extended, the concepts must all be the same primitive type. In the boolean
example above, core.Confirmation
is a boolean
primitive, because that is required for Confirmation
to inherit from it.
A structure concept is a record-like type, similar to a struct
or a key/value object in other programming languages. Structure concepts contain any number of properties. Each property of a structure must be a concept, not a primitive data type; see Properties, below.
In order for Bixby to use structures, you need an appropriate action model and corresponding function that outputs these structures.
For example, consider the following Order
structure:
structure (Order) {
property (items) {
type (Item)
min (Optional)
max (Many)
}
property (orderNumber) {
type (OrderNumber)
min (Required)
}
property (totalPrice) {
type (Price)
min (Required)
}
property (holdTime) {
type (HoldTime)
min (Required)
}
features {
transaction
}
}
The following action model outputs an Order
.
action (CreateOrder) {
type (BeginTransaction)
collect {
input (initialItems) {
type (InitialItems)
min (Required)
max (One)
default-init {
intent: goal: CreateItems
}
}
}
output (Order)
}
Here is the corresponding JavaScript:
import ZonedDateTime from './lib/zoned-date-time-polyfill.js';
import config from 'config';
//CreateOrder
export default function ({ initialItems, $vivContext }) {
ZonedDateTime.setVivContext($vivContext);
return {
items: initialItems.items,
totalPrice: calculateTotalPrice(initialItems.items),
orderNumber: 23343,
holdTime: ZonedDateTime.now('UTC')
.plusSeconds(parseInt(config.get('hold_time')))
.getMillisFromEpoch(),
};
}
function calculateTotalPrice(items) {
var totalPrice = 0;
for (var i = 0; i < items.length; i++) {
totalPrice += items[i].shirt.price.value * items[i].quantity;
}
return {
value: totalPrice,
currencyType: {
prefixSymbol: '$',
currencyCode: 'USD',
},
};
}
Properties define a "has-a" relationship between concepts. For example, a business has an address; address
could be defined as a primitive concept of type name
, and the Business
structure concept could include address
as a property.
Properties must be semantically meaningful, so they can be useful as input to other concepts or actions. Because of this, every property must be a concept. For example, you can't define a Magnitude
property directly as type (decimal)
in a structure. Instead, define a Magnitude
primitive concept using the decimal
data type:
decimal (Magnitude) {
description (The magnitude of an earthquake)
features {
recognized-range {
min-value(0)
max-value(12)
}
}
}
Then use type (Magnitude)
for your property in the structure:
structure (Earthquake) {
description (An earthquake)
property (title) {
type (Title)
min (Optional) max (One)
}
property (dateTime) {
type (EarthquakeDateTime)
min (Optional) max (One)
}
property (location) {
type (EarthquakeLocation)
min (Optional) max (One)
}
property (magnitude) {
type (Magnitude)
min (Optional) max (One)
}
}
Before creating your own concepts for values like location, dates, times, and currency, see if you can import a library capsule that provides the functionality you need. For example, if you need a Latitude
property, you can import the viv.geo
library capsule, which includes not only a predefined Latitude
concept but higher-level geographical concepts and actions. In addition, concepts from library capsules sometimes include appropriate dialog for values (for example, viv.geo
will format Latitude
values as degrees north or south), and might include vocabulary for name primitives (for example, viv.geo
includes US state abbreviations such as CA
and NY
).
Another use of library capsules is defining structure properties by using the primitive concepts in the viv.core
library as types:
structure (Earthquake) {
property (title) {
type (viv.core.Text)
min (Optional) max (One)
}
property (magnitude) {
type (viv.core.Decimal)
min (Optional) max (One)
}
}
However, if you need to directly refer to a property, including using them in Bixby Views or as inputs to actions, you must define as their own primitive concepts as described above. Also, not all possible types have corresponding concepts in viv.core
. For instance, if you need to distinguish between name
and text
types, you'll have to define your own primitive concepts to do so.
By default, each property is optional and single in cardinality, meaning it can optionally have a single value. Optionality is set with the min
keyword; cardinality is set with max
.
You can state the default values explicitly with min (Optional)
and max (One)
. To allow any number of values for a property, specify max (Many)
. To require at least one value, specify min (Required)
. If a property allows more than one value, all of the values must be of the property's declared type.
Here's an example Hotel
structure with several properties:
structure (Hotel) {
property (name) {
type (HotelName)
min (Required) max (One)
}
property (rating) {
type (HotelRating)
min (Required) max (One)
}
property (lowRate) {
type (HotelLowRate)
min (Required) max (One)
}
property (location) {
type (geo.GeoPoint)
min (Optional) max (One)
}
property (reviewCount) {
type (HotelReviewCount)
min (Optional) max (One)
}
property (images) {
type (core.BaseImage)
min (Optional) max (Many)
}
property (amenities) {
type (HotelAmenity)
min (Optional) max (Many)
}
}
The property rating
must have a single HotelRating
value. The reviewCount
property is optional, but if present, it must be a HotelReviewCount
and have a max of one. However, the images
and amenities
properties are also optional but can have multiple values.
A property will normally only store unique values: if your capsule adds a value to a max (Many)
node that is equivalent to a value already in that node, the values will be automatically merged. If the amenities
property for the Hotel
structure had the values ["Wifi", "Spa", "Swimming Pool"]
, you could add the value "Kid-friendly"
to it. However, if you added the value "Spa"
, it would automatically be merged with the existing value "Spa"
, and the values in the amenities
property would remain unique. Read about Merging and Equivalence for more about this, as well as how this behavior can be overridden.
There are times when you might want the properties within concepts to be visible or invisible to the Planner.
For example, let's say you're defining the GeoPoint
structure. This contains latitude
and longitude
properties, which you probably wouldn't expose independently of the rest of the structure. But it might make sense to declare the timeZone
property with visibility(Public)
. That helps if some execution plan includes an action with a time.TimeZoneId
input and the system already has a GeoPoint
input.
structure (GeoPoint) {
description (A geographic point.)
property (latitude) {
type (Latitude)
min (Required)
}
property (longitude) {
type (Longitude)
min (Required)
}
property (timeZone) {
type (time.TimeZoneId)
visibility (Public)
min (Optional) max (One)
}
}
Here's an example of an execution graph for the utterance, "get the timezone in Seoul, South Korea," in which NamedPoint
contains a property point
of type GeoPoint
:
Likewise, for a stock lookup feature, certain commonly used properties such as the stock exchange (stockExchange
) and stock symbol (StockSymbol
) should be available throughout the entire plan:
structure (ExchangeListing) {
description (Listings on various exchanges)
property (stockExchange) {
type (Exchange)
min (Required) max (One)
visibility (Public)
}
property (companyName) {
type (CompanyName)
min (Required) max (One)
visibility (Public)
}
property (stockSymbol) {
type (StockSymbol)
min (Required) max (One)
visibility (Public)
}
property (ipoYear) {
type (IpoYear)
}
property (companyUrl) {
type (entity.EntityUrl)
}
}
Here is an execution graph that illustrates this usage:
If a property has Default
visibility, subsequent actions won't see it separately from its parent structure. But the Planner can still see the final goal, and so can a route or a sort value. For example, Weather
is a structure with a DayHighTemperature
property with Default
visibility. If a user asks "What was the high temperature today?" that becomes the plan's final goal, and gains direct access to the DayHighTemperature
property.
You could prevent this by setting visibility (Private)
, prohibiting the use of the property anywhere in a plan. Private properties should be properties that a user would never want to use or that are only used internally.
A Structure Enum is the structure counterpart to the the enum data type used in primitive concepts. Both primitive and structure enums have a value chosen from a pre-defined list of constants, but a structure enum's constants set all the properties in the structure at once. It composes properties into a single enumeration list.
Here is an example of the syntax for a structure enum:
structure-enum (CurrencyType) {
property (currencyCode) {
type (CurrencyCode)
}
property (prefixSymbol) {
type (PrefixSymbol)
}
constant: CurrencyType {
prefixSymbol: ($)
currencyCode: (USD)
}
constant: CurrencyType {
prefixSymbol: (€)
currencyCode: (EUR)
}
// ... additional currency types ...
}
This structure enum contains two properties, prefixSymbol
and currencyCode
, and the enumeration sets them together:
prefixSymbol: $, currencyCode: USD
prefixSymbol: €, currencyCode: EUR
As you can see, this CurrencyType
structure-enum is important in any capsule where different currency is required and the appropriate currency needs to be used.
Before creating new concepts, see if you can reuse or extend any existing concepts. This is very powerful. Besides saving time, you will benefit from existing training.
You have already seen how to reuse existing concepts as properties of a new concept. That's useful, but you can also define relationships between concepts.
You can extend
a parent concept with a new child concept. If the parent concept is a structure with properties, the child concept inherits those properties as well. Then you can define new properties specific to the child
concept. This is like inheritance in many programming languages and represents an "is-a" relationship; it describes a parent-child or subset relationship. You can extend both primitive concepts and structure concepts. You can also extend multiple concepts at the same time.
For example, a restaurant is a business. Like any business, it has a phone number, but it can also have a menu. All restaurants are businesses, but not all businesses are restaurants. Restaurants have characteristics that not all other businesses have. Because of this, you can treat any restaurant as a business. But you can't treat all businesses as restaurants.
Here are some other examples:
// A DeliveryRestaurant is both a Restaurant and a
// Vendor (that has a catalog you can buy from, like a menu)
structure (DeliveryRestaurant) {
extends (viv.restaurant.Restaurant)
extends (viv.order.Vendor)
}
// A Country is a special type of AdministrativeDivision
structure (CountryDivision) {
extends (AdministrativeDivision)
property (countryCode2) {
type (ISOCountryCode2)
min (Optional)
max (One)
}
property (countryCode3) {
type (ISOCountryCode3)
min (Optional)
max (One)
}
}
structure (ThisPlanet) {
role-of (Planet)
//extends will allow projections as goals description
extends (Planet)
}
When you extend a structure concept, you can also choose to override
a parent property. You can use this to bind a particular value. This example for Restaurant
inherits BusinessCategory
from business.Business
, but its value will always be "Restaurant."
structure (Restaurant) {
description (A business who prepares and serves food to customers)
extends (business.Business)
property (name) {
override type (RestaurantName)
}
property (category) {
override bind (Restaurant)
}
property (restaurantStyle) {
type (RestaurantStyle)
max (Many)
}
// ... more properties ...
}
You can also override the type
of a property to a more specific type. In the last example, the name
property of a Restaurant
is specified as RestaurantName
. This is more specific than the BusinessName
used by the parent concept. This only works if RestaurantName
extends BusinessName
.
When overriding a property you can also change the minimum cardinality, but only from Optional
to Required
.
Suppose your capsule has a FindFlight
action which takes two airports: a departure airport and an arrival airport. You have an Airport
concept. But you can't give a type of Airport
to more than one input
in your action. You could make ArrivalAirport
and DepartureAirport
concepts that extend Airport
, but this would make planning actions more complicated: you want any action that instantiates an Airport
concept to be able to use that Airport
for either departure or arrival based on context. To do this, you can give ArrivalAirport
and DepartureAirport
a role-of
the Airport
concept.
structure (ArrivalAirport) {
role-of (Airport)
}
A role lets a concept take on different behaviors based on its context. The context is the only difference between a concept and its parent. With the role of Airport
assigned to ArrivalAirport
and DepartureAirport
, then any Airport
created by Bixby in a previous plan step can be converted into an ArrivalAirport
or a DepartureAirport
when it's needed. This way, any airport could be used for arrival or departure based on flight itineraries.
Here's how a corresponding execution graph for flight booking looks:
Notice that both DepartureAirport
and ArrivalAirport
first start as Airport
, after which they are given new roles.
You cannot add new properties to a concept using role-of
. If you need to create a child concept that adds new properties to its parent, you should use extends
instead.
You can add new symbols to an enum
with extends
, but not with role-of
. An enum
can extend multiple concepts or have multiple roles only if all the target enum
concepts have identical symbol sets.
When you flag a concept with concept features
, you are flagging it so that the Bixby platform can treat it in a special way. This special treatment ranges from marking a concept as something stored with a user profile to not storing a value at all. Concept features offer ways to manage user preferences, privacy, or security.
profile-stored
: Associate the concept value with the user profile. Store and retrieve as needed.
transient
: Do not store the concept value. Do not preserve it across execution context.
transaction
: Treat a concept as a "transaction draft", which you can use for inputs to transaction
and commit
actions.
activity
: Represents "transaction records", which are the result of completing commit-type actions. They can also be used as inputs to activity
actions.
recognized-range: Used with decimal
and integer
concepts to set a range of acceptable values with a lower bound (min-value
) and an upper bound (max-value
).
The transaction
and activity
features are transactional features, so after you mark a concept with a transactional feature, you must also define the corresponding state in corresponding support files (*.transaction.bxb
for transaction
and *.activity.bxb
for activity
). You can read more in Transactional Workflows.
Sometimes you know that the user will want to reuse a concept value. It might be the user's name, billing address, or airline seat preferences. Mark these concepts with the features key profile-stored
, and users will have the option to save their values for reuse.
structure (FlightPreferences) {
description (Used to store user flight preferences.)
features {
profile-stored { max (Many) }
}
property (seat) {
type (AirplaneSeatType)
}
property (carrier) {
type (CarrierCode)
}
}
Storing these values is optional, and Bixby prompts the user for approval.
You might want to store certain concept values as singletons, where there can only be one value at a time for the concept. For example, when booking a flight, a single passenger's name must appear on each ticket. Mark such concepts with profile-stored
with a max
cardinality of One
, to ensure that the profile is unique and the restriction is enforced.
Some concepts should never have their values stored and re-used across execution contexts. For example, the user's current location should always be determined every time that value is accessed.
Give a concept the transient
feature to ensure that Bixby will never store or reuse the concept values. Transient concepts will not be re-used from the historical context of a conversation. The library concept geo.CurrentLocation
has the transient
feature, as do most of the concepts within the DateTime library.
You can see another example of transient
being used with the SearchTerm
concept in the Fact sample capsule prototype. For example, if the user asks "Tell me a fact about dogs", then "dogs" would be tagged as the transient SearchTerm
. If the user follows up that utterance with "Tell me another fact", the SearchTerm
will be dropped and not carried over into the follow-up's conversational context. Bixby would then return a different fact not necessarily about dogs.