diff --git a/.github/workflows/templates-tests.yml b/.github/workflows/templates-tests.yml index 885ce69..dc6b7f6 100644 --- a/.github/workflows/templates-tests.yml +++ b/.github/workflows/templates-tests.yml @@ -12,7 +12,7 @@ jobs: - name: Setting up node uses: actions/setup-node@v1 with: - node-version: '12' + node-version: '20' - name: Setting up cache uses: actions/cache@v1 with: @@ -39,7 +39,7 @@ jobs: env: BOTONIC_DISABLE_MIXPANEL: 1 TEST_PATH: custom-webchat - run: (cd $TEST_PATH && npm install -D && npm run build && npm run test) + run: (cd $TEST_PATH && npm install -D --legacy-peer-deps && npm run build && npm run test) - name: dynamic-carousel build & test env: BOTONIC_DISABLE_MIXPANEL: 1 @@ -60,11 +60,6 @@ jobs: BOTONIC_DISABLE_MIXPANEL: 1 TEST_PATH: intent run: (cd $TEST_PATH && npm install -D && npm run build && npm run test) - - name: nlu build & test - env: - BOTONIC_DISABLE_MIXPANEL: 1 - TEST_PATH: nlu - run: (cd $TEST_PATH && npm install -D && npm run build && npm run test) - name: tutorial build & test env: BOTONIC_DISABLE_MIXPANEL: 1 diff --git a/blank-typescript/babel.config.js b/blank-typescript/babel.config.js index 8eabd70..072f7fb 100644 --- a/blank-typescript/babel.config.js +++ b/blank-typescript/babel.config.js @@ -24,8 +24,7 @@ module.exports = { '@babel/typescript', ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/blank-typescript/jest.config.js b/blank-typescript/jest.config.js new file mode 100644 index 0000000..5914ebf --- /dev/null +++ b/blank-typescript/jest.config.js @@ -0,0 +1,17 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.[j|t]sx?$": [ + "ts-jest", + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/blank-typescript/package.json b/blank-typescript/package.json index 52e2da6..bf929d7 100644 --- a/blank-typescript/package.json +++ b/blank-typescript/package.json @@ -3,61 +3,50 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0" + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@babel/preset-typescript": "^7.12.7", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@babel/preset-typescript": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "ts-jest": "^29.1.2", + "ts-node": "^10.9.2", + "typescript": "^4.9.5", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" } } diff --git a/blank-typescript/src/nlp/data/en/.gitkeep b/blank-typescript/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/blank-typescript/src/nlp/tasks/intent-classification/models/en/.gitkeep b/blank-typescript/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/blank-typescript/src/nlp/tasks/intent-classification/train.ts b/blank-typescript/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/blank-typescript/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/blank-typescript/src/plugins.ts b/blank-typescript/src/plugins.ts index 7d797bf..9f7cc5e 100644 --- a/blank-typescript/src/plugins.ts +++ b/blank-typescript/src/plugins.ts @@ -1,3 +1,3 @@ -import { PluginConfig } from '@botonic/core' +import type { PluginConfig } from '@botonic/core' export const plugins: PluginConfig[] = [] diff --git a/blank-typescript/tests/app.test.js b/blank-typescript/tests/app.test.js index 18e0a44..e9d8962 100644 --- a/blank-typescript/tests/app.test.js +++ b/blank-typescript/tests/app.test.js @@ -11,11 +11,10 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") - ) + const response = await input.text('whatever') + expect(response).toBe(output.text("I don't understand you")) }) diff --git a/blank-typescript/tsconfig.json b/blank-typescript/tsconfig.json index a2077bb..8c9b50d 100644 --- a/blank-typescript/tsconfig.json +++ b/blank-typescript/tsconfig.json @@ -7,16 +7,17 @@ //ERROR in [at-loader] ./node_modules/@botonic/react/node_modules/@types/react/index.d.ts:2814:14 //TS2300: Duplicate identifier 'LibraryManagedAttributes'. "sourceMap": true, - "target": "es2015", - "module": "commonjs", + "target": "ES2015", + "module": "Node16", + "moduleResolution": "Node16", "baseUrl": "src", "paths": { "*": ["src/*", "lib/*", "types/*"] }, "outDir": "lib", - "jsx": "react", + "jsx": "react-jsx", "allowJs": true, - "lib": ["dom", "es2017"], + "lib": ["DOM", "ES2022"], "allowSyntheticDefaultImports": true, "esModuleInterop": true }, diff --git a/blank-typescript/webpack.config.js b/blank-typescript/webpack.config.js index c80d1df..bc9a260 100644 --- a/blank-typescript/webpack.config.js +++ b/blank-typescript/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -122,8 +103,6 @@ const babelTypescriptLoaderConfig = { '@babel/typescript', ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -166,16 +145,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -201,7 +181,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -334,14 +314,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/blank/babel.config.js b/blank/babel.config.js index 09ae315..ce0c91d 100644 --- a/blank/babel.config.js +++ b/blank/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - require('@babel/plugin-proposal-object-rest-spread'), - require('@babel/plugin-proposal-class-properties'), + require('@babel/plugin-transform-modules-commonjs'), require('@babel/plugin-transform-runtime'), ], } diff --git a/blank/jest.config.js b/blank/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/blank/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/blank/package.json b/blank/package.json index 4919207..c18a13f 100644 --- a/blank/package.json +++ b/blank/package.json @@ -3,60 +3,50 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0" + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/blank/src/nlp/data/en/.gitkeep b/blank/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/blank/src/nlp/tasks/intent-classification/models/en/.gitkeep b/blank/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/blank/src/nlp/tasks/intent-classification/train.ts b/blank/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/blank/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/blank/tests/app.test.js b/blank/tests/app.test.js index 18e0a44..e9d8962 100644 --- a/blank/tests/app.test.js +++ b/blank/tests/app.test.js @@ -11,11 +11,10 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") - ) + const response = await input.text('whatever') + expect(response).toBe(output.text("I don't understand you")) }) diff --git a/blank/webpack.config.js b/blank/webpack.config.js index 4b545fa..03250b1 100644 --- a/blank/webpack.config.js +++ b/blank/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -121,8 +102,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -165,16 +144,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -200,7 +180,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -333,14 +313,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/booking-platform/babel.config.js b/booking-platform/babel.config.js index 7325b98..ce0c91d 100644 --- a/booking-platform/babel.config.js +++ b/booking-platform/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/booking-platform/jest.config.js b/booking-platform/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/booking-platform/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/booking-platform/package.json b/booking-platform/package.json index 7ceeb3e..7314225 100644 --- a/booking-platform/package.json +++ b/booking-platform/package.json @@ -3,66 +3,59 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0", + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5", "@date-io/date-fns": "^1.3.13", - "@material-ui/core": "^4.11.0", - "@material-ui/lab": "^4.0.0-alpha.56", - "@material-ui/pickers": "^3.2.10", - "date-fns": "^2.16.1", + "@material-ui/core": "4.11.0", + "@material-ui/lab": "4.0.0-alpha.56", + "@material-ui/pickers": "^3.3.11", + "date-fns": "^2.30.0", "react-stars": "^2.2.5" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "ts-node": "^10.9.2", + "typescript": "^4.9.5", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/booking-platform/src/nlp/data/en/.gitkeep b/booking-platform/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/booking-platform/src/nlp/tasks/intent-classification/models/en/.gitkeep b/booking-platform/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/booking-platform/src/nlp/tasks/intent-classification/train.ts b/booking-platform/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/booking-platform/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/booking-platform/webpack.config.js b/booking-platform/webpack.config.js index 4b545fa..03250b1 100644 --- a/booking-platform/webpack.config.js +++ b/booking-platform/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -121,8 +102,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -165,16 +144,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -200,7 +180,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -333,14 +313,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/childs/babel.config.js b/childs/babel.config.js index 7325b98..ce0c91d 100644 --- a/childs/babel.config.js +++ b/childs/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/childs/jest.config.js b/childs/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/childs/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/childs/package.json b/childs/package.json index 8e6876b..4aa433f 100644 --- a/childs/package.json +++ b/childs/package.json @@ -3,60 +3,50 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0" + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/childs/src/nlp/data/en/.gitkeep b/childs/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/childs/src/nlp/tasks/intent-classification/models/en/.gitkeep b/childs/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/childs/src/nlp/tasks/intent-classification/train.ts b/childs/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/childs/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/childs/tests/app.test.js b/childs/tests/app.test.js index 9410f1c..3bc2bec 100644 --- a/childs/tests/app.test.js +++ b/childs/tests/app.test.js @@ -11,14 +11,15 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: hi.js', async () => { - await expect(i.text('Hi')).resolves.toBe( - o.text( + const response = await input.text('Hi') + await expect(response).toBe( + output.text( 'Hi! Choose what you want to eat:', - o.replies( + output.replies( { text: 'Pizza', payload: 'pizza' }, { text: 'Pasta', path: 'pasta' } ) @@ -27,10 +28,11 @@ test('TEST: hi.js', async () => { }) test('TEST: pizza.js', async () => { - await expect(i.payload('pizza', {}, 'hi')).resolves.toBe( - o.text( + const response = await input.payload('pizza', undefined, 'hi') + expect(response).toBe( + output.text( 'You chose Pizza! Choose one ingredient:', - o.replies( + output.replies( { text: 'Sausage', payload: 'sausage' }, { text: 'Bacon', payload: 'bacon' } ) @@ -39,22 +41,25 @@ test('TEST: pizza.js', async () => { }) test('TEST: sausage.js', async () => { - await expect(i.payload('sausage', {}, 'hi/pizza')).resolves.toBe( - o.text('You chose Sausage on Pizza') + const response = await input.payload('sausage', undefined, 'hi/pizza') + expect(response).toBe( + output.text('You chose Sausage on Pizza') ) }) test('TEST: bacon.js', async () => { - await expect(i.path('bacon', {}, 'hi/pizza')).resolves.toBe( - o.text('You chose Bacon on Pizza') + const response = await input.path('bacon', undefined, 'hi/pizza') + expect(response).toBe( + output.text('You chose Bacon on Pizza') ) }) test('TEST: pasta.js', async () => { - await expect(i.payload('pasta', {}, 'hi')).resolves.toBe( - o.text( + const response = await input.payload('pasta', undefined, 'hi') + expect(response).toBe( + output.text( 'You chose Pasta! Choose one ingredient:', - o.replies( + output.replies( { text: 'Cheese', payload: 'cheese' }, { text: 'Tomato', payload: 'tomato' } ) @@ -63,19 +68,22 @@ test('TEST: pasta.js', async () => { }) test('TEST: cheese.js', async () => { - await expect(i.payload('cheese', {}, 'hi/pasta')).resolves.toBe( - o.text('You chose Cheese on Pasta') + const response = await input.payload('cheese', undefined, 'hi/pasta') + expect(response).toBe( + output.text('You chose Cheese on Pasta') ) }) test('TEST: tomato.js', async () => { - await expect(i.path('tomato', {}, 'hi/pasta')).resolves.toBe( - o.text('You chose Tomato on Pasta') + const response = await input.path('tomato', undefined, 'hi/pasta') + expect(response).toBe( + output.text('You chose Tomato on Pasta') ) }) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") + const response = await input.text('whatever') + expect(response).toBe( + output.text("I don't understand you") ) }) diff --git a/childs/webpack.config.js b/childs/webpack.config.js index 4b545fa..a40a3bf 100644 --- a/childs/webpack.config.js +++ b/childs/webpack.config.js @@ -1,34 +1,16 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -121,8 +103,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -165,16 +145,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -200,7 +181,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -333,14 +314,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/custom-webchat/babel.config.js b/custom-webchat/babel.config.js index 7325b98..ce0c91d 100644 --- a/custom-webchat/babel.config.js +++ b/custom-webchat/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/custom-webchat/jest.config.js b/custom-webchat/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/custom-webchat/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/custom-webchat/package.json b/custom-webchat/package.json index 22bb14e..e9d282d 100644 --- a/custom-webchat/package.json +++ b/custom-webchat/package.json @@ -3,61 +3,51 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0", + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5", "react-calendar": "2.19.2" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/custom-webchat/src/nlp/data/en/.gitkeep b/custom-webchat/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/custom-webchat/src/nlp/tasks/intent-classification/models/en/.gitkeep b/custom-webchat/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/custom-webchat/src/nlp/tasks/intent-classification/train.ts b/custom-webchat/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/custom-webchat/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/custom-webchat/tests/app.test.js b/custom-webchat/tests/app.test.js index 18e0a44..35bf4b6 100644 --- a/custom-webchat/tests/app.test.js +++ b/custom-webchat/tests/app.test.js @@ -11,11 +11,11 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") - ) + const response = await input.text('whatever') + expect(response).toBe(output.text("I don't understand you")) }) + diff --git a/custom-webchat/webpack.config.js b/custom-webchat/webpack.config.js index 4b545fa..03250b1 100644 --- a/custom-webchat/webpack.config.js +++ b/custom-webchat/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -121,8 +102,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -165,16 +144,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -200,7 +180,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -333,14 +313,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/dynamic-carousel/babel.config.js b/dynamic-carousel/babel.config.js index 7325b98..ce0c91d 100644 --- a/dynamic-carousel/babel.config.js +++ b/dynamic-carousel/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/dynamic-carousel/jest.config.js b/dynamic-carousel/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/dynamic-carousel/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/dynamic-carousel/package.json b/dynamic-carousel/package.json index b408c6d..dabcaa2 100644 --- a/dynamic-carousel/package.json +++ b/dynamic-carousel/package.json @@ -3,61 +3,51 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0", + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5", "isomorphic-fetch": "^2.2.1" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/dynamic-carousel/src/actions/get-shirts.jsx b/dynamic-carousel/src/actions/get-shirts.jsx index 90e94bf..582d52a 100644 --- a/dynamic-carousel/src/actions/get-shirts.jsx +++ b/dynamic-carousel/src/actions/get-shirts.jsx @@ -5,7 +5,6 @@ import { Pic, RequestContext, Subtitle, - Text, Title, } from '@botonic/react' import fetch from 'isomorphic-fetch' diff --git a/dynamic-carousel/src/nlp/data/en/.gitkeep b/dynamic-carousel/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/dynamic-carousel/src/nlp/tasks/intent-classification/models/en/.gitkeep b/dynamic-carousel/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/dynamic-carousel/src/nlp/tasks/intent-classification/train.ts b/dynamic-carousel/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/dynamic-carousel/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/dynamic-carousel/tests/app.test.js b/dynamic-carousel/tests/app.test.js index 18e0a44..e9d8962 100644 --- a/dynamic-carousel/tests/app.test.js +++ b/dynamic-carousel/tests/app.test.js @@ -11,11 +11,10 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") - ) + const response = await input.text('whatever') + expect(response).toBe(output.text("I don't understand you")) }) diff --git a/dynamic-carousel/webpack.config.js b/dynamic-carousel/webpack.config.js index 4b545fa..03250b1 100644 --- a/dynamic-carousel/webpack.config.js +++ b/dynamic-carousel/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -121,8 +102,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -165,16 +144,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -200,7 +180,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -333,14 +313,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/dynamodb/babel.config.js b/dynamodb/babel.config.js index 7325b98..ce0c91d 100644 --- a/dynamodb/babel.config.js +++ b/dynamodb/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/dynamodb/jest.config.js b/dynamodb/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/dynamodb/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/dynamodb/package.json b/dynamodb/package.json index 95549fc..c6a2f87 100644 --- a/dynamodb/package.json +++ b/dynamodb/package.json @@ -3,61 +3,52 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic\\/(core|react)).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/plugin-dynamodb": "~0.21.0", - "@botonic/react": "~0.21.0" + "@babel/runtime": "^7.23.9", + "@botonic/plugin-dynamodb": "0.25.0-alpha.0", + "@botonic/react": "0.25.0-alpha.5" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } + diff --git a/dynamodb/src/nlp/data/en/.gitkeep b/dynamodb/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/dynamodb/src/nlp/tasks/intent-classification/models/en/.gitkeep b/dynamodb/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/dynamodb/src/nlp/tasks/intent-classification/train.ts b/dynamodb/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/dynamodb/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/dynamodb/tests/app.test.js b/dynamodb/tests/app.test.js index 18e0a44..e9d8962 100644 --- a/dynamodb/tests/app.test.js +++ b/dynamodb/tests/app.test.js @@ -11,11 +11,10 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") - ) + const response = await input.text('whatever') + expect(response).toBe(output.text("I don't understand you")) }) diff --git a/dynamodb/webpack.config.js b/dynamodb/webpack.config.js index 883a80a..fe92e04 100644 --- a/dynamodb/webpack.config.js +++ b/dynamodb/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -123,8 +104,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -167,16 +146,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -202,7 +182,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -335,14 +315,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/handoff/babel.config.js b/handoff/babel.config.js index 7325b98..ce0c91d 100644 --- a/handoff/babel.config.js +++ b/handoff/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/handoff/jest.config.js b/handoff/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/handoff/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/handoff/package.json b/handoff/package.json index a9b00b4..e0b7b83 100644 --- a/handoff/package.json +++ b/handoff/package.json @@ -3,60 +3,50 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0" + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/handoff/src/nlp/data/en/.gitkeep b/handoff/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/handoff/src/nlp/tasks/intent-classification/models/en/.gitkeep b/handoff/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/handoff/src/nlp/tasks/intent-classification/train.ts b/handoff/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/handoff/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/handoff/tests/app.test.js b/handoff/tests/app.test.js index 18e0a44..e9d8962 100644 --- a/handoff/tests/app.test.js +++ b/handoff/tests/app.test.js @@ -11,11 +11,10 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") - ) + const response = await input.text('whatever') + expect(response).toBe(output.text("I don't understand you")) }) diff --git a/handoff/webpack.config.js b/handoff/webpack.config.js index 4b545fa..a40a3bf 100644 --- a/handoff/webpack.config.js +++ b/handoff/webpack.config.js @@ -1,34 +1,16 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -121,8 +103,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -165,16 +145,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -200,7 +181,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -333,14 +314,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/intent/babel.config.js b/intent/babel.config.js index 7325b98..ce0c91d 100644 --- a/intent/babel.config.js +++ b/intent/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/intent/jest.config.js b/intent/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/intent/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/intent/package.json b/intent/package.json index 48f62f1..54a248f 100644 --- a/intent/package.json +++ b/intent/package.json @@ -3,61 +3,52 @@ "version": "1.0.0", "scripts": { "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "start": "webpack-dev-server --env target=dev --mode=development", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@botonic/plugin-dialogflow": "~0.21.0", - "@babel/runtime": "^7.12.5", - "@botonic/react": "~0.21.0" + "@botonic/plugin-dialogflow": "0.25.0-alpha.2", + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", + "@babel/core": "^7.23.9", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin": "^8.0.1", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass": "^1.71.1", + "sass-loader": "^14.1.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser": "^5.27.2", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2" + }, + "engines": { + "node": ">=20.0.0" } } + diff --git a/intent/src/nlp/data/en/.gitkeep b/intent/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/intent/src/nlp/tasks/intent-classification/models/en/.gitkeep b/intent/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/intent/src/nlp/tasks/intent-classification/train.ts b/intent/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/intent/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/intent/src/plugins.js b/intent/src/plugins.js index cec3a7d..bad9f74 100644 --- a/intent/src/plugins.js +++ b/intent/src/plugins.js @@ -1,7 +1,9 @@ +import * as pluginDialogflow from '@botonic/plugin-dialogflow' + export const plugins = [ { id: 'dialogflow', - resolve: require('@botonic/plugin-dialogflow'), + resolve: pluginDialogflow, // Copy-past here the generated JSON: https://dialogflow.com/docs/reference/v2-auth-setup options: { credentials: { diff --git a/intent/tests/app.test.js b/intent/tests/app.test.js index bf9299b..91ccbcb 100644 --- a/intent/tests/app.test.js +++ b/intent/tests/app.test.js @@ -10,14 +10,13 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text( - // replace with 'Try typing "hello" to start the bot.' after configuring dialogflow - `Enter the generated JSON key for dialogflowV2 in plugins.js to test the bot.` - ) - ) + const response = await input.text('whatever') + expect(response).toBe(output.text( + // replace with 'Try typing "hello" to start the bot.' after configuring dialogflow + `Enter the generated JSON key for dialogflowV2 in plugins.js to test the bot.` + )) }) diff --git a/intent/webpack.config.js b/intent/webpack.config.js index 4b545fa..03250b1 100644 --- a/intent/webpack.config.js +++ b/intent/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) const BOTONIC_PATH = path.resolve( __dirname, @@ -121,8 +102,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -165,16 +144,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -200,7 +180,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -333,14 +313,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } } diff --git a/nlu-assistant/README.md b/nlu-assistant/README.md deleted file mode 100644 index 89d46ed..0000000 --- a/nlu-assistant/README.md +++ /dev/null @@ -1,520 +0,0 @@ -# NLU Assistant - -This example shows you how to train a a model using Botonic NLU and take profit of it in order to recognize user intents. This example has been trained in order to recognize the following intents: - -- `Greetings`: A user greets the bot. **e.g:** `hey!` -- `Jokes`: A user asks for a joke. **e.g:** `bot, tell me a joke` -- `GetDirections`: A user asks for directions. **e.g:** `how can I go to Barcelona?` -- `Weather`: A user asks for weather's information. **e.g:** `what's the weather like?` -- `Thanks`: A user thanks the bot. **e.g:** `thanks for your help!` -- `Farewell`: A user says goodbye to the bot. **e.g:** `ok, bye bye!` - -**What's in this document?** - -- [How to use this example](#how-to-use-this-example) -- [Walk-through](#walk-through) - - [1. Create your knowledge base](#1-create-your-knowledge-base) - - [2. Creating your Neural Network model](#2-creating-your-neural-network-model) - - [2.1 Understanding the main points of a model](#21-understanding-the-main-points-of-a-model) - - [2.2 Creating your own custom model](#22-creating-your-own-custom-model) - - [2.3 Creating your own custom data preprocessors](#23-creating-your-own-custom-data-preprocessors) - - [2.3.1 Creating a custom tokenizer](#231-creating-a-custom-tokenizer) - - [2.3.2 Creating a custom normalizer](#232-creating-a-custom-normalizer) - - [2.4 Using your own custom model](#24-using-your-own-custom-model) - - [2.4.1 Initializing the NLU with custom preprocessors](#241-initializing-the-nlu-with-custom-preprocessors) - - [2.4.2 Preparing the data](#242-preparing-the-data) - - [2.4.3 Model configuration and tunning](#243-model-configuration-and-tunning) - - [2.4.4 Training and evaluating the model](#244-training-and-evaluating-the-model) - - [2.5 Run the model](#25-run-the-model) - - [3. Defining the conversational flow](#3-defining-the-conversational-flow) - - [4. Using the Botonic NLU plugin](#4-using-the-botonic-nlu-plugin) - -## How to use this example - -1. From your command line, download the example by running: - ```bash - $ botonic new nlu-assistant - ``` -2. `cd` into `` directory that has been created. -3. Run `botonic serve` to test it in your local machine. - -Additionally, you can try to add new examples in **src/nlu/utterances/en** and train the model again by running: - -```bash -$ botonic train -``` - -Now you can test how this new model performs by running `botonic serve` again and try with new inputs. - -## Walk-through - -In this walk-through we will cover the most important steps that has been done in order to develop this example. - -#### 1. Create your knowledge base - -First of all, we need to define some training examples to feed our neural network (**NN**). To do so, we are going to create **6** different files, one for each intent. Under **src/nlu/utterances/en/** create the following files: - -- `Greeting.txt` -- `Joke.txt` -- `GetDirections.txt` -- `Weather.txt` -- `Thanks.txt` -- `Farewell.txt` - -Now, inside every one of them, add several examples as follows: - -**src/nlu/utterances/en/GetDirections.txt** - -``` -how can i reach a train station? -get directions to the nearest ATM -how do I get home taking E120 -find the fastest route to Baxter Building 42nd street Madison Avenue Manhattan -how to get to the Main Street avoiding heavy traffic -... -``` - -Each example should be defined in single line and should be separated of the next one by a newline. - -> **Note:** Take into account that in Deep Learning models, the more examples you train with, the better the neural network will perform. For the sake of this example, we have added 50 examples per intent (but they could be insufficient). - -Once you have defined all the examples for every intent, we are going to proceed with the model creation. - -#### 2. Creating your Neural Network model - -Under **src/nlu/** you will notice that there is a `train-model.ts` file. This is the main entry point responsible of running your model. - -##### 2.1 Understanding the main points of a model - -We could start by using the example which comes with the `nlu` example: - -```ts -import { BotonicNLU, ModelTemplatesType } from '@botonic/nlu' -import { join } from 'path' - -const LANGUAGE = 'en' -const UTTERANCES_PATH = join( - process.cwd(), - 'src', - 'nlu', - 'utterances', - LANGUAGE -) - -const nlu = new BotonicNLU() -const data = nlu.readData({ - path: UTTERANCES_PATH, - language: LANGUAGE, - maxSeqLen: 20, -}) - -const [xTrain, xTest, yTrain, yTest] = nlu.trainTestSplit({ - data: data, - testPercentage: 0.1, -}) - -;(async () => { - await nlu.createModel({ - template: ModelTemplatesType.SIMPLE_NN, - learningRate: 5e-3, - trainableEmbeddings: true, - }) - await nlu.train(xTrain, yTrain, { epochs: 8 }) - await nlu.saveModel() - console.log('Accuracy:', nlu.evaluate(xTest, yTest)) - console.log('Model saved.') -})() -``` - -Let's break it down into little pieces: - -1. First of all, you need to define which is the target language to be trained. You must specify both the language and the directory from where the utterances will be retrieved: - ```ts - const LANGUAGE = 'en' - const UTTERANCES_PATH = join( - process.cwd(), - 'src', - 'nlu', - 'utterances', - LANGUAGE - ) - ``` -2. Now we will initialize an instance of `BotonicNLU` and feed it with our data: - ```ts - const nlu = new BotonicNLU() - const data = nlu.readData({ - path: UTTERANCES_PATH, - language: LANGUAGE, - maxSeqLen: 20, - }) - ``` - `maxSeqLen` defines the maximum length of the sequences generated to train the NN (_it's pretty uncommon that a user types an input of more of 20 words_). -3. Before start training our model, we need to split the data into two sets; one for training and one for evaluating the model. This can be done with: - ```ts - const [xTrain, xTest, yTrain, yTest] = nlu.trainTestSplit({ - data: data, - testPercentage: 0.1, - }) - ``` - `testPercentage` is the percentage of examples that will be used to do the evaluation. -4. Finally, we will use a predefined template of NN to do the training: - - ```ts - ;(async () => { - await nlu.createModel({ - template: ModelTemplatesType.SIMPLE_NN, - learningRate: 5e-3, - trainableEmbeddings: true, - }) - await nlu.train(xTrain, yTrain, { epochs: 8 }) - await nlu.saveModel() - console.log('Accuracy:', nlu.evaluate(xTest, yTest)) - console.log('Model saved.') - })() - ``` - - We won't go over the details a this moment, but it's important you notice that the main logic here consists of: specifying a model, training it during some epochs and finally save and evaluate the results. - -5. Now run `botonic train`. - - **Output:** - - ``` - ... - 3283ms 15198us/step - acc=1.00 loss=8.80e-4 val_acc=0.167 val_loss=6.70 - Accuracy: 0.6333333333333333 - Model saved. - ``` - - As you can see, the `acc` (training accuracy) is high, and `val_acc` (validation accuracy) is low. This means that our network learned to correctly classify the inputs that it has already seen during the training, but the performance when seeing new examples is really poor. This problem is also known as **Overffiting**. - -We could try to do lots of changes within this code, but we will never obtain significantly better results. To address this problem we are going to try the other way of implementing a model. This option offers the possibility of creating a custom model without using any template. - -##### 2.2 Creating your own custom model - -In this example, we are going to create a common model's configuration in terms of natural language processing. The sequential model is composed of three different layers: an **Embedding** layer, an **LSTM** (Long Short-Term Memory) layer, and finally a **Dense** layer with a `softmax` activation function to compute all intent probabilities. - -Let's start by creating a new file called `lstm-model.ts` under **src/nlu/**. - -Using the power of [tfjs](https://www.tensorflow.org/js) you can define a new model as follows: - -**src/nlu/train-model.ts** - -```ts -import { WordEmbeddingsConfig } from '@botonic/nlu/dist/types' -import { WordEmbeddingsManager } from '@botonic/nlu/dist/embeddings/word-embeddings-manager' - -import { layers, sequential, Sequential } from '@tensorflow/tfjs-node' -import { DenseLayerArgs } from '@tensorflow/tfjs-layers/dist/layers/core' -import { LSTMLayerArgs, ModelCompileArgs } from '@tensorflow/tfjs-layers/dist' - -interface LayersConfig { - LSTM: LSTMLayerArgs - DENSE: DenseLayerArgs -} - -export const LSTMModel = async ( - maxSeqLen: number, - wordEmbeddingsConfig: WordEmbeddingsConfig, - layersConfig: LayersConfig, - modelCompileArgs: ModelCompileArgs -): Promise => { - const wordEmbeddingsManager = await WordEmbeddingsManager.withConfig( - wordEmbeddingsConfig - ) - const wordEmbeddingsMatrix = wordEmbeddingsManager.matrix - const model = sequential() - model.add( - layers.embedding({ - inputDim: wordEmbeddingsMatrix.shape[0], - outputDim: wordEmbeddingsMatrix.shape[1], - inputLength: maxSeqLen, - trainable: false, - weights: [wordEmbeddingsMatrix], - }) - ) - model.add(layers.lstm(layersConfig.LSTM)) - model.add(layers.dense(layersConfig.DENSE)) - model.compile(modelCompileArgs) - model.summary() - return model -} -``` - -We have defined here the interface `LayersConfig` to be able to tune the model easily from the outside. In this piece of we are: - -1. Loading the matrix of word embeddings needed to initialize the embedding layer. - ```ts - const wordEmbeddingsManager = await WordEmbeddingsManager.withConfig( - wordEmbeddingsConfig - ) - const wordEmbeddingsMatrix = wordEmbeddingsManager.matrix - ``` -2. Place the embedding layer as the first layer in the stack: - ```ts - const model = sequential() // initializing the stack of layers - model.add( - layers.embedding({ - inputDim: wordEmbeddingsMatrix.shape[0], - outputDim: wordEmbeddingsMatrix.shape[1], - inputLength: maxSeqLen, - trainable: false, - weights: [wordEmbeddingsMatrix], - }) - ) - ``` - > **Note:** It's very important you define correctly these parameters for a correct functioning of the model. - > - > - **`inputDim`**: size of the vocabulary. - > - **`outputDim`**: size of the pretrained embedding vectors. - > - **`inputLength`**: must be the `maxSeqLen`. - > - **`trainable`**: whether to freeze the weights of WE or leave them to variate. If you want to take all the profit of word embeddings, set it to `false`. - > **`weights`**: Loaded word embeddings matrix. -3. Add the rest of layers: - ```ts - model.add(layers.lstm(layersConfig.LSTM)) - model.add(layers.dense(layersConfig.DENSE)) - ``` -4. Configure and prepare the model for training and evaluation. - ```ts - model.compile(modelCompileArgs) model.summary() - ``` - -##### 2.3 Creating your own custom data preprocessors - -In certain situations, you would want to tell `BotonicNLU` how to preprocess the data (removing accents, question marks, ...). To do so, `BotonicNLU` let's you to inject your own `tokenizer`, `normalizer` and/or `stemmer`. In this example we are going to use a custom `tokenizer` and `normalizer`. In order to maintain the project cleaner, we have created a `preprocessing-tools` directory in **src/nlu/**. - -###### 2.3.1 Creating a custom tokenizer - -**src/nlu/preprocessing-tools/tokenizer.ts** - -```ts -const TreebankWordTokenizer = require('natural/lib/natural/tokenizers/treebank_word_tokenizer') - -export const tokenizer = new TreebankWordTokenizer() -``` - -In this example, we will be using [`natural`](https://github.com/NaturalNode/natural#tokenizers)'s `TreebankWordTokenizer` which works well for every language. To define your tokenizer, the only requirement is to export a class that implements a `tokenize` method. - -> **Note:** As `natural` is a library centered on Node and we need to have the bot working in a browser environment, we have to require the class explicitly from the absolute path. - -###### 2.3.2 Creating a custom normalizer - -**src/nlu/preprocessing-tools/normalizer.ts** - -```ts -export class DefaultNormalizer { - normalize(sentence) { - return sentence.toLowerCase().replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, '') - } -} -``` - -In this normalizer, we are removing special characters we don't want to trait when the user enters an input. As well as `tokenizer`, this class must implement a `normalize` method. - -##### 2.4 Using your own custom model - -Now, let's move again to **src/nlu/train-model.ts**. - -###### 2.4.1 Initializing the NLU with custom preprocessors - -**src/nlu/train-model.ts** - -```ts -import { BotonicNLU } from '@botonic/nlu' -import { join } from 'path' -import { train } from '@tensorflow/tfjs-node' -import { DenseLayerArgs } from '@tensorflow/tfjs-layers/dist/layers/core' -import { LSTMLayerArgs, ModelCompileArgs } from '@tensorflow/tfjs-layers/dist' - -import { tokenizer } from './preprocessing-tools/tokenizer' -import { DefaultNormalizer } from './preprocessing-tools/normalizer' -import { LSTMModel } from './lstm-model' -import { readdirSync } from 'fs' - -const LANGUAGE = 'en' -const UTTERANCES_PATH = join( - process.cwd(), - 'src', - 'nlu', - 'utterances', - LANGUAGE -) -const MAX_SEQ_LEN = 20 -const INTENTS_COUNT = readdirSync(UTTERANCES_PATH).length - -const nlu = new BotonicNLU({ - tokenizer: tokenizer, - normalizer: new DefaultNormalizer(), -}) -``` - -Call `BotonicNLU` constructor and initialize it by passing your imported tokenizer and normalizer. - -###### 2.4.2 Preparing the data - -**src/nlu/train-model.ts** (continuation) - -```ts -const data = nlu.readData({ - path: UTTERANCES_PATH, - language: LANGUAGE, - maxSeqLen: MAX_SEQ_LEN, -}) -const [xTrain, xTest, yTrain, yTest] = nlu.trainTestSplit({ - data: data, - testPercentage: 0.15, - stratify: false, -}) -``` - -At this point, you only had to call `readData` and all the preprocessing will be handled internally with the preprocesors you have injected before. As we have an homogenized distribution of the data (same number of examples per intent), we will be setting `stratify` option to `false`. - -###### 2.4.3 Model configuration and tunning - -**src/nlu/train-model.ts** (continuation) - -```ts -const WE_CONFIG = { - language: LANGUAGE, - dimension: 50, - type: 'glove', - vocabulary: nlu.vocabulary, -} as WordEmbeddingsConfig - -const LAYERS_CONFIG = { - LSTM: { - units: 32, - dropout: 0.4, - recurrentDropout: 0.4, - } as LSTMLayerArgs, - DENSE: { - units: INTENTS_COUNT, - activation: 'softmax', - } as DenseLayerArgs, -} -``` - -Time to configure our model. - -- **Word Embeddings**: - - Here we have chosen [`glove`](https://nlp.stanford.edu/projects/glove/) `50d` pretrained word embeddings. We must pass here the vocabulary extracted from previous data processing. -- **LSTM Layer**: - - As we said before, we want our model to be less rigid when predicting over new inputs. To achive that, we use a technique called `regularization` that can be done by specifying `dropout` parameters to this layer. Also, reducing the number of `units` (neurons per layer) will help. -- **Dense Layer**: - - This is the very last layer and **must** have as many units as intents we are targeting. We have to make use of `softmax` activation as we want to compute the probabilities of intents so the sum of all of them is equal to 1. - -###### 2.4.4 Training and evaluating the model - -**src/nlu/train-model.ts** (continuation) - -```ts -const LEARNING_RATE = 5e-3 -const MODEL_COMPILE_ARGS = { - optimizer: train.adam(LEARNING_RATE), - loss: 'sparseCategoricalCrossentropy', - metrics: ['accuracy'], -} as ModelCompileArgs - -const EPOCHS = 16 - -;(async () => { - nlu.model = await LSTMModel( - MAX_SEQ_LEN, - WE_CONFIG, - LAYERS_CONFIG, - MODEL_COMPILE_ARGS - ) - await nlu.train(xTrain, yTrain, { epochs: EPOCHS }) - await nlu.saveModel() - console.log('Accuracy:', nlu.evaluate(xTest, yTest)) - console.log('Model saved.') -})() -``` - -Now we have to add the compilation parameters for our model, this is: choosing an `optimizer`, a `loss` (`sparseCategoricalCrossentropy` for multiple classes) and the `metrics` we want to track. Here the most obvious option is to use the `accuracy` metric. -Finally, we put all the pieces together so we: - -1. Assign our model to current `nlu` instance with `nlu.model`. -2. Do iterative cycles of training during the defined number of `epochs`. -3. Save the model. -4. Evaluate it. - -##### 2.5 Run the model - -Now, run `botonic train` to train with your custom model. - -**Output:** - -``` -... -1979ms 9703us/step - acc=0.897 loss=0.261 val_acc=0.961 val_loss=0.154 -Accuracy: 0.8888888888888888 -Model saved. -``` - -As you can see, we have been able to increase our accuracy up to a 88% and have more closer values between `acc` and `val_acc` which are good signs that our model is working properly. Also the values for both losses are decreasing over every training epoch. So we are now ready to define our conversational flow! - -> **Note:** These results are not deterministic because NN weights are initialized with random values, so maybe you have to run the `botonic train` script various times before obtaining a convincing result. - -#### 3. Defining the conversational flow - -Once we have our model trained, the necessary information to make predictions will be stored under **src/nlu/models/en/** directory. -We can now move on and declare with what actions must respond the bot for every intent. We have created one class per intent with the same name, to be easier to identify. - -**src/routes.js** - -```javascript -import Greetings from './actions/Greetings' -import Farewell from './actions/Farewell' -import Jokes from './actions/Jokes' -import GetDirections from './actions/GetDirections' -import Weather from './actions/Weather' -import Thanks from './actions/Thanks' -import NotFound from './actions/NotFound' - -const withConfidence = (input, intent) => - input.intent === intent && input.confidence >= 0.6 - -export const routes = [ - { input: i => withConfidence(i, 'Greetings'), action: Greetings }, - { input: i => withConfidence(i, 'Farewell'), action: Farewell }, - { input: i => withConfidence(i, 'Jokes'), action: Jokes }, - { input: i => withConfidence(i, 'GetDirections'), action: GetDirections }, - { input: i => withConfidence(i, 'Weather'), action: Weather }, - { input: i => withConfidence(i, 'Thanks'), action: Thanks }, - { path: '404', action: NotFound }, -] -``` - -As you would observe, here we don't want to trigger actions if the confidence for an input is lower than a 60%. We have encapsulated this logic into the `withConfidence` function, that also checks if the returned intent for input is the same that we are expecting. If user input has predicted an intent with equal or more accuracy of 60%, it will trigger automatically the specified action. It's that easy! - -> We are not covering the implementation of the actions in this explanation, but you could put it there whatever logic you want. - -#### 4. Using the Botonic NLU plugin - -Last step, but no less important, is to load the Botonic NLU plugin in **src/plugins.js** file, which is the one who knows how to run the predictions in a browser environment. - -**src/plugins.js** - -```javascript -import { tokenizer } from './nlu/preprocessing-tools/tokenizer' -import { DefaultNormalizer } from './nlu/preprocessing-tools/normalizer' - -export const plugins = [ - { - id: 'nlu', - resolve: require('@botonic/plugin-nlu'), - options: { - en: { - tokenizer: tokenizer, - normalizer: new DefaultNormalizer(), - }, - }, - }, -] -``` - -Here the most important thing is that you **must** initialize every language you want your bot supports with its locale code (`en` in this example) and use pass as configuration the same tokenizer and normalizer we have used to train the model. Otherwise, your results will differ from the ones obtained in the training. - -Finally run `botonic serve` to try the bot and deploy it where you desire... and we are done! πŸŽ‰ diff --git a/nlu-assistant/babel.config.js b/nlu-assistant/babel.config.js deleted file mode 100644 index 7325b98..0000000 --- a/nlu-assistant/babel.config.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This babel configuration is used along with Jest for execute tests, - * do not modify to avoid conflicts with webpack.config.js. - */ - -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - targets: { - node: 'current', - }, - }, - ], - [ - '@babel/react', - { - targets: { - node: 'current', - }, - }, - ], - ], - plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', - ], -} diff --git a/nlu-assistant/package.json b/nlu-assistant/package.json deleted file mode 100644 index 2b8935c..0000000 --- a/nlu-assistant/package.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "botonic-nlu-assistant", - "version": "1.0.0", - "scripts": { - "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } - }, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/plugin-intent-classification": "~0.20.0", - "@botonic/react": "~0.21.0", - "axios": "^0.21.0", - "leaflet": "^1.7.1", - "natural": "^2.1.5", - "react-leaflet": "^3.0.2" - }, - "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "babel-preset-es2015": "^6.24.1", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", - "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", - "null-loader": "^4.0.1", - "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "util": "^0.12.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" - } -} diff --git a/nlu-assistant/src/actions/Farewell.jsx b/nlu-assistant/src/actions/Farewell.jsx deleted file mode 100644 index c8abdb9..0000000 --- a/nlu-assistant/src/actions/Farewell.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - let _ = this.context.getString - return {_('farewell')} - } -} diff --git a/nlu-assistant/src/actions/GetDirections.jsx b/nlu-assistant/src/actions/GetDirections.jsx deleted file mode 100644 index 8014a5a..0000000 --- a/nlu-assistant/src/actions/GetDirections.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -import MapMessage from '../webchat/map-message' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - let _ = this.context.getString - - return ( - <> - {_('getDirections')} - - {_('getDirectionsSuggestion')} - - ) - } -} diff --git a/nlu-assistant/src/actions/Greetings.jsx b/nlu-assistant/src/actions/Greetings.jsx deleted file mode 100644 index 1bad20c..0000000 --- a/nlu-assistant/src/actions/Greetings.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - let _ = this.context.getString - return ( - <> - {_('greetings.intro')} - {_('greetings.explanation')} - {_('greetings.tryMe')} - - ) - } -} diff --git a/nlu-assistant/src/actions/Jokes.jsx b/nlu-assistant/src/actions/Jokes.jsx deleted file mode 100644 index 91b61f7..0000000 --- a/nlu-assistant/src/actions/Jokes.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' -import axios from 'axios' -export default class extends React.Component { - static contextType = RequestContext - - static async botonicInit() { - try { - const res = await axios.get('https://api.jokes.one/jod') - return { joke: res.data.contents.jokes[0].joke.text } - } catch (e) { - return { joke: undefined } - } - } - - render() { - let _ = this.context.getString - return ( - <> - {_('jokes')} - {this.props.joke ? ( - "{this.props.joke}" - ) : ( - {_('jokesError')} - )} - - ) - } -} diff --git a/nlu-assistant/src/actions/NotFound.jsx b/nlu-assistant/src/actions/NotFound.jsx deleted file mode 100644 index f2e952c..0000000 --- a/nlu-assistant/src/actions/NotFound.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - let _ = this.context.getString - return {_('notFound')} - } -} diff --git a/nlu-assistant/src/actions/Thanks.jsx b/nlu-assistant/src/actions/Thanks.jsx deleted file mode 100644 index 9e61e8f..0000000 --- a/nlu-assistant/src/actions/Thanks.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - let _ = this.context.getString - return {_('thanks')} - } -} diff --git a/nlu-assistant/src/actions/Weather.jsx b/nlu-assistant/src/actions/Weather.jsx deleted file mode 100644 index 834acdc..0000000 --- a/nlu-assistant/src/actions/Weather.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -const renderTable = () => - '### Weather Report\n
SunSunny
\n\n| πŸ“ | 🌑️ | πŸ’¨ |\n| ------ | ------ | ------ |\n| **Barcelona** | 25ΒΊC | 10km/h |\n' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - let _ = this.context.getString - return ( - <> - {_('weather')} - {renderTable()} - - ) - } -} diff --git a/nlu-assistant/src/assets/bot-icon-purple.svg b/nlu-assistant/src/assets/bot-icon-purple.svg deleted file mode 100644 index 8fdc7bd..0000000 --- a/nlu-assistant/src/assets/bot-icon-purple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/nlu-assistant/src/assets/bot-icon-white.svg b/nlu-assistant/src/assets/bot-icon-white.svg deleted file mode 100644 index c741250..0000000 --- a/nlu-assistant/src/assets/bot-icon-white.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/nlu-assistant/src/assets/chat.svg b/nlu-assistant/src/assets/chat.svg deleted file mode 100644 index c195a5e..0000000 --- a/nlu-assistant/src/assets/chat.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/nlu-assistant/src/assets/close.svg b/nlu-assistant/src/assets/close.svg deleted file mode 100644 index 6a51548..0000000 --- a/nlu-assistant/src/assets/close.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/nlu-assistant/src/assets/close_header.svg b/nlu-assistant/src/assets/close_header.svg deleted file mode 100644 index a2759b0..0000000 --- a/nlu-assistant/src/assets/close_header.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/nlu-assistant/src/index.js b/nlu-assistant/src/index.js deleted file mode 100644 index 7a88500..0000000 --- a/nlu-assistant/src/index.js +++ /dev/null @@ -1 +0,0 @@ -export const config = { defaultDelay: 0, defaultTyping: 1 } diff --git a/nlu-assistant/src/locales/index.js b/nlu-assistant/src/locales/index.js deleted file mode 100644 index 8e3933a..0000000 --- a/nlu-assistant/src/locales/index.js +++ /dev/null @@ -1,53 +0,0 @@ -export const locales = { - en: { - greetings: { - intro: [ - 'Hi human! How are you? πŸ˜„', - 'Hello buddy! How is it going? 😎', - "Hey there! I'm using Botonic. πŸ€–", - ], - explanation: [ - "I'm a bot who has been trained with some intents, so I have learnt from examples.", - ], - tryMe: ["Now try to text me: what's the weather like? πŸ˜™"], - }, - farewell: [ - 'See you later, aligator! 🐊', - 'Nice to meet you, have a nice day! πŸ’ƒ', - 'Ciao bambino! πŸ‘¨πŸ»', - ], - thanks: [ - "You're welcome. I hope I made your day a little bit more exciting. πŸ™Œ", - "Don't mention it. You are a treasure. πŸ’Ž", - 'Thank you for making my day more enjoyable. I was getting bored lost in my server. πŸ˜ͺ', - ], - getDirections: [ - 'This is all I have been able to discover.', - 'This is all I have found.', - 'You seem to be here more or less.', - ], - getDirectionsSuggestion: [ - 'I suggest you to pick the nearest metro from here.', - 'My recommendation would be to order a taxi to go there faster.', - 'What about picking the Bus 78? You will be saving some time.', - ], - jokes: [ - 'What about this one? πŸ˜†', - 'Today you are naughty. I like it. 😈', - "I have this one up my sleeve. I promise I'm not using any kind of API. πŸ™ˆ", - ], - jokesError: [ - "I'm sorry. I could not find anything in those servers. I'm a mess. 😒", - ], - weather: [ - 'This is what I can tell you so far.', - 'I hope this can be useful.', - 'This is all I have been able to discover.', - ], - notFound: [ - "I'm sorry, I swear you I have tried all my best. I'm just a bot trying to look human. 😞", - "I'm sorry, I'm better at numbers than letters, although I have tried. πŸ€“", - '404 human understanding not found. ❌', - ], - }, -} diff --git a/nlu-assistant/src/nlp/data/en/Farewell.yaml b/nlu-assistant/src/nlp/data/en/Farewell.yaml deleted file mode 100644 index aba61f7..0000000 --- a/nlu-assistant/src/nlp/data/en/Farewell.yaml +++ /dev/null @@ -1,53 +0,0 @@ -intent: Farewell - -samples: - - bye bye - - ciao - - bye! - - sweet dreams - - okay have a good night - - good night for now - - night - - good night bye - - goodnight - - good tonight - - goodnight - - bye good night - - thank you good night - - night - - alright goodnight - - bye good night - - have a good night - - good night see you tomorrow - - good night - - good night to you - - have a good day - - have a nice day! - - hope you have a nice day - - hope to see you later - - that's it goodbye - - now bye - - okay bye - - alright bye - - bye bye see you - - see ya - - goodbye see you later - - okay thank you bye - - bye-bye - - bye - - goodbye - - bye bye see you soon - - okay see you later - - go to bed - - see you tomorrow - - bye for now - - see you soon - - I must go - - ok bye - - good bye - - till next time - - I said bye - - see you - - bye bye - - talk to you later - - never mind bye \ No newline at end of file diff --git a/nlu-assistant/src/nlp/data/en/GetDirections.yaml b/nlu-assistant/src/nlp/data/en/GetDirections.yaml deleted file mode 100755 index 7c3d095..0000000 --- a/nlu-assistant/src/nlp/data/en/GetDirections.yaml +++ /dev/null @@ -1,53 +0,0 @@ -intent: GetDirections - -samples: - - how can i reach a train station? - - get directions to the nearest ATM - - how do I get home taking E120 - - find the fastest route to Baxter Building 42nd street Madison Avenue Manhattan - - how to get to the Main Street avoiding heavy traffic - - can you direct me to the mobile office - - can you find me the best route to Ohio - - directions to bar with light traffic - - show me the way home - - how do I get to San Diego from my house - - how can I go to London? - - directions to New York avoiding heavy traffic - - give me directions to Starbucks - - how to get to the nearest petrol station - - what's the best way to get home - - directions to New York avoiding toll roads - - directions from Denver to Ohio - - start navigation to nearest Starbucks via fastest route - - show me directions to the Heathrow airport - - find directions to the nearest grocery shop - - what's the best way to get home from here - - show me the best way to get to the airport - - can you please direct me to the nearest petrol pump - - how to get to Danemea High street St Mary Mead - - what is the fastest way to my house - - directions to metro with fastest way - - how can i reach this site? - - how do I get to Central Park - - the quickest route to 420 Paper St Wilmington DE 19886 - - show me the fastest way to get to the office - - display directions to San Francisco - - show me directions to nearest gas station - - directions to restaurant with light traffic - - route me to Fort Bragg from my current location - - how do I get to the Cherry Hill mall from here - - let me see the directions to go to 301 Cobblestone Way Bedrock 70777 - - how do I get to my house from here - - I need directions to Pittsburgh - - driving directions to Longbourn near Meryton Hertfortshire - - how to get to nearest petrol station - - tell me the best way to go to goiko grill - - show me the shortest way to go home - - which is the shortest way to go home - - tell me which is the nearest parking - - how do i go to montauk avoiding tolls? - - directions to disneyworld avoiding traffic - - show me the way to go to 33 greene street - - give me directions to my parents' place avoiding toll roads - - show me directions to my next meeting avoiding traffic - - quickest directions to go pick up my son at soccer practice \ No newline at end of file diff --git a/nlu-assistant/src/nlp/data/en/Greetings.yaml b/nlu-assistant/src/nlp/data/en/Greetings.yaml deleted file mode 100644 index 948d837..0000000 --- a/nlu-assistant/src/nlp/data/en/Greetings.yaml +++ /dev/null @@ -1,53 +0,0 @@ -intent: Greetings - -samples: - - good afternoon - - hello there - - hello - - hi - - hey - - I greet you - - howdy - - hi there - - hi - - good morning - - hey there - - heya - - just going to say hi - - hello again - - greetings - - a good day - - what's up? - - how are you going? - - have a nice morning - - good morning too - - top of the morning to you - - it's good to see you too - - it's nice to see you - - good to see you again - - nice to see you again - - great to see you too - - great to see you again - - always a pleasure to see you - - great to see you - - how good it is to see you - - it's good to see you - - good to see you - - I'm glad to see you - - nice to see you - - glad to see you - - I am glad to see you again - - glad to see you too - - what is on your mind - - good what's up - - what's up today - - what's shaking - - what's cooking - - what is going on - - then what's up - - what is up - - wassup - - what's happened - - hey what's up - - what's cracking - - what's up \ No newline at end of file diff --git a/nlu-assistant/src/nlp/data/en/Jokes.yaml b/nlu-assistant/src/nlp/data/en/Jokes.yaml deleted file mode 100644 index 719dfc3..0000000 --- a/nlu-assistant/src/nlp/data/en/Jokes.yaml +++ /dev/null @@ -1,53 +0,0 @@ -intent: Jokes - -samples: - - tell me some jokes - - joke please - - any good jokes - - tell me a joke please - - jokes - - let's hear funny jokes - - can you tell me a joke please - - more jokes - - I want a funny joke - - random joke - - tell me a joke - - some joke - - tell me the best joke ever - - do you joke well - - give me a joke - - let's joke - - tell me a funny joke - - I'm feeling glum make me laugh - - tell a joke - - tell me a scary joke - - tell me a funny joke - - can you tell jokes - - it would be nice to hear a joke right now - - can you joke well - - do you know any jokes - - tell me a silly joke - - do you have any jokes - - joke - - tell me a funny joke please - - tell me your funniest joke - - tell me a nice joke - - I want to hear a joke - - can you tell me another joke - - I want some jokes - - tell me a joke - - tell me something funny - - hilarious joke - - funny jokes - - can you tell me a joke? - - i would like to hear a joke - - tell me a joke - - a joke please - - tell me a joke please - - i would like to hear a joke - - i would loke to hear a joke, please - - can you tell jokes? - - please tell me a joke - - i need to hear a joke - - make me laugh a lot - - please, a joke \ No newline at end of file diff --git a/nlu-assistant/src/nlp/data/en/Thanks.yaml b/nlu-assistant/src/nlp/data/en/Thanks.yaml deleted file mode 100644 index 55c3fdf..0000000 --- a/nlu-assistant/src/nlp/data/en/Thanks.yaml +++ /dev/null @@ -1,53 +0,0 @@ -intent: Thanks - -samples: - - thanks - - many thanks - - thanks a lot - - thanks a bunch - - thank you very much - - it’s very kind of you - - i really appreciate it - - thank you for everything - - i owe you one - - i really appreciate your help - - i’m so grateful - - thanks a million - - thanks for everything - - thanks so much - - i can’t thank you enough - - i cannot express my appreciation - - i appreciate your time - - a million thanks to you - - i owe you big time - - i truly appreciate you - - thanks a ton - - how thoughtful of you! - - i’ll forever be grateful - - please accept my deepest thanks - - you’re too kind - - you are an angel - - you are the best - - you have my gratitude - - you made my day - - you saved my day - - you’re a dear - - you’re a life saver - - you’re awesome - - you’re great - - you’ve saved my life - - accept my endless gratitude - - all i can say is, thanks! - - all my love and thanks to you - - consider yourself heartily thanked - - how can i ever possibly thank you - - how can i ever thank you enough - - how can i show you how grateful i am? - - i couldn’t have done it without you - - i do not know what i would do without you - - i don’t know what to say! - - oh, you shouldn’t have! - - i humbly thank you - - please accept my best thanks - - i’m grateful for your assistance - - i’m really grateful for your help \ No newline at end of file diff --git a/nlu-assistant/src/nlp/data/en/Weather.yaml b/nlu-assistant/src/nlp/data/en/Weather.yaml deleted file mode 100644 index 76772be..0000000 --- a/nlu-assistant/src/nlp/data/en/Weather.yaml +++ /dev/null @@ -1,53 +0,0 @@ -intent: Weather - -samples: - - tell me if it's cold outside - - will it be warm tomorrow - - I'd like to know the temperature - - do you know if it's cold now - - is it going to be chilly tomorrow - - temperature - - show me the temperature - - I'd like to know the temperature in London - - what will the temperature be in Munich on Monday - - do you know if it's cold outside now - - at New York is there cold - - today's temperature - - is it hot now - - temperature tonight - - temperature in Bangkok - - temperature in Moscow - - what's the temperature in Moscow in Celsius - - allright is it cold tonight in Aspen - - show me the temperature for this weekend - - check temperature - - how cold is it - - is there warm in New York - - chances of volcano eruption in Sicilia this month - - when will it rain today - - are there hurricanes expected - - is it going to snow in Paris on Sunday - - is it supposed to rain in Madrid next weekend - - is it supposed to snow on Friday - - is it going to be fog tomorrow in Paris - - is it going to rain - - will it be raining tomorrow - - is it going to rain this Friday - - is it supposed to snow today - - any snow in the forecast - - any chance of tornadoes on the weekend - - chances of storm - - when will it rain - - any chance of snow showers tomorrow in Barcelona - - what's the weather like? - - when will it rain in Oslo today - - is it supposed to rain in London - - are there freezing rains expected in Glasgow - - will it be foggy next Saturday in San Francisco - - is it supposed to snow - - what is the weather - - will it be foggy - - is it raining - - will it rain tomorrow in Paris - - any chance of thunderstorms in Colorado - - what is the weather forecast for snow in New York today \ No newline at end of file diff --git a/nlu-assistant/src/nlp/tasks/intent-classification/models/en/.gitkeep b/nlu-assistant/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nlu-assistant/src/nlp/tasks/intent-classification/train.ts b/nlu-assistant/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index 54f7ebb..0000000 --- a/nlu-assistant/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 20 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/nlu-assistant/src/plugins.js b/nlu-assistant/src/plugins.js deleted file mode 100644 index dd9b5a9..0000000 --- a/nlu-assistant/src/plugins.js +++ /dev/null @@ -1,9 +0,0 @@ -export const plugins = [ - { - id: 'intent-classification', - resolve: require('@botonic/plugin-intent-classification'), - options: { - locales: ['en'], - }, - }, -] diff --git a/nlu-assistant/src/routes.js b/nlu-assistant/src/routes.js deleted file mode 100644 index 380b639..0000000 --- a/nlu-assistant/src/routes.js +++ /dev/null @@ -1,19 +0,0 @@ -import Greetings from './actions/Greetings' -import Farewell from './actions/Farewell' -import Jokes from './actions/Jokes' -import GetDirections from './actions/GetDirections' -import Weather from './actions/Weather' -import Thanks from './actions/Thanks' -import NotFound from './actions/NotFound' - -const withConfidence = (input, intent) => input.intents.some(int => int.label === intent && int.confidence >= 0.4) - -export const routes = [ - { input: i => withConfidence(i, 'Greetings'), action: Greetings }, - { input: i => withConfidence(i, 'Farewell'), action: Farewell }, - { input: i => withConfidence(i, 'Jokes'), action: Jokes }, - { input: i => withConfidence(i, 'GetDirections'), action: GetDirections }, - { input: i => withConfidence(i, 'Weather'), action: Weather }, - { input: i => withConfidence(i, 'Thanks'), action: Thanks }, - { path: '404', action: NotFound }, -] diff --git a/nlu-assistant/src/webchat/constants.js b/nlu-assistant/src/webchat/constants.js deleted file mode 100644 index e4742ac..0000000 --- a/nlu-assistant/src/webchat/constants.js +++ /dev/null @@ -1 +0,0 @@ -export const PRIMARY_COLOR = '#7857FF' diff --git a/nlu-assistant/src/webchat/custom-header.jsx b/nlu-assistant/src/webchat/custom-header.jsx deleted file mode 100644 index 336e0fa..0000000 --- a/nlu-assistant/src/webchat/custom-header.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useContext } from 'react' -import styled from 'styled-components' -import Close from '../assets/close_header.svg' -import BotIconWhite from '../assets/bot-icon-white.svg' -import { staticAsset, WebchatContext } from '@botonic/react' -import { PRIMARY_COLOR } from './constants' - -const IconContainer = styled.div` - width: 30px; - height: 30px; - padding: 10px; - display: flex; - align-items: center; - justify-content: center; -` - -const Header = styled.div` - height: 48px; - background: linear-gradient( - 90deg, - rgba(46, 32, 59, 1) 0%, - ${PRIMARY_COLOR} 100% - ); - z-index: 2; - display: flex; - align-items: center; - border-top-right-radius: 5px; - border-top-left-radius: 5px; -` -const Title = styled.h1` - @import url('https://fonts.googleapis.com/css?family=Lato:300,400,700'); - font-size: 16px; - font-weight: 300; - line-height: 1.5px; - color: white; - width: 80%; -` -export const CloseIcon = styled.img` - width: 20px; - height: 30px; -` -export const BotIcon = styled.img` - width: 30px; -` - -export const CustomHeader = () => { - const { getThemeProperty } = useContext(WebchatContext) - const headerTitle = getThemeProperty('header.title') - - return ( - <> -
- - - - {headerTitle} - Online 🟒 - { - Botonic.close() - }} - > - - -
- - ) -} diff --git a/nlu-assistant/src/webchat/index.js b/nlu-assistant/src/webchat/index.js deleted file mode 100644 index 2105332..0000000 --- a/nlu-assistant/src/webchat/index.js +++ /dev/null @@ -1,143 +0,0 @@ -import MapMessage from './map-message' -import BotIconPurple from '../assets/bot-icon-purple.svg' -import BotIconWhite from '../assets/bot-icon-white.svg' -import 'leaflet/dist/leaflet.css' -import { CustomHeader } from './custom-header' -import { PRIMARY_COLOR } from './constants' - -export const webchat = { - storage: sessionStorage, - storageKey: 'botonic-nlu-example', - shadowDOM: true, - - onInit: app => { - app.clearMessages() - app.open() - }, - onOpen: app => app.addUserText('hi'), - theme: { - style: { - fontFamily: '"Helvetica Neue",Arial,sans-serif', - width: 350, - borderRadius: 10, - }, - header: { - image: BotIconWhite, - title: 'Botonic NLU Assistant', - subtitle: '🟒 Online', - custom: CustomHeader, - }, - brand: { - color: `${PRIMARY_COLOR}`, - }, - triggerButton: { - image: BotIconPurple, - }, - scrollbar: { - autoHide: true, - thumb: { - opacity: 1, - bgcolor: `linear-gradient(-131deg,#B09BFF 0%,${PRIMARY_COLOR} 100%)`, - border: '20px', - }, - }, - message: { - bot: { - blobWidth: '75%', - blobTick: false, - image: BotIconPurple, - style: { - background: 'white', - border: `1px solid ${PRIMARY_COLOR}`, - }, - }, - customTypes: [MapMessage], - }, - userInput: { - style: { - background: `linear-gradient(90deg,${PRIMARY_COLOR} 0%, rgba(46, 32, 59, 1) 100%)`, - borderBottomLeftRadius: '10px', - borderBottomRightRadius: '10px', - }, - box: { - style: { - fontFamily: '"Helvetica Neue",Arial,sans-serif', - border: '2px solid white', - color: `${PRIMARY_COLOR}`, - background: 'white', - width: '90%', - borderRadius: 20, - paddingLeft: 20, - marginLeft: 10, - marginRight: 10, - }, - placeholder: 'Type something...', - }, - }, - markdownStyle: ` - p { - font-size: 16px; - font-weight: 300; - line-height: 1.5625; - margin: 0px; - } - - h3 { - margin: 5px; - } - - table { - margin-top: 10px; - border-collapse: collapse; - overflow: hidden; - box-shadow: 0 0 20px rgba(0,0,0,0.1); - border-radius:5px; - } - - th, - td { - border-radius:5px; - padding: 7px; - background-color: rgba(255,255,255,0.2); - color: black; - text-align: center; - } - - thead { - border-radius:5px; - border: 2px solid ${PRIMARY_COLOR}; - th { - border-radius:0px; - border: 1px solid ${PRIMARY_COLOR}; - background-color: #F1EEFF; - } - } - - tbody { - border-radius:5px; - border: 2px solid ${PRIMARY_COLOR}; - tr { - &:hover { - background-color: rgba(255,255,255,0.3); - } - border: 1px solid ${PRIMARY_COLOR}; - } - td { - border: 1px solid ${PRIMARY_COLOR}; - position: relative; - &:hover { - &:before { - content: ""; - position: absolute; - left: 0; - right: 0; - top: -9999px; - bottom: -9999px; - background-color: rgba(255,255,255,0.2); - z-index: -1; - } - } - } - `, - }, -} diff --git a/nlu-assistant/src/webchat/map-message.js b/nlu-assistant/src/webchat/map-message.js deleted file mode 100644 index 4e10227..0000000 --- a/nlu-assistant/src/webchat/map-message.js +++ /dev/null @@ -1,44 +0,0 @@ -import { customMessage, WebchatContext } from '@botonic/react' -import React from 'react' - -let MapContainer, TileLayer -if (typeof window !== 'undefined') { - MapContainer = require('react-leaflet').MapContainer - TileLayer = require('react-leaflet').TileLayer -} - -class MapMessage extends React.Component { - static contextType = WebchatContext - constructor(props) { - super(props) - } - - render() { - if (typeof window !== 'undefined') - return ( - - - - ) - else return null - } -} - -export default customMessage({ - name: 'map-message', - component: MapMessage, - defaultProps: { - style: { - maxWidth: '90%', - borderColor: 'black', - }, - }, -}) diff --git a/nlu-assistant/src/webviews/index.js b/nlu-assistant/src/webviews/index.js deleted file mode 100644 index 4ca4089..0000000 --- a/nlu-assistant/src/webviews/index.js +++ /dev/null @@ -1 +0,0 @@ -export const webviews = [] diff --git a/nlu-assistant/tests/.gitkeep b/nlu-assistant/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nlu-assistant/webpack-entries/dev-entry.js b/nlu-assistant/webpack-entries/dev-entry.js deleted file mode 100644 index 6e32a58..0000000 --- a/nlu-assistant/webpack-entries/dev-entry.js +++ /dev/null @@ -1,15 +0,0 @@ -import { DevApp } from '@botonic/react' - -import { config } from '../src' -import { locales } from '../src/locales' -import { plugins } from '../src/plugins' -import { routes } from '../src/routes' -import { webchat } from '../src/webchat' - -export const app = new DevApp({ - routes, - locales, - plugins, - ...webchat, - ...config, -}) diff --git a/nlu-assistant/webpack-entries/node-entry.js b/nlu-assistant/webpack-entries/node-entry.js deleted file mode 100644 index fbb24ed..0000000 --- a/nlu-assistant/webpack-entries/node-entry.js +++ /dev/null @@ -1,8 +0,0 @@ -import { NodeApp } from '@botonic/react' - -import { config } from '../src' -import { locales } from '../src/locales' -import { plugins } from '../src/plugins' -import { routes } from '../src/routes' - -export const app = new NodeApp({ routes, locales, plugins, ...config }) diff --git a/nlu-assistant/webpack-entries/webchat-entry.js b/nlu-assistant/webpack-entries/webchat-entry.js deleted file mode 100644 index 4bd967e..0000000 --- a/nlu-assistant/webpack-entries/webchat-entry.js +++ /dev/null @@ -1,5 +0,0 @@ -import { WebchatApp } from '@botonic/react' - -import { webchat } from '../src/webchat' - -export const app = new WebchatApp(webchat) diff --git a/nlu-assistant/webpack-entries/webviews-entry.js b/nlu-assistant/webpack-entries/webviews-entry.js deleted file mode 100644 index 030c2fc..0000000 --- a/nlu-assistant/webpack-entries/webviews-entry.js +++ /dev/null @@ -1,6 +0,0 @@ -import { WebviewApp } from '@botonic/react' - -import { locales } from '../src/locales' -import { webviews } from '../src/webviews' - -export const app = new WebviewApp({ webviews, locales }) diff --git a/nlu-assistant/webpack.config.js b/nlu-assistant/webpack.config.js deleted file mode 100644 index 4b545fa..0000000 --- a/nlu-assistant/webpack.config.js +++ /dev/null @@ -1,366 +0,0 @@ -const path = require('path') -const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') -const TerserPlugin = require('terser-webpack-plugin') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') - -const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' -const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' - -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' -const OUTPUT_PATH = path.resolve(__dirname, 'dist') -const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) - -const BOTONIC_PATH = path.resolve( - __dirname, - 'node_modules', - '@botonic', - 'react' -) - -const WEBPACK_MODE = { - DEVELOPMENT: 'development', - PRODUCTION: 'production', -} - -const BOTONIC_TARGETS = { - ALL: 'all', - DEV: 'dev', - NODE: 'node', - WEBVIEWS: 'webviews', - WEBCHAT: 'webchat', -} - -const WEBPACK_ENTRIES_DIRNAME = 'webpack-entries' -const WEBPACK_ENTRIES = { - DEV: 'dev-entry.js', - NODE: 'node-entry.js', - WEBCHAT: 'webchat-entry.js', - WEBVIEWS: 'webviews-entry.js', -} - -const TEMPLATES = { - WEBCHAT: 'webchat.template.html', - WEBVIEWS: 'webview.template.html', -} - -const UMD_LIBRARY_TARGET = 'umd' -const BOTONIC_LIBRARY_NAME = 'Botonic' -const WEBCHAT_FILENAME = 'webchat.botonic.js' - -function sourceMap(mode) { - if (mode === WEBPACK_MODE.PRODUCTION) return 'hidden-source-map' - else if (mode === WEBPACK_MODE.DEVELOPMENT) return 'eval-cheap-source-map' - else - throw new Error( - 'Invalid mode argument (' + mode + '). See package.json scripts' - ) -} - -const optimizationConfig = { - minimize: true, - minimizer: [ - new TerserPlugin({ - parallel: true, - terserOptions: { - keep_fnames: true, - }, - }), - ], -} - -const resolveConfig = { - extensions: ['*', '.js', '.jsx', '.ts', '.tsx', '.mjs'], - alias: { - react: path.resolve(__dirname, 'node_modules', 'react'), - 'styled-components': path.resolve( - __dirname, - 'node_modules', - 'styled-components' - ), - }, - fallback: { - util: require.resolve('util'), - }, -} - -const babelLoaderConfig = { - test: /\.(js|jsx|ts|tsx|mjs)$/, - exclude: /node_modules\/(?!@botonic)/, - use: { - loader: 'babel-loader', - options: { - sourceType: 'unambiguous', - cacheDirectory: true, - presets: [ - '@babel/react', - [ - '@babel/preset-env', - { - modules: false, - }, - ], - ], - plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', - ], - }, - }, -} - -function fileLoaderConfig(outputPath) { - return { - test: /\.(jpe?g|png|gif|svg)$/i, - use: [ - { - loader: 'file-loader', - options: { - outputPath: outputPath, - }, - }, - ], - } -} - -const nullLoaderConfig = { - test: /\.(scss|css)$/, - use: 'null-loader', -} - -const stylesLoaderConfig = { - test: /\.(scss|css)$/, - use: [ - { - loader: 'style-loader', - options: { - insert: function (element) { - if (!window._botonicInsertStyles) window._botonicInsertStyles = [] - window._botonicInsertStyles.push(element) - }, - }, - }, - 'css-loader', - 'sass-loader', - ], -} - -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], - }, -}) - -function botonicDevConfig(mode) { - return { - mode: mode, - devtool: sourceMap(mode), - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.DEV), - target: 'web', - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(ASSETS_DIRNAME), - stylesLoaderConfig, - ], - }, - output: { - filename: WEBCHAT_FILENAME, - library: BOTONIC_LIBRARY_NAME, - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: OUTPUT_PATH, - }, - resolve: resolveConfig, - devServer: { - static: [OUTPUT_PATH, TASKS_PATH], - liveReload: true, - historyApiFallback: true, - hot: true, - }, - plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(BOTONIC_PATH, 'src', TEMPLATES.WEBCHAT), - filename: 'index.html', - }), - new webpack.HotModuleReplacementPlugin(), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: true, - IS_NODE: false, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - ...(mode === 'development' - ? { MODELS_BASE_URL: JSON.stringify('http://localhost:8080') } - : {}), - }), - new webpack.ProvidePlugin({ - process: 'process/browser', - }), - ], - } -} - -function botonicWebchatConfig(mode) { - return { - optimization: optimizationConfig, - mode: mode, - devtool: sourceMap(mode), - target: 'web', - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.WEBCHAT), - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(ASSETS_DIRNAME), - stylesLoaderConfig, - ], - }, - output: { - filename: WEBCHAT_FILENAME, - library: BOTONIC_LIBRARY_NAME, - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: OUTPUT_PATH, - }, - resolve: resolveConfig, - plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(BOTONIC_PATH, 'src', TEMPLATES.WEBCHAT), - filename: 'index.html', - }), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: true, - IS_NODE: false, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - WEBCHAT_PUSHER_KEY: JSON.stringify(process.env.WEBCHAT_PUSHER_KEY), - }), - ], - } -} - -function botonicWebviewsConfig(mode) { - return { - optimization: optimizationConfig, - mode: mode, - devtool: sourceMap(mode), - target: 'web', - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.WEBVIEWS), - output: { - filename: 'webviews.js', - library: 'BotonicWebview', - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: WEBVIEWS_PATH, - }, - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(path.join('..', ASSETS_DIRNAME)), - stylesLoaderConfig, - ], - }, - resolve: resolveConfig, - plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(BOTONIC_PATH, 'src', TEMPLATES.WEBVIEWS), - filename: 'index.html', - }), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: true, - IS_NODE: false, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - }), - ], - } -} - -function botonicNodeConfig(mode) { - return { - context: ROOT, - optimization: optimizationConfig, - mode: mode, - devtool: sourceMap(mode), - target: 'node', - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.NODE), - resolve: resolveConfig, - output: { - filename: 'bot.js', - library: 'bot', - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: OUTPUT_PATH, - }, - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(ASSETS_DIRNAME), - nullLoaderConfig, - ], - }, - plugins: [ - new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['dist'] }), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: false, - IS_NODE: true, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), - ], - } -} - -module.exports = function (env, argv) { - if (env.target === BOTONIC_TARGETS.ALL) { - return [ - botonicNodeConfig(argv.mode), - botonicWebviewsConfig(argv.mode), - botonicWebchatConfig(argv.mode), - ] - } else if (env.target === BOTONIC_TARGETS.DEV) { - return [botonicDevConfig(argv.mode)] - } else if (env.target === BOTONIC_TARGETS.NODE) { - return [botonicNodeConfig(argv.mode)] - } else if (env.target === BOTONIC_TARGETS.WEBVIEWS) { - return [botonicWebviewsConfig(argv.mode)] - } else if (env.target === BOTONIC_TARGETS.WEBCHAT) { - return [botonicWebchatConfig(argv.mode)] - } else { - return null - } -} diff --git a/nlu/babel.config.js b/nlu/babel.config.js deleted file mode 100644 index 7325b98..0000000 --- a/nlu/babel.config.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This babel configuration is used along with Jest for execute tests, - * do not modify to avoid conflicts with webpack.config.js. - */ - -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - targets: { - node: 'current', - }, - }, - ], - [ - '@babel/react', - { - targets: { - node: 'current', - }, - }, - ], - ], - plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', - ], -} diff --git a/nlu/package.json b/nlu/package.json deleted file mode 100644 index 3ac54e8..0000000 --- a/nlu/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "nlu", - "version": "1.0.0", - "scripts": { - "build": "webpack --env target=all --mode=production", - "start": "webpack serve --env target=dev --mode=development", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } - }, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/plugin-intent-classification": "~0.20.0", - "@botonic/react": "~0.21.0", - "natural": "^2.1.5" - }, - "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", - "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", - "null-loader": "^4.0.1", - "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass": "^1.30.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "util": "^0.12.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" - } -} diff --git a/nlu/src/actions/book-restaurant.js b/nlu/src/actions/book-restaurant.js deleted file mode 100644 index 87b3ada..0000000 --- a/nlu/src/actions/book-restaurant.js +++ /dev/null @@ -1,18 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - return ( - <> - A table has been booked! Enjoy it! πŸ‘¨πŸ½β€πŸ³ - - You can ask me for the weather or to play music while you go to the - restaurant. - - - ) - } -} diff --git a/nlu/src/actions/get-directions.js b/nlu/src/actions/get-directions.js deleted file mode 100644 index dfb7e37..0000000 --- a/nlu/src/actions/get-directions.js +++ /dev/null @@ -1,16 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - return ( - <> - I see that you are quite near from there.πŸ“ - So i suggest you to take the bus N85 to reach it! - And remind to put your mask on! 😷 - - ) - } -} diff --git a/nlu/src/actions/not-found.js b/nlu/src/actions/not-found.js deleted file mode 100644 index d69cd5c..0000000 --- a/nlu/src/actions/not-found.js +++ /dev/null @@ -1,16 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - return ( - <> - I'm sorry! I don't understand you! - I'm just a bot expecting to be a human one day πŸ˜… - Could you please repeat that again? - - ) - } -} diff --git a/nlu/src/actions/show-weather.js b/nlu/src/actions/show-weather.js deleted file mode 100644 index 3094801..0000000 --- a/nlu/src/actions/show-weather.js +++ /dev/null @@ -1,20 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - static async botonicInit({ input }) { - return { input } - } - render() { - return ( - <> - It seems today is a rainny day. 🌧 - - But hey! Don't be sad, rainny days are perfect for staying home - enjoying a good movie! 🎬 - - - ) - } -} diff --git a/nlu/src/actions/start.js b/nlu/src/actions/start.js deleted file mode 100644 index 8d2c2f9..0000000 --- a/nlu/src/actions/start.js +++ /dev/null @@ -1,19 +0,0 @@ -import { RequestContext, Text } from '@botonic/react' -import React from 'react' - -export default class extends React.Component { - static contextType = RequestContext - - render() { - return ( - <> - Hi human! πŸ‘‹ - - I have been trained to recognize intents for booking tables and asking - for weather or directions. - - Ask me something related! 😊 - - ) - } -} diff --git a/nlu/src/assets/.gitkeep b/nlu/src/assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nlu/src/index.js b/nlu/src/index.js deleted file mode 100644 index f733484..0000000 --- a/nlu/src/index.js +++ /dev/null @@ -1 +0,0 @@ -export const config = { defaultDelay: 0, defaultTyping: 0 } diff --git a/nlu/src/locales/.gitkeep b/nlu/src/locales/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nlu/src/locales/index.js b/nlu/src/locales/index.js deleted file mode 100644 index 1fb527a..0000000 --- a/nlu/src/locales/index.js +++ /dev/null @@ -1 +0,0 @@ -export const locales = {} diff --git a/nlu/src/nlp/data/en/BookRestaurant.yaml b/nlu/src/nlp/data/en/BookRestaurant.yaml deleted file mode 100644 index 5524651..0000000 --- a/nlu/src/nlp/data/en/BookRestaurant.yaml +++ /dev/null @@ -1,103 +0,0 @@ -intent: BookRestaurant - -samples: - - book The Middle East restaurant in IN for noon - - Book a table at T-Rex distant from Halsey St. - - I'd like to eat at a taverna that serves chili con carne for a party of 10 - - I have a party of four in Japan and need a reservation at Rimsky-Korsakoffee House on Aug. the 3rd. - - Please make a restaurant reservation for somewhere in Mondovi Connecticut. - - book a spot far from Aland - - I'd like to eat at the best restaurant in Coalton - - book a taverna that serves vichyssoise within walking distance in OH - - I want to book a popular tyrolean steakhouse in Madison Park WA in 1 hour nine minutes and one second - - Find a table for 8 somewhere in Bonaire in 345 days - - Book a restaurant with parking facility for 3. - - please book a room in Spaghetti Warehouse for catalina delores and brandie mendoza at 12 AM - - I need a table for 1 at a highly rated restaurant next autumn in Emmons RI - - book a spot in 1 second that is neighboring robin's hotel - - patty and I need a table booked at a highly rated restaurant in Sandstone. - - Make a reservation for four at a pub in Sugarville. - - book a table for a Macedonia restaurant - - I'd like to go to a halal restaurant in twenty minutes around the District Of Columbia and book seats for four - - Book a table for 10 people at Dunbrody Country House Hotel in Strandburg. - - Book a table in a Haines Borough restaurant for nine that is within walking distance. - - book midday at a faraway cuban place for five at a top-rated bakery in Grainola - - Book a bakery with smoking room for nine in Niger. - - Book a restaurant for ten in OK. - - Book the Gus Stevens Seafood Restaurant & Buccaneer Lounge in Papua New Guinea for one person. - - book a Boon Brick Store for my grandkid and I at Evans Mills - - Book a pub with fisn'n chips in Timberville. - - Restaurant near in town uses in two years - - Book seating at a restaurant in Topton - - Book a table for lunch - - I need a table for four at a pub in Heard Island and McDonald Islands - - I want to go to Antigua and Barbuda and eat at a table for 4. - - I want to eat at a scandinavian restaurant that is highly rated in Bonita Texas - - I want to eat for two at a brasserie in Gwynedd Valley TN with pickled cucumber in one second - - book on feb. the twenty-sixth 2026 in Tracyton for two at a cafe - - I'd like to eat in Iran with a party of 9 on sep. the 12th - - Book The Oriel in Allison for a party of four. - - book a table on 6/14/2035 at a bakery within walking distance of Equatorial Guinea that serves paella - - book me top-rated restaurant for 9 members for midnight at Fair Bluff RI - - book a Ukraine pub serves mineiro - - Book a restaurant in Mauritania for 1 person. - - me and my niece want to eat somewhere close by Hopatcong - - Can you book me a table for 9 in DE? - - I need a reservation for a pizzeria nearby Vigus - - I want to book a restaurant neighboring Livonia Av - - Book me a table for seven people at a bar with a pool. - - Book a spot for kelli jean and I at a pub at elevenses - - book a gastropub serves waffle for 8 - - I need a reservation for 7 people at a bar in Chile that serves bio. - - I need to book a table at a food court in NH that serves smelt - - Book a restaurant in MP at 3 pm with pigs in blankets - - Book an australian restaurant in Jacksboro three weeks from now for my step father and I. - - Please book reservations at a restaurant that serves empanada party for three with betty and dolly. - - I need a table at The Ledbury at 18 o'clock - - At four pm. I need a table for 8 at a restaurant in Guernsey that serves salade. - - book a table 1 year from now for corinne tisha and I at a restaurant in Guernsey that is top-rated - - I'm looking to reserve a table at a pasta restaurant in Serbia. - - Book me a reservation for Ad Hoc in Brazil for a party of 7 - - Lets go eat in Friday 43 days from now - - I need a reservation for a brasserie that serves ankimo in PW - - Please book reservations for 3 people at a restaurant in Alderwood Manor - - book a table in MT for 3 for now at a pub that serves south indian - - Book a table in Belhaven for a party of seven at the Great House at Sonning - - I want to book a restaurant near Wyoming for 5 people. - - Get me a table at Eighth Step Coffee House in Germania for 7 - - Locate the best pub in Apache Junction - - book a restaurant in McMurray on august seventeenth for marjorie vasquez edith and martina - - Book a table for mindy and angelita at a restaurant which serves cafe liegeois - - I need a table for ten on april the eighteenth 2030 - - book a popular restaurant of thai cuisine - - I want to book a pastelaria cafe in Alabama for me and my great grandfather. - - I need a reservation for apr. the sixteenth 2027 in Cazenovia CA - - Book a table at a bar in Moody for deloris ester and petra alvarez. - - Can you find me reservations for this morning at a restaurant that serves stracciatella? - - Book the best joint for 3 people. - - book a spot for me tiffany and sondra at a top-rated restaurant in MS with udupi - - Please book a restaurant room which serves hangtown fry dish in Jagual - - I need a reservation for the best asian joint on Jan. 3 in Bradford Alabama for a party of 9 - - Book a gastropub that has balinese food in twenty hours in Comunas for my nephew and I - - I need to book a Theme restaurant this month in Chile - - Book a table for ten people at a restaurant which serves snack. - - I would like to book a restaurant in Lebanon - - Book the Fashion Cafe for elevenses. - - book a spot for me and my great grandfather at a brasserie with a pool at 7 am - - I need a table at T-Rex for nine in Brunei - - Book a table for two for this autumn - - book a spot at a restaurant with wifi that is neighboring Suriname - - Book a restaurant in Papua New Guinea for me and my daughters - - book a spot at a highly rated tavern in Colombia - - Make a reservation at a tavern which serves clam cake for a party of 8 in Western Sahara. - - Make me a reservation in South Carolina - - Book a table for 2 at a restaurant in Follett - - book a sushis serving restaurant in Uzbekistan - - I need a table for 8 during midday in Montana - - book a table in California for rita and brenda at Hurley Mountain Inn on january the 11th - - me and my grandmother want to eat umbrian food on January the 7th at a brasserie. - - book a restaurant for 4 that's not far from CA - - Book me a restaurant please. - - book for five in Georgia at The Coffee Bean & Tea Leaf - - Book a table for 6 in New Hampshire for next week. - - book a table at SkyCity in Salado for me and hannah diff --git a/nlu/src/nlp/data/en/GetDirections.yaml b/nlu/src/nlp/data/en/GetDirections.yaml deleted file mode 100755 index f3df50f..0000000 --- a/nlu/src/nlp/data/en/GetDirections.yaml +++ /dev/null @@ -1,59 +0,0 @@ -intent: GetDirections - -samples: - - How do I go to Montauk avoiding tolls? - - Directions to Disneyworld avoiding traffic - - Show me the way to go to 33 Greene Street - - Give me directions to my parents' place avoiding toll roads - - Show me directions to my next meeting avoiding traffic - - Quickest directions to go pick up my son at soccer practice - - Show me directions to my yoga retreat avoiding the highway - - Bicycling directions to Millenium park - - Directions to La Guardia airport using Waze - - Cycling directions to Chelsea Market - - Cycling directions to my surf lesson at 10am - - Show me the fastest itinerary to my Airbnb on a Friday night - - Driving directions to Tavern on the Green - - Transit directions to Barcelona Wine Bar - - Navigate to Palo Alto avoiding traffic - - Directions to JFK airport at 7am - - How do I go back home? - - Walking directions to my Christmas party with Citymapper - - Get me directions to John's place if I leave at 6pm - - Walking directions from home to my Halloween party stopping by a wine store - - Directions to Tulsa with road 66 - - Show me the way to go to work - - Give me transit directions from Grand Central to Brooklyn bridge - - Navigate me to Empire State Building using the shortest way - - Show me walking directions to MOMA - - Get me directions to Las Vegas avoiding toll roads - - Show me directions to Jersey City avoiding the highway - - Show me the directions from the LA County Museum to Santa Monica - - Directions to my beach house avoiding tolls - - Show me directions to my hotel - - Show me the fastest itinerary to go to Williamsburg - - Fastest way to go to my next meeting - - Directions to Joe's pub - - I want to go to Boston with the quickest itinerary - - Show me the way to Rand's birthday party by car - - how can i reach the nearest bus station? - - how do i arrive to la sagrada familia? - - how could i go to pedraforca? - - tell me how to get to ronda universitat - - please, show me the way from my location to sarriΓ  - - how can i go to the nearest metro? - - how can i reach a metro station? - - which is the straightforward route to arc de triomf? - - how do i reach my home? - - please, tell me how to get to goiko grill - - tell me the best way to go to goiko grill - - show me the shortest way to go home - - which is the shortest way to go home - - tell me which is the nearest parking - - tell me which is the nearest hairdresser? - - which is the shortest path to arrive at homeΒΏ - - is this the best way to go home? - - how can i reach this restaurant? - - how can i reach this site? - - yes! how can i get there? - - where am i? diff --git a/nlu/src/nlp/data/en/GetWeather.yaml b/nlu/src/nlp/data/en/GetWeather.yaml deleted file mode 100644 index cae81b8..0000000 --- a/nlu/src/nlp/data/en/GetWeather.yaml +++ /dev/null @@ -1,103 +0,0 @@ -intent: GetWeather - -samples: - - What will the weather be this year in Horseshoe Lake State Fish and Wildlife Area? - - Will it be sunny one hundred thirty five days from now in Monterey Bay National Marine Sanctuary - - Is it supposed to rain nearby my current location at 0 o'clock? - - what is the forecast starting on september 1 2039 for chillier conditions in AK - - how cold is it in Princeton Junction - - weather in Nationalpark Nevado Tres Cruces on mar. 4th 2020 - - What will be wind speed in Tiplersville South Sudan? - - whats the weather in GA - - what is the weather at my current location - - Will it snow in Haigler Bosnia and Herzegovina - - What is the weather in Aland 4 seconds from now - - Will it snowstorm neighboring the Rio Grande Wild and Scenic River on feb. the second? - - what is the Sri Lanka forecast for snow - - How is the weather going to be in Pearblossom. - - Can you tell me if it'll be freezing in Wrightstown in seven years ? - - Is the weather going to be colder in GU in 11 years - - Will there be a snowstorm at my current place? - - Is it going to be warmer in Central Cebu Protected Landscape - - What will the weather be in Federated States Of Micronesia at 00:17 am - - Will it be chillier at 06:05:48 in Wagener Reunion - - will it be colder in Cut MA tomorrow - - Is it going to be sunny on Oct. twenty-seventh 2031 within the same area of this current place - - Will it be chilly in Fiji at ten pm - - Will there be a blizzard here on dec. 26 2027? - - On may the thirteenth 2037 what will it be like in Wilderville Montenegro - - Will it get warmer in the same area as Sandy Point National Wildlife Refuge - - What will the weather forecast be in Mount Victory Delaware in 1 second? - - Tell me if it'll be overcast nearby Papua New Guinea - - Is there rain in Nauru at 6 am - - What is the weather like in Hebbronville Venezuela - - What's the weather next week in Somis - - Tell me if it'll be humid in GA one minute from now - - Is it foggy in Shelter Island - - I need to know the weather for San Martin - - Check the forecast for Fernwood MT. - - What is the predicted weather for Wells in Indiana? - - What's the weather looking like right now in Croatia? - - what is the weather forecast for Kinbrae - - Will it be snowy in Crouch French Polynesia? - - Is it warm at seven AM in Greenland - - Tell me the forecast for Elderton Indiana in 19 hours - - what is the weather of Sri Lanka - - What's the weather forecast in the same area as LA in 1 minute? - - what is the chilly forecast for Mustoe North Carolina - - How is the weather in Birta? - - What will the weather be next apr. in MT? - - What is the temperate in Beltsville - - What will the weather be like here next week? - - What will the weather be in Nationalpark Rila on Nov. 25? - - Will there be a blizzard in neighboring Niobrara Valley Preserve - - Is it freezing far from here on 12/5/2032 - - what do the cloud indicate in East Aurora - - What will the weather be this month in Wesley Hills? - - What's the weather in Timbo? - - show weather forcast here at 19 - - Weather for my nearby current location - - Is there a blizzard coming tomorrow in Methuen Uganda - - forecast for Laguna Heights Barbados - - what is the forecast starting on 5/17/2037 for Austria for warmer temps - - What will it be like feb. the twentieth in Colorado - - Is the forecast colder in Idaho 1 second from now - - What's the weather in GU on Jul. the 4th 2024 - - What is the temperate in Uintah right now - - Tell me the weather forecast 1 hour from now in Big Thicket National Preserve - - I need a forecast for Jetmore Massachusetts in 1 hour and 1 second from now - - Will it be chillier in Portugal in one hour? - - What are the weather conditions going to be like in Manhasset Vermont on Dec. the 6th 2036? - - What is the weather in CT - - Check the weather forecast for Sappho - - What will the weather be like on September the eighteenth in Puerto Rico - - Will it be overcast in my current location at twelve PM. - - show weather forecast in West Wildwood South Carolina - - What is the forecast in Heber - - What's the weather this month in Mozambique - - What's the weather close to Cambodia at 05:44:13 - - Weather for East Peru Bolivia 3 years and a half from now - - what is the weather like now in AK - - what is the forecast starting at 8 pm for Alaska - - What will the weather be in Peru? - - What will the weather be in the current place 1 minute from now? - - Is it warmer in Lothian Island Wildlife Sanctuary - - will there be fog Tonight at Beamer Memorial Conservation Area - - Tell me if it will be windy here - - what is the forecast for 15:04 in Georgia for overcast conditions in Valley View - - Will there be cloud action in Saint Bernard Virgin Islands - - What will the weather be like on feb. 8 2034 in Cedar Mountain Wilderness - - Tell me the forecast in the same area here on Robert E Lee's Birthday - - Is there rain now in Maine - - Will it get chillier in North Creek Forest? - - What's the weather forecast for Elida Rhode Island? - - Will it be colder in Ohio - - Is it forecast to be windy here in seven seconds? - - Will it be cold close-by Iowa on september twenty-first 2025 - - What will the weather be a nine in Willow River State Park? - - What will the weather be here? - - in 257 days what will the weather be like in Cannon Ball - - What will the weather be like 56 weeks from now will there be sun in Tabor? - - What's the weather like in North Brunswick Township Virginia - - Is the forecast windy in Nigeria on Nov. the 6th - - What's the wather in Coleville Kenya diff --git a/nlu/src/nlp/tasks/intent-classification/models/en/.gitkeep b/nlu/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/nlu/src/nlp/tasks/intent-classification/train.ts b/nlu/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/nlu/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/nlu/src/plugins.js b/nlu/src/plugins.js deleted file mode 100644 index dd9b5a9..0000000 --- a/nlu/src/plugins.js +++ /dev/null @@ -1,9 +0,0 @@ -export const plugins = [ - { - id: 'intent-classification', - resolve: require('@botonic/plugin-intent-classification'), - options: { - locales: ['en'], - }, - }, -] diff --git a/nlu/src/routes.js b/nlu/src/routes.js deleted file mode 100644 index 28a59e8..0000000 --- a/nlu/src/routes.js +++ /dev/null @@ -1,13 +0,0 @@ -import BookRestaurant from './actions/book-restaurant' -import GetDirections from './actions/get-directions' -import NotFound from './actions/not-found' -import ShowWeather from './actions/show-weather' -import Start from './actions/start' - -export const routes = [ - { text: 'hi', action: Start }, - { input: i => i.intents && i.intents[0].confidence < 0.7, action: NotFound }, - { intent: 'GetDirections', action: GetDirections }, - { intent: 'GetWeather', action: ShowWeather }, - { intent: 'BookRestaurant', action: BookRestaurant }, -] diff --git a/nlu/src/webchat/index.js b/nlu/src/webchat/index.js deleted file mode 100644 index fce3d87..0000000 --- a/nlu/src/webchat/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export const webchat = { - onInit: app => app.open(), - onOpen: app => app.addUserText('hi'), -} diff --git a/nlu/src/webviews/index.js b/nlu/src/webviews/index.js deleted file mode 100644 index 4ca4089..0000000 --- a/nlu/src/webviews/index.js +++ /dev/null @@ -1 +0,0 @@ -export const webviews = [] diff --git a/nlu/tests/__mocks__/fileMock.js b/nlu/tests/__mocks__/fileMock.js deleted file mode 100644 index 0e56c5b..0000000 --- a/nlu/tests/__mocks__/fileMock.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = 'test-file-stub' diff --git a/nlu/tests/__mocks__/styleMock.js b/nlu/tests/__mocks__/styleMock.js deleted file mode 100644 index 4ba52ba..0000000 --- a/nlu/tests/__mocks__/styleMock.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {} diff --git a/nlu/tests/app.test.js b/nlu/tests/app.test.js deleted file mode 100644 index 4205ef9..0000000 --- a/nlu/tests/app.test.js +++ /dev/null @@ -1,20 +0,0 @@ -import { - BotonicInputTester, - BotonicOutputTester, - NodeApp, -} from '@botonic/react' - -import { config } from '../src/' -import { locales } from '../src/locales' -import { routes } from '../src/routes' - -const app = new NodeApp({ routes, locales, ...config }) - -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) - -test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text("I don't understand you") - ) -}) diff --git a/nlu/webpack-entries/dev-entry.js b/nlu/webpack-entries/dev-entry.js deleted file mode 100644 index 6e32a58..0000000 --- a/nlu/webpack-entries/dev-entry.js +++ /dev/null @@ -1,15 +0,0 @@ -import { DevApp } from '@botonic/react' - -import { config } from '../src' -import { locales } from '../src/locales' -import { plugins } from '../src/plugins' -import { routes } from '../src/routes' -import { webchat } from '../src/webchat' - -export const app = new DevApp({ - routes, - locales, - plugins, - ...webchat, - ...config, -}) diff --git a/nlu/webpack-entries/node-entry.js b/nlu/webpack-entries/node-entry.js deleted file mode 100644 index fbb24ed..0000000 --- a/nlu/webpack-entries/node-entry.js +++ /dev/null @@ -1,8 +0,0 @@ -import { NodeApp } from '@botonic/react' - -import { config } from '../src' -import { locales } from '../src/locales' -import { plugins } from '../src/plugins' -import { routes } from '../src/routes' - -export const app = new NodeApp({ routes, locales, plugins, ...config }) diff --git a/nlu/webpack-entries/webchat-entry.js b/nlu/webpack-entries/webchat-entry.js deleted file mode 100644 index 4bd967e..0000000 --- a/nlu/webpack-entries/webchat-entry.js +++ /dev/null @@ -1,5 +0,0 @@ -import { WebchatApp } from '@botonic/react' - -import { webchat } from '../src/webchat' - -export const app = new WebchatApp(webchat) diff --git a/nlu/webpack-entries/webviews-entry.js b/nlu/webpack-entries/webviews-entry.js deleted file mode 100644 index 030c2fc..0000000 --- a/nlu/webpack-entries/webviews-entry.js +++ /dev/null @@ -1,6 +0,0 @@ -import { WebviewApp } from '@botonic/react' - -import { locales } from '../src/locales' -import { webviews } from '../src/webviews' - -export const app = new WebviewApp({ webviews, locales }) diff --git a/nlu/webpack.config.js b/nlu/webpack.config.js deleted file mode 100644 index 4b545fa..0000000 --- a/nlu/webpack.config.js +++ /dev/null @@ -1,366 +0,0 @@ -const path = require('path') -const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') -const TerserPlugin = require('terser-webpack-plugin') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') - -const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' -const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' - -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' -const OUTPUT_PATH = path.resolve(__dirname, 'dist') -const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME -) - -const BOTONIC_PATH = path.resolve( - __dirname, - 'node_modules', - '@botonic', - 'react' -) - -const WEBPACK_MODE = { - DEVELOPMENT: 'development', - PRODUCTION: 'production', -} - -const BOTONIC_TARGETS = { - ALL: 'all', - DEV: 'dev', - NODE: 'node', - WEBVIEWS: 'webviews', - WEBCHAT: 'webchat', -} - -const WEBPACK_ENTRIES_DIRNAME = 'webpack-entries' -const WEBPACK_ENTRIES = { - DEV: 'dev-entry.js', - NODE: 'node-entry.js', - WEBCHAT: 'webchat-entry.js', - WEBVIEWS: 'webviews-entry.js', -} - -const TEMPLATES = { - WEBCHAT: 'webchat.template.html', - WEBVIEWS: 'webview.template.html', -} - -const UMD_LIBRARY_TARGET = 'umd' -const BOTONIC_LIBRARY_NAME = 'Botonic' -const WEBCHAT_FILENAME = 'webchat.botonic.js' - -function sourceMap(mode) { - if (mode === WEBPACK_MODE.PRODUCTION) return 'hidden-source-map' - else if (mode === WEBPACK_MODE.DEVELOPMENT) return 'eval-cheap-source-map' - else - throw new Error( - 'Invalid mode argument (' + mode + '). See package.json scripts' - ) -} - -const optimizationConfig = { - minimize: true, - minimizer: [ - new TerserPlugin({ - parallel: true, - terserOptions: { - keep_fnames: true, - }, - }), - ], -} - -const resolveConfig = { - extensions: ['*', '.js', '.jsx', '.ts', '.tsx', '.mjs'], - alias: { - react: path.resolve(__dirname, 'node_modules', 'react'), - 'styled-components': path.resolve( - __dirname, - 'node_modules', - 'styled-components' - ), - }, - fallback: { - util: require.resolve('util'), - }, -} - -const babelLoaderConfig = { - test: /\.(js|jsx|ts|tsx|mjs)$/, - exclude: /node_modules\/(?!@botonic)/, - use: { - loader: 'babel-loader', - options: { - sourceType: 'unambiguous', - cacheDirectory: true, - presets: [ - '@babel/react', - [ - '@babel/preset-env', - { - modules: false, - }, - ], - ], - plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', - ], - }, - }, -} - -function fileLoaderConfig(outputPath) { - return { - test: /\.(jpe?g|png|gif|svg)$/i, - use: [ - { - loader: 'file-loader', - options: { - outputPath: outputPath, - }, - }, - ], - } -} - -const nullLoaderConfig = { - test: /\.(scss|css)$/, - use: 'null-loader', -} - -const stylesLoaderConfig = { - test: /\.(scss|css)$/, - use: [ - { - loader: 'style-loader', - options: { - insert: function (element) { - if (!window._botonicInsertStyles) window._botonicInsertStyles = [] - window._botonicInsertStyles.push(element) - }, - }, - }, - 'css-loader', - 'sass-loader', - ], -} - -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], - }, -}) - -function botonicDevConfig(mode) { - return { - mode: mode, - devtool: sourceMap(mode), - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.DEV), - target: 'web', - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(ASSETS_DIRNAME), - stylesLoaderConfig, - ], - }, - output: { - filename: WEBCHAT_FILENAME, - library: BOTONIC_LIBRARY_NAME, - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: OUTPUT_PATH, - }, - resolve: resolveConfig, - devServer: { - static: [OUTPUT_PATH, TASKS_PATH], - liveReload: true, - historyApiFallback: true, - hot: true, - }, - plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(BOTONIC_PATH, 'src', TEMPLATES.WEBCHAT), - filename: 'index.html', - }), - new webpack.HotModuleReplacementPlugin(), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: true, - IS_NODE: false, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - ...(mode === 'development' - ? { MODELS_BASE_URL: JSON.stringify('http://localhost:8080') } - : {}), - }), - new webpack.ProvidePlugin({ - process: 'process/browser', - }), - ], - } -} - -function botonicWebchatConfig(mode) { - return { - optimization: optimizationConfig, - mode: mode, - devtool: sourceMap(mode), - target: 'web', - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.WEBCHAT), - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(ASSETS_DIRNAME), - stylesLoaderConfig, - ], - }, - output: { - filename: WEBCHAT_FILENAME, - library: BOTONIC_LIBRARY_NAME, - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: OUTPUT_PATH, - }, - resolve: resolveConfig, - plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(BOTONIC_PATH, 'src', TEMPLATES.WEBCHAT), - filename: 'index.html', - }), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: true, - IS_NODE: false, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - WEBCHAT_PUSHER_KEY: JSON.stringify(process.env.WEBCHAT_PUSHER_KEY), - }), - ], - } -} - -function botonicWebviewsConfig(mode) { - return { - optimization: optimizationConfig, - mode: mode, - devtool: sourceMap(mode), - target: 'web', - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.WEBVIEWS), - output: { - filename: 'webviews.js', - library: 'BotonicWebview', - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: WEBVIEWS_PATH, - }, - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(path.join('..', ASSETS_DIRNAME)), - stylesLoaderConfig, - ], - }, - resolve: resolveConfig, - plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(BOTONIC_PATH, 'src', TEMPLATES.WEBVIEWS), - filename: 'index.html', - }), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: true, - IS_NODE: false, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - }), - ], - } -} - -function botonicNodeConfig(mode) { - return { - context: ROOT, - optimization: optimizationConfig, - mode: mode, - devtool: sourceMap(mode), - target: 'node', - entry: path.resolve(WEBPACK_ENTRIES_DIRNAME, WEBPACK_ENTRIES.NODE), - resolve: resolveConfig, - output: { - filename: 'bot.js', - library: 'bot', - libraryTarget: UMD_LIBRARY_TARGET, - libraryExport: 'app', - path: OUTPUT_PATH, - }, - module: { - rules: [ - babelLoaderConfig, - fileLoaderConfig(ASSETS_DIRNAME), - nullLoaderConfig, - ], - }, - plugins: [ - new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['dist'] }), - imageminPlugin, - new webpack.DefinePlugin({ - IS_BROWSER: false, - IS_NODE: true, - HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), - }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), - ], - } -} - -module.exports = function (env, argv) { - if (env.target === BOTONIC_TARGETS.ALL) { - return [ - botonicNodeConfig(argv.mode), - botonicWebviewsConfig(argv.mode), - botonicWebchatConfig(argv.mode), - ] - } else if (env.target === BOTONIC_TARGETS.DEV) { - return [botonicDevConfig(argv.mode)] - } else if (env.target === BOTONIC_TARGETS.NODE) { - return [botonicNodeConfig(argv.mode)] - } else if (env.target === BOTONIC_TARGETS.WEBVIEWS) { - return [botonicWebviewsConfig(argv.mode)] - } else if (env.target === BOTONIC_TARGETS.WEBCHAT) { - return [botonicWebchatConfig(argv.mode)] - } else { - return null - } -} diff --git a/tutorial/babel.config.js b/tutorial/babel.config.js index 7325b98..ce0c91d 100644 --- a/tutorial/babel.config.js +++ b/tutorial/babel.config.js @@ -23,8 +23,7 @@ module.exports = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-runtime'), ], } diff --git a/tutorial/jest.config.js b/tutorial/jest.config.js new file mode 100644 index 0000000..6b56f40 --- /dev/null +++ b/tutorial/jest.config.js @@ -0,0 +1,17 @@ +const path = require('path') + +module.exports = { + rootDir: 'tests', + transform: { + '^.+\\.jsx?$': [ + 'babel-jest', + { configFile: path.resolve(__dirname, 'babel.config.js') }, + ], + }, + transformIgnorePatterns: ['/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$'], + moduleNameMapper: { + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': + '/__mocks__/fileMock.js', + '\\.(scss|css|less)$': '/__mocks__/styleMock.js', + }, +} diff --git a/tutorial/package.json b/tutorial/package.json index 27088d2..c6394d6 100644 --- a/tutorial/package.json +++ b/tutorial/package.json @@ -3,71 +3,63 @@ "version": "1.0.0", "scripts": { "analyze": "esbuild-visualizer --metadata ./meta.json", - "build": "ts-node ./esbuild-config.ts", - "build:webpack": "rm -rf ./dist; webpack --env target=all --mode=production;", - "start": "webpack serve --env target=dev --mode=development", - "start:esbuild:in-progress": "esbuild ./esbuild-entries/webchat-entry.js --bundle --outdir=dist --serve", - "test": "jest", - "train:ner": "ts-node src/nlp/tasks/ner/train.ts", - "train:intent-classification": "ts-node src/nlp/tasks/intent-classification/train.ts" - }, - "jest": { - "rootDir": "tests", - "transformIgnorePatterns": [ - "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx|mjs)$" - ], - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(scss|css|less)$": "/__mocks__/styleMock.js" - } + "build:esbuild": "rm -rf ./dist; ts-node ./esbuild-config.ts", + "build": "rm -rf ./dist; webpack --env target=all --mode=production", + "start": "rm -rf ./dist; webpack-dev-server --env target=dev --mode=development", + "start:esbuild:in-progress": "rm -rf ./dist; esbuild ./esbuild-entries/webchat-entry.js --bundle --outdir=dist --serve", + "deploy": "botonic deploy -c build", + "test": "jest" }, "dependencies": { - "@babel/runtime": "^7.12.5", - "@botonic/react": "0.22.0-beta.1", - "@types/node": "^18.16.1", - "core-js": "^3.30.2" + "@babel/runtime": "^7.23.9", + "@botonic/react": "0.25.0-alpha.5", + "core-js": "^3.36.0" }, "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.12.10", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-runtime": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/preset-react": "^7.23.3", "@craftamap/esbuild-plugin-html": "^0.5.0", - "@hot-loader/react-dom": "^17.0.1", - "analytics-node": "^3.4.0-beta.3", - "babel-jest": "^26.6.3", - "babel-loader": "^8.2.2", - "chokidar": "^3.4.3", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^7.0.0", - "css-loader": "^5.0.1", - "esbuild": "^0.17.18", + "@hot-loader/react-dom": "^16.14.0", + "analytics-node": "^3.5.0", + "babel-jest": "^29.7.0", + "babel-loader": "^9.1.3", + "chokidar": "^3.6.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.10.0", "esbuild-plugin-imagemin": "^1.0.1", "esbuild-plugin-inline-image": "0.0.9", - "esbuild-sass-plugin": "^2.9.0", - "esbuild-visualizer": "^0.4.0", + "esbuild-sass-plugin": "^2.16.1", + "esbuild-visualizer": "^0.4.1", + "esbuild": "^0.19.4", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.0.0-alpha.17", - "imagemin-gifsicle": "^6.0.0", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^7.0.0", - "imagemin-svgo": "^7.0.0", - "imagemin-webpack": "^5.0.0", - "jest": "^26.6.3", - "node-sass": "^8.0.0", + "html-webpack-plugin": "^5.6.0", + "image-minimizer-webpack-plugin": "^4.0.0", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-optipng": "^8.0.0", + "imagemin-svgo": "^10.0.1", + "imagemin": "^8.0.1", + "jest": "^29.7.0", "null-loader": "^4.0.1", - "prettier": "^2.8.8", + "prettier": "^3.2.5", "process": "^0.11.10", - "react-hot-loader": "^4.13.0", - "sass-loader": "^10.1.0", - "style-loader": "^2.0.0", - "terser": "^5.5.1", - "terser-webpack-plugin": "^5.0.3", - "ts-node": "^10.9.1", - "typescript": "^4.4.4", - "webpack": "^5.10.3", - "webpack-cli": "^4.2.0", - "webpack-dev-server": "4.13.3" + "react-hot-loader": "4.12.21", + "sass-loader": "^14.1.1", + "sass": "^1.71.1", + "style-loader": "^3.3.4", + "svgo": "^3.2.0", + "terser-webpack-plugin": "^5.3.10", + "terser": "^5.27.2", + "ts-node": "^10.9.2", + "typescript": "^4.9.5", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.2", + "webpack": "^5.90.3", + "@babel/core": "^7.23.9" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/tutorial/src/jest.config.js b/tutorial/src/jest.config.js new file mode 100644 index 0000000..d2514ba --- /dev/null +++ b/tutorial/src/jest.config.js @@ -0,0 +1,18 @@ +const path = require('path') + +module.exports = { + rootDir: "tests", + transform: { + "^.+\\.jsx?$": [ + "babel-jest", + { "configFile": path.resolve(__dirname, "babel.config.js") }, + ], + }, + transformIgnorePatterns: [ + "/node_modules/(?!@botonic).+\\.(js|jsx|ts|tsx)$" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "\\.(scss|css|less)$": "/__mocks__/styleMock.js" + } +} \ No newline at end of file diff --git a/tutorial/src/nlp/data/en/.gitkeep b/tutorial/src/nlp/data/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tutorial/src/nlp/tasks/intent-classification/models/en/.gitkeep b/tutorial/src/nlp/tasks/intent-classification/models/en/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tutorial/src/nlp/tasks/intent-classification/train.ts b/tutorial/src/nlp/tasks/intent-classification/train.ts deleted file mode 100644 index ff9693c..0000000 --- a/tutorial/src/nlp/tasks/intent-classification/train.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BotonicIntentClassifier, - DatabaseStorage, - Dataset, - INTENT_CLASSIFIER_TEMPLATE, - Preprocessor, -} from '@botonic/nlp' // eslint-disable-line node/no-missing-import, import/no-unresolved -import { join } from 'path' - -const LOCALE = 'en' - -const DATASET_DIR_PATH = join(process.cwd(), 'src', 'nlp', 'data', LOCALE) -const MODEL_DIR_PATH = join( - process.cwd(), - 'src', - 'nlp', - 'tasks', - 'intent-classification', - 'models' -) - -const MAX_SEQUENCE_LENGTH = 12 -const EMBEDDINGS_DIMENSION = 50 -const EMBEDDINGS_TYPE = 'glove' -const EPOCHS = 8 -const BATCH_SIZE = 8 - -const dataset = Dataset.load(DATASET_DIR_PATH) - -console.log(`Dataset size: ${dataset.length}`) - -const { trainSet, testSet } = dataset.split() -console.log(`Train set size: ${trainSet.length}`) -console.log(`Test set size: ${testSet.length}`) - -const preprocessor = new Preprocessor(LOCALE, MAX_SEQUENCE_LENGTH) - -const vocabulary = trainSet.extractVocabulary(preprocessor) - -const trainModel = async () => { - const classifier = new BotonicIntentClassifier( - { - locale: LOCALE, - maxLength: MAX_SEQUENCE_LENGTH, - intents: dataset.intents, - vocabulary, - }, - preprocessor - ) - - const model = await classifier.createModel( - INTENT_CLASSIFIER_TEMPLATE.SIMPLE_NN, - await DatabaseStorage.with(LOCALE, EMBEDDINGS_TYPE, EMBEDDINGS_DIMENSION), - { units: 128, dropout: 0.6 } - ) - - classifier.setModel(model) - - await classifier.train(trainSet, EPOCHS, BATCH_SIZE) - - const { accuracy, loss } = await classifier.evaluate(testSet) - console.log(`Test Accuracy: ${accuracy}`) - console.log(`Test loss: ${loss}`) - - await classifier.saveModel(MODEL_DIR_PATH) -} - -trainModel() diff --git a/tutorial/tests/app.test.js b/tutorial/tests/app.test.js index 9c2883d..e027bcf 100644 --- a/tutorial/tests/app.test.js +++ b/tutorial/tests/app.test.js @@ -11,11 +11,12 @@ import { routes } from '../src/routes' const app = new NodeApp({ routes, locales, plugins, ...config }) -const i = new BotonicInputTester(app) -const o = new BotonicOutputTester(app) +const input = new BotonicInputTester(app) +const output = new BotonicOutputTester(app) test('TEST: (404) NOT FOUND', async () => { - await expect(i.text('whatever')).resolves.toBe( - o.text('Please, type "start" to start the tutorial.') + const response = await input.text('whatever') + expect(response).toBe( + output.text('Please, type "start" to start the tutorial.'), ) }) diff --git a/tutorial/webpack.config.js b/tutorial/webpack.config.js index f407cd1..73befd0 100644 --- a/tutorial/webpack.config.js +++ b/tutorial/webpack.config.js @@ -1,34 +1,15 @@ const path = require('path') const webpack = require('webpack') -const CopyPlugin = require('copy-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const ImageminPlugin = require('imagemin-webpack') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const ROOT = path.resolve(__dirname, 'src') -const NLP_DIRNAME = 'nlp' const ASSETS_DIRNAME = 'assets' -const MODELS_DIRNAME = 'models' -const TASKS_DIRNAME = 'tasks' -const INTENT_CLASSIFICATION_DIRNAME = 'intent-classification' const OUTPUT_PATH = path.resolve(__dirname, 'dist') const WEBVIEWS_PATH = path.resolve(OUTPUT_PATH, 'webviews') -const TASKS_PATH = path.join(ROOT, NLP_DIRNAME, TASKS_DIRNAME) - -const INTENT_CLASSIFICATION_MODELS_PATH = path.join( - NLP_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME, -) -const INTENTS_ASSETS_MODELS_PATH = path.join( - ASSETS_DIRNAME, - TASKS_DIRNAME, - INTENT_CLASSIFICATION_DIRNAME, - MODELS_DIRNAME, -) const BOTONIC_PATH = path.resolve( __dirname, @@ -125,8 +106,6 @@ const babelLoaderConfig = { ], ], plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime', ], }, @@ -169,16 +148,17 @@ const stylesLoaderConfig = { ], } -const imageminPlugin = new ImageminPlugin({ - bail: false, - cache: false, - imageminOptions: { - plugins: [ - ['imagemin-gifsicle', { interlaced: true }], - ['imagemin-jpegtran', { progressive: true }], - ['imagemin-optipng', { optimizationLevel: 5 }], - ['imagemin-svgo', { removeViewBox: true }], - ], +const imageminPlugin = new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + "imagemin-gifsicle", + "imagemin-jpegtran", + "imagemin-optipng", + "imagemin-svgo", + ], + }, }, }) @@ -204,7 +184,7 @@ function botonicDevConfig(mode) { }, resolve: resolveConfig, devServer: { - static: [OUTPUT_PATH, TASKS_PATH], + static: [OUTPUT_PATH], liveReload: true, historyApiFallback: true, hot: true, @@ -337,14 +317,6 @@ function botonicNodeConfig(mode) { IS_NODE: true, HUBTYPE_API_URL: JSON.stringify(process.env.HUBTYPE_API_URL), }), - new CopyPlugin({ - patterns: [ - { - from: INTENT_CLASSIFICATION_MODELS_PATH, - to: INTENTS_ASSETS_MODELS_PATH, - }, - ], - }), ], } }