diff --git a/components/wit_ai/actions/add-utterance/add-utterance.mjs b/components/wit_ai/actions/add-utterance/add-utterance.mjs new file mode 100644 index 0000000000000..bfc674bae8408 --- /dev/null +++ b/components/wit_ai/actions/add-utterance/add-utterance.mjs @@ -0,0 +1,89 @@ +import app from "../../wit_ai.app.mjs"; +import utils from "../../common/utils.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "wit_ai-add-utterance", + name: "Add Utterance", + description: "Add a sample utterance to train your app with an intent. [See the documentation](https://wit.ai/docs/http/20240304#post__utterances).", + version: "0.0.1", + type: "action", + props: { + app, + text: { + type: "string", + label: "Text", + description: "The text (sentence) you want your app to understand.", + }, + outOfScope: { + type: "boolean", + label: "Out of Scope", + description: "To train your app to recognize utterances that it should not handle set this to `true`.", + optional: true, + default: false, + reloadProps: true, + }, + }, + async additionalProps() { + if (this.outOfScope === true) { + return {}; + } + + const data = await this.app.listIntents(); + const intents = data.map(({ name }) => name); + const intentOptions = intents.concat(Object.values(constants.BUILTIN_INTENTS)); + + return { + intent: { + type: "string", + label: "Intent", + description: "The name of the intent you wish to create or train.", + options: intentOptions, + }, + entities: { + type: "string[]", + label: "Entities", + description: "The list of entities appearing in this sentence, that you want your app to extract once it is trained. Each entity must be a JSON string with the following properties:\n- `entity` (string, required): The entity name, including the role (e.g., `temperature:temperature` or `wit$temperature:temperature`).\n- `start` (integer, required): The starting index within the text.\n- `end` (integer, required): The ending index within the text.\n- `body` (string, required): The span of the text for the entity.\n- `entities` (array, required): List of entities found within the composite entity.\n\nExample: `{\"entity\":\"wit$temperature:temperature\",\"start\":19,\"end\":26,\"body\":\"34 degrees\",\"entities\":[]}`", + optional: true, + }, + traits: { + type: "string[]", + label: "Traits", + description: "The list of traits that are relevant for the utterance. Each trait must be a JSON string with the following properties:\n- `trait` (string, required): The trait name. This can be a trait you created, or a built-in one. i.e `faq` or `wit$sentiment`.\n- `value` (string, required): The value for the trait, e.g. `positive`.\n\nExample: `{\"trait\":\"wit$sentiment\",\"value\":\"neutral\"}`", + optional: true, + }, + }; + }, + methods: { + addUtterance(args = {}) { + return this.app.post({ + path: "/utterances", + ...args, + }); + }, + }, + async run({ $ }) { + const { + addUtterance, + text, + intent, + entities, + traits, + } = this; + + const response = await addUtterance({ + $, + data: [ + { + text, + intent, + entities: utils.parseArray(entities), + traits: utils.parseArray(traits), + }, + ], + }); + + $.export("$summary", "Successfully added an utterance to your app."); + return response; + }, +}; diff --git a/components/wit_ai/actions/create-entity/create-entity.mjs b/components/wit_ai/actions/create-entity/create-entity.mjs new file mode 100644 index 0000000000000..7b55ef749e8c8 --- /dev/null +++ b/components/wit_ai/actions/create-entity/create-entity.mjs @@ -0,0 +1,63 @@ +import app from "../../wit_ai.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "wit_ai-create-entity", + name: "Create Entity", + description: "Creates a new entity with the given attributes. [See the documentation](https://wit.ai/docs/http/20240304/#post__entities_link)", + version: "0.0.1", + type: "action", + props: { + app, + name: { + type: "string", + label: "Name", + description: "Name for the entity. For built-in entities, use the `wit$` prefix.", + }, + roles: { + type: "string[]", + label: "Roles", + description: "List of roles you want to create for the entity. A default role will always be created", + optional: true, + }, + lookups: { + type: "string[]", + label: "Lookups", + description: "For custom entities, list of lookup strategies (free-text, keywords). Both lookup strategies will be created if empty.", + optional: true, + options: [ + "free-text", + "keywords", + ], + }, + }, + methods: { + createEntity(args = {}) { + return this.app.post({ + path: "/entities", + ...args, + }); + }, + }, + async run({ $ }) { + const { + createEntity, + name, + roles, + lookups, + } = this; + + const response = await createEntity({ + $, + data: { + name, + roles: utils.parseArray(roles), + lookups: utils.parseArray(lookups), + }, + }); + + $.export("$summary", "Successfully created entity"); + + return response; + }, +}; diff --git a/components/wit_ai/actions/create-intent/create-intent.mjs b/components/wit_ai/actions/create-intent/create-intent.mjs new file mode 100644 index 0000000000000..056ae5c440cc5 --- /dev/null +++ b/components/wit_ai/actions/create-intent/create-intent.mjs @@ -0,0 +1,39 @@ +import app from "../../wit_ai.app.mjs"; + +export default { + key: "wit_ai-create-intent", + name: "Create Intent", + description: "Creates a new intent. [See the documentation](https://wit.ai/docs/http/20240304/#post__intents_link)", + version: "0.0.1", + type: "action", + props: { + app, + name: { + type: "string", + label: "Name", + description: "Name for the intent.", + }, + }, + methods: { + createIntent(args = {}) { + return this.app.post({ + path: "/intents", + ...args, + }); + }, + }, + async run({ $ }) { + const { + createIntent, + name, + } = this; + const response = await createIntent({ + $, + data: { + name, + }, + }); + $.export("$summary", "Successfully created intent"); + return response; + }, +}; diff --git a/components/wit_ai/common/constants.mjs b/components/wit_ai/common/constants.mjs new file mode 100644 index 0000000000000..9b7168916dcbc --- /dev/null +++ b/components/wit_ai/common/constants.mjs @@ -0,0 +1,371 @@ +const BASE_URL = "https://api.wit.ai"; +const VERSION = "20240304"; + +const BUILTIN_INTENTS = { + WIT_ADD_TIME_TIMER: { + value: "wit$add_time_timer", + label: "Add time to the timer", + }, + WIT_ADD_TO_PLAYLIST: { + value: "wit$add_to_playlist", + label: "Add a song to a playlist", + }, + WIT_CANCEL: { + value: "wit$cancel", + label: "Cancels an action or selection", + }, + WIT_CHECK_WEATHER_CONDITION: { + value: "wit$check_weather_condition", + label: "Ask for the weather condition", + }, + WIT_CONFIRMATION: { + value: "wit$confirmation", + label: "Confirms or agrees with information", + }, + WIT_CREATE_ALARM: { + value: "wit$create_alarm", + label: "Create a new alarm", + }, + WIT_CREATE_PLAYLIST: { + value: "wit$create_playlist", + label: "Users can create a new playlist and add the current song to it", + }, + WIT_CREATE_TIMER: { + value: "wit$create_timer", + label: "Create a new timer", + }, + WIT_DECREASE_VOLUME: { + value: "wit$decrease_volume", + label: "Decreases the volume of the selected media", + }, + WIT_DELETE_ALARM: { + value: "wit$delete_alarm", + label: "Delete an alarm", + }, + WIT_DELETE_PLAYLIST: { + value: "wit$delete_playlist", + label: "Deletes the current playlist", + }, + WIT_DELETE_TIMER: { + value: "wit$delete_timer", + label: "Delete the timer", + }, + WIT_DISLIKE_MUSIC: { + value: "wit$dislike_music", + label: "User can let the music service know they don't like this song", + }, + WIT_FAST_FORWARD_TRACK: { + value: "wit$fast_forward_track", + label: "Fast forward this song", + }, + WIT_GET_ALARMS: { + value: "wit$get_alarms", + label: "Find out alarms set", + }, + WIT_GET_DATE: { + value: "wit$get_date", + label: "User wants to know the date", + }, + WIT_GET_SUNRISE: { + value: "wit$get_sunrise", + label: "Ask for sunrise", + }, + WIT_GET_SUNSET: { + value: "wit$get_sunset", + label: "Ask for the sunset", + }, + WIT_GET_TEMPERATURE: { + value: "wit$get_temperature", + label: "Ask for the temperature", + }, + WIT_GET_TIME: { + value: "wit$get_time", + label: "User wants to know the time", + }, + WIT_GET_TIME_UNTIL_DATE: { + value: "wit$get_time_until_date", + label: "User wants to know how many days till a certain date", + }, + WIT_GET_TIMER: { + value: "wit$get_timer", + label: "Get how much time is left on the timer", + }, + WIT_GET_TRACK_INFO: { + value: "wit$get_track_info", + label: "Get information about the current song playing", + }, + WIT_GET_WEATHER: { + value: "wit$get_weather", + label: "Ask for the weather", + }, + WIT_GO_BACK: { + value: "wit$go_back", + label: "Indicates that an action or individual should go back or be reversed", + }, + WIT_GO_FORWARD: { + value: "wit$go_forward", + label: "Indicates that an action or individual should move forward", + }, + WIT_INCREASE_VOLUME: { + value: "wit$increase_volume", + label: "Increases the volume of the selected media", + }, + WIT_LIKE_MUSIC: { + value: "wit$like_music", + label: "Users can like a song and gets saved into the favorites", + }, + WIT_LOOP_MUSIC: { + value: "wit$loop_music", + label: "Repeatedly play this song", + }, + WIT_NEGATION: { + value: "wit$negation", + label: "Denies or disagrees with information", + }, + WIT_NEVERMIND: { + value: "wit$nevermind", + label: "Discards the voice activation", + }, + WIT_OPEN_RESOURCE: { + value: "wit$open_resource", + label: "Opens an app", + }, + WIT_PAUSE: { + value: "wit$pause", + label: "Pauses the media (song, video, podcast)", + }, + WIT_PAUSE_MUSIC: { + value: "wit$pause_music", + label: "Pause the current song playing", + }, + WIT_PAUSE_TIMER: { + value: "wit$pause_timer", + label: "Pause the timer", + }, + WIT_PLAY: { + value: "wit$play", + label: "Plays the media (song, video, podcast)", + }, + WIT_PLAY_MUSIC: { + value: "wit$play_music", + label: "Play music or media content", + }, + WIT_PLAY_PODCAST: { + value: "wit$play_podcast", + label: "Play the particular podcast", + }, + WIT_PREVIOUS_TRACK: { + value: "wit$previous_track", + label: "Play the previous song", + }, + WIT_REMOVE_FROM_PLAYLIST: { + value: "wit$remove_from_playlist", + label: "Deleting a song from the playlist", + }, + WIT_REPEAT_RESPONSE: { + value: "wit$repeat_response", + label: "The statement was unclear; please repeat the response", + }, + WIT_REPLAY_TRACK: { + value: "wit$replay_track", + label: "Replay the current song", + }, + WIT_RESUME: { + value: "wit$resume", + label: "Resumes the media (song, video, podcast)", + }, + WIT_RESUME_MUSIC: { + value: "wit$resume_music", + label: "Resumes the song that was paused", + }, + WIT_RESUME_TIMER: { + value: "wit$resume_timer", + label: "Resume the timer", + }, + WIT_REWIND_TRACK: { + value: "wit$rewind_track", + label: "Rewind the song for 10 seconds", + }, + WIT_SELECT_ITEM: { + value: "wit$select_item", + label: "Indicates a selection", + }, + WIT_SHARE: { + value: "wit$share", + label: "Shares this with another individual", + }, + WIT_SHUFFLE_PLAYLIST: { + value: "wit$shuffle_playlist", + label: "Play the playlist in random order", + }, + WIT_SILENCE_ALARM: { + value: "wit$silence_alarm", + label: "Stop the alarm", + }, + WIT_SKIP_TRACK: { + value: "wit$skip_track", + label: "Skipping the current song playing", + }, + WIT_SNOOZE_ALARM: { + value: "wit$snooze_alarm", + label: "Snooze the alarm", + }, + WIT_STOP: { + value: "wit$stop", + label: "Stops the media (song, video, podcast)", + }, + WIT_STOP_MUSIC: { + value: "wit$stop_music", + label: "Stop music or media content that's playing", + }, + WIT_SUBTRACT_TIME_TIMER: { + value: "wit$subtract_time_timer", + label: "Reduce time from the timer", + }, + WIT_UNLOOP_MUSIC: { + value: "wit$unloop_music", + label: "Stop looping the song", + }, + WIT_UNSHUFFLE_PLAYLIST: { + value: "wit$unshuffle_playlist", + label: "Plays according to the order in the playlist", + }, +}; + +const BUILTIN_ENTITIES = { + WIT_AGE_OF_PERSON: { + value: "wit$age_of_person", + label: "Captures the age of a person, pet or object", + }, + WIT_AGENDA_ENTRY: { + value: "wit$agenda_entry", + label: "Extrapolates typical agenda items from free text", + }, + WIT_AMOUNT_OF_MONEY: { + value: "wit$amount_of_money", + label: "Measures an amount of money such as $20, 30 euros", + }, + WIT_CONTACT: { + value: "wit$contact", + label: "Captures free text that's either the name or a clear reference to a person", + }, + WIT_CREATIVE_WORK: { + value: "wit$creative_work", + label: "Captures and resolves creative work including movies, TV shows, music albums and tracks", + }, + WIT_DATETIME: { + value: "wit$datetime", + label: "Captures and resolves date and time, like tomorrow at 6pm", + }, + WIT_DISTANCE: { + value: "wit$distance", + label: "Captures a distance in miles or kilometers such as 5km, 5 miles and 12m", + }, + WIT_DURATION: { + value: "wit$duration", + label: "Captures a duration such as 30min, 2 hours or 15sec and normalizes the value in seconds", + }, + WIT_EMAIL: { + value: "wit$email", + label: "Captures an email but do not try to check the validity of the email", + }, + WIT_LOCAL_SEARCH_QUERY: { + value: "wit$local_search_query", + label: "Captures free text that's a query for a local search", + }, + WIT_LOCATION: { + value: "wit$location", + label: "Captures free text that's a typical location, place or address", + }, + WIT_MATH_EXPRESSION: { + value: "wit$math_expression", + label: "Captures free text that's a mathematical, computable expression", + }, + WIT_MESSAGE_BODY: { + value: "wit$message_body", + label: "Captures free text that's the body of a message, such as email or SMS", + }, + WIT_NOTABLE_PERSON: { + value: "wit$notable_person", + label: "Captures and resolves names of notable people and public figures", + }, + WIT_NUMBER: { + value: "wit$number", + label: "Extrapolates a number from free text", + }, + WIT_ORDINAL: { + value: "wit$ordinal", + label: "Captures the measure of an ordinal number", + }, + WIT_PHONE_NUMBER: { + value: "wit$phone_number", + label: "Captures phone numbers, but does not try to check the validity of the number", + }, + WIT_PHRASE_TO_TRANSLATE: { + value: "wit$phrase_to_translate", + label: "Captures free text that is a phrase the user wants to translate", + }, + WIT_QUANTITY: { + value: "wit$quantity", + label: "Captures the quantity of something", + }, + WIT_REMINDER: { + value: "wit$reminder", + label: "Captures free text that's a typical reminder", + }, + WIT_SEARCH_QUERY: { + value: "wit$search_query", + label: "Captures free text that's a generic search engine query", + }, + WIT_TEMPERATURE: { + value: "wit$temperature", + label: "Captures the temperature in units of celsius or fahrenheit degrees", + }, + WIT_URL: { + value: "wit$url", + label: "Captures an URL, but does not try to check the validity of the URL", + }, + WIT_VOLUME: { + value: "wit$volume", + label: "Captures measures of volume like 250 ml, 3 gal", + }, + WIT_WIKIPEDIA_SEARCH_QUERY: { + value: "wit$wikipedia_search_query", + label: "Captures free text that's a typical query for Wikipedia", + }, + WIT_WOLFRAM_SEARCH_QUERY: { + value: "wit$wolfram_search_query", + label: "Captures free text that's a typical query for Wolfram Alpha", + }, +}; + +const BUILTIN_TRAITS = { + WIT_BYE: { + value: "wit$bye", + label: "A binary trait that captures goodbye intents (example: bye).", + }, + WIT_GREETINGS: { + value: "wit$greetings", + label: "A binary trait that captures greeting intents (example: hello).", + }, + WIT_ON_OFF: { + value: "wit$on_off", + label: "A trait that deciphers the intent of toggling something (e.g. a device with 2 states), such as turning on the lights, turn the tv off or toggle the shades.", + }, + WIT_SENTIMENT: { + value: "wit$sentiment", + label: "A trait that captures the sentiment in an utterance and returns positive, neutral or negative.", + }, + WIT_THANKS: { + value: "wit$thanks", + label: "A binary trait that captures thankful intents, such as thank you.", + }, +}; + +export default { + BASE_URL, + VERSION, + BUILTIN_INTENTS, + BUILTIN_ENTITIES, + BUILTIN_TRAITS, +}; diff --git a/components/wit_ai/common/utils.mjs b/components/wit_ai/common/utils.mjs new file mode 100644 index 0000000000000..a0d53237abddb --- /dev/null +++ b/components/wit_ai/common/utils.mjs @@ -0,0 +1,58 @@ +import { ConfigurationError } from "@pipedream/platform"; + +const parseJson = (input) => { + const parse = (value) => { + if (typeof(value) === "string") { + if (value === "true" || value === "false") { + return value; + } + try { + return parseJson(JSON.parse(value)); + } catch (e) { + return value; + } + } else if (Array.isArray(value)) { + return value.map((item) => parse(item)); + + } else if (typeof(value) === "object" && value !== null) { + return Object.entries(value) + .reduce((acc, [ + key, + val, + ]) => Object.assign(acc, { + [key]: parse(val), + }), {}); + } + return value; + }; + + return parse(input); +}; + +function parseArray(value) { + try { + if (!value) { + return []; + } + + if (Array.isArray(value)) { + return value; + } + + const parsedValue = JSON.parse(value); + + if (!Array.isArray(parsedValue)) { + throw new Error("Not an array"); + } + + return parsedValue; + + } catch (e) { + throw new ConfigurationError("Make sure the custom expression contains a valid array object"); + } +} + +export default { + parseJson, + parseArray: (value) => parseArray(value)?.map(parseJson), +}; diff --git a/components/wit_ai/package.json b/components/wit_ai/package.json index a38857247dcbe..7abe0f9baface 100644 --- a/components/wit_ai/package.json +++ b/components/wit_ai/package.json @@ -1,7 +1,7 @@ { "name": "@pipedream/wit_ai", - "version": "0.6.0", - "description": "Pipedream wit_ai Components", + "version": "0.7.0", + "description": "Pipedream Wit AI Components", "main": "wit_ai.app.mjs", "keywords": [ "pipedream", @@ -13,6 +13,6 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^3.0.0" + "@pipedream/platform": "^3.0.3" } } diff --git a/components/wit_ai/wit_ai.app.mjs b/components/wit_ai/wit_ai.app.mjs index e7d60546941ce..26df204585de7 100644 --- a/components/wit_ai/wit_ai.app.mjs +++ b/components/wit_ai/wit_ai.app.mjs @@ -1,11 +1,46 @@ +import { axios } from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + export default { type: "app", app: "wit_ai", - propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + getUrl(path) { + return `${constants.BASE_URL}${path}`; + }, + getHeaders(headers) { + return { + ...headers, + "Authorization": `Bearer ${this.$auth.app_token}`, + }; + }, + getParams(params) { + return { + ...params, + v: constants.VERSION, + }; + }, + _makeRequest({ + $ = this, path, headers, params, ...args + } = {}) { + return axios($, { + ...args, + url: this.getUrl(path), + headers: this.getHeaders(headers), + params: this.getParams(params), + }); + }, + post(args = {}) { + return this._makeRequest({ + method: "POST", + ...args, + }); + }, + listIntents(args = {}) { + return this._makeRequest({ + path: "/intents", + ...args, + }); }, }, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fa7a92823dd6..2f7ee969edb2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14538,7 +14538,7 @@ importers: components/wit_ai: dependencies: '@pipedream/platform': - specifier: ^3.0.0 + specifier: ^3.0.3 version: 3.0.3 components/withings: @@ -15440,14 +15440,6 @@ importers: specifier: ^6.0.0 version: 6.2.0 - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/cjs: {} - - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/esm: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/cjs: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/esm: {} - packages/ai: dependencies: '@pipedream/sdk': @@ -35831,8 +35823,6 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) - transitivePeerDependencies: - - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: