Skip to content

Commit

Permalink
[Feat] Kepler.gl AI Assistant [1] (#2735)
Browse files Browse the repository at this point in the history
  • Loading branch information
lixun910 authored Nov 19, 2024
1 parent ab9e253 commit 6fd4f88
Show file tree
Hide file tree
Showing 35 changed files with 7,159 additions and 234 deletions.
1 change: 1 addition & 0 deletions examples/demo-app/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<title>kepler.gl demo</title>
<link rel="stylesheet" href="//d1a3f4spazzrp4.cloudfront.net/kepler.gl/uber-fonts/4.0.0/superfine.css">
<link href="https://unpkg.com/maplibre-gl@^3/dist/maplibre-gl.css" rel="stylesheet">
<link rel="stylesheet" href="/bundle.css">
<style type="text/css">
body {margin: 0; padding: 0; overflow: hidden;}
</style>
Expand Down
29 changes: 19 additions & 10 deletions examples/demo-app/esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,29 @@ const RESOLVE_LOCAL_ALIASES = {
// Suppress useless warnings from react-date-picker's dep
'tiny-warning': `${SRC_DIR}/utils/src/noop.ts`,
// kepler.gl and loaders.gl need to use same apache-arrow
'apache-arrow': `${NODE_MODULES_DIR}/apache-arrow`
'apache-arrow': `${NODE_MODULES_DIR}/apache-arrow`,
// all react-ai-assist needs to be resolved from samenode_modules
'react-ai-assist': `${NODE_MODULES_DIR}/react-ai-assist`
};

const config = {
platform: 'browser',
format: 'iife',
logLevel: 'info',
loader: {'.js': 'jsx'},
loader: {'.js': 'jsx', '.css': 'css'},
entryPoints: ['src/main.js'],
outfile: 'dist/bundle.js',
bundle: true,
define: {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'production'),
'process.env.MapboxAccessToken': JSON.stringify(process.env.MapboxAccessToken), // eslint-disable-line
'process.env.DropboxClientId': JSON.stringify(process.env.DropboxClientId), // eslint-disable-line
'process.env.MapboxExportToken': JSON.stringify(process.env.MapboxExportToken), // eslint-disable-line
'process.env.CartoClientId': JSON.stringify(process.env.CartoClientId), // eslint-disable-line
'process.env.FoursquareClientId': JSON.stringify(process.env.FoursquareClientId), // eslint-disable-line
'process.env.FoursquareDomain': JSON.stringify(process.env.FoursquareDomain), // eslint-disable-line
'process.env.FoursquareAPIURL': JSON.stringify(process.env.FoursquareAPIURL), // eslint-disable-line
'process.env.FoursquareUserMapsURL': JSON.stringify(process.env.FoursquareUserMapsURL) // eslint-disable-line
'process.env.MapboxAccessToken': JSON.stringify(process.env.MapboxAccessToken || ''),
'process.env.DropboxClientId': JSON.stringify(process.env.DropboxClientId || ''),
'process.env.MapboxExportToken': JSON.stringify(process.env.MapboxExportToken || ''),
'process.env.CartoClientId': JSON.stringify(process.env.CartoClientId || ''),
'process.env.FoursquareClientId': JSON.stringify(process.env.FoursquareClientId || ''),
'process.env.FoursquareDomain': JSON.stringify(process.env.FoursquareDomain || ''),
'process.env.FoursquareAPIURL': JSON.stringify(process.env.FoursquareAPIURL || ''),
'process.env.FoursquareUserMapsURL': JSON.stringify(process.env.FoursquareUserMapsURL || '')
},
plugins: [
// automatically injected kepler.gl package version into the bundle
Expand All @@ -70,6 +72,13 @@ function addAliases(externals, args) {
// Combine flags
const useLocalDeck = args.includes('--env.deck') || args.includes('--env.hubble_src');
const useRepoDeck = args.includes('--env.deck_src');
const useLocalAiAssistant = args.includes('--env.ai');

// resolve ai-assistant from local dir
if (useLocalAiAssistant) {
resolveAlias['react-ai-assist'] = join(LIB_DIR, '../ai-assistant/src');
resolveAlias['@kepler.gl/ai-assistant'] = join(SRC_DIR, 'ai-assistant/src');
}

// resolve deck.gl from local dir
if (useLocalDeck || useRepoDeck) {
Expand Down
1 change: 1 addition & 0 deletions examples/demo-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"scripts": {
"start": "node esbuild.config.mjs --start",
"start:local": "NODE_ENV=local node esbuild.config.mjs --start",
"start:local-ai": "NODE_ENV=local node esbuild.config.mjs --start --env.ai",
"start:local-deck": "NODE_ENV=local node esbuild.config.mjs --start --env.deck",
"start:local-deck-src": "NODE_ENV=local node esbuild.config.mjs --start --env.deck_src",
"start:local-loaders-src": "NODE_ENV=local node esbuild.config.mjs --start --env.loaders_src",
Expand Down
87 changes: 59 additions & 28 deletions examples/demo-app/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import window from 'global/window';
import {connect} from 'react-redux';
import cloneDeep from 'lodash.clonedeep';

import {ScreenshotWrapper} from 'react-ai-assist';
import {
messages as aiAssistantMessages,
setStartScreenCapture,
setScreenCaptured
} from '@kepler.gl/ai-assistant';
import {theme} from '@kepler.gl/styles';
import Banner from './components/banner';
import Announcement, {FormLink} from './components/announcement';
Expand Down Expand Up @@ -136,6 +142,14 @@ class App extends Component {
// this._loadMockNotifications();
}

_setStartScreenCapture = flag => {
this.props.dispatch(setStartScreenCapture(flag));
};

_setScreenCaptured = screenshot => {
this.props.dispatch(setScreenCaptured(screenshot));
};

_showBanner = () => {
this.setState({showBanner: true});
};
Expand Down Expand Up @@ -425,6 +439,17 @@ class App extends Component {
}
};

combinedMessages = Object.keys(messages).reduce(
(acc, language) => ({
...acc,
[language]: {
...(messages[language] || {}),
...(aiAssistantMessages[language] || {})
}
}),
{}
);

render() {
return (
<ThemeProvider theme={theme}>
Expand All @@ -436,35 +461,41 @@ class App extends Component {
node ? (this.root = node) : null;
}}
>
<Banner
show={this.state.showBanner}
height={BannerHeight}
bgColor="#2E7CF6"
onClose={this._hideBanner}
<ScreenshotWrapper
startScreenCapture={this.props.demo.aiAssistant.screenshotToAsk.startScreenCapture}
setScreenCaptured={this._setScreenCaptured}
setStartScreenCapture={this._setStartScreenCapture}
>
<Announcement onDisable={this._disableBanner} />
</Banner>
<div style={CONTAINER_STYLE}>
<AutoSizer>
{({height, width}) => (
<KeplerGl
mapboxApiAccessToken={CLOUD_PROVIDERS_CONFIGURATION.MAPBOX_TOKEN}
id="map"
/*
* Specify path to keplerGl state, because it is not mount at the root
*/
getState={keplerGlGetState}
width={width}
height={height}
cloudProviders={CLOUD_PROVIDERS}
localeMessages={messages}
onExportToCloudSuccess={onExportFileSuccess}
onLoadCloudMapSuccess={onLoadCloudMapSuccess}
featureFlags={DEFAULT_FEATURE_FLAGS}
/>
)}
</AutoSizer>
</div>
<Banner
show={this.state.showBanner}
height={BannerHeight}
bgColor="#2E7CF6"
onClose={this._hideBanner}
>
<Announcement onDisable={this._disableBanner} />
</Banner>
<div style={CONTAINER_STYLE}>
<AutoSizer>
{({height, width}) => (
<KeplerGl
mapboxApiAccessToken={CLOUD_PROVIDERS_CONFIGURATION.MAPBOX_TOKEN}
id="map"
/*
* Specify path to keplerGl state, because it is not mount at the root
*/
getState={keplerGlGetState}
width={width}
height={height}
cloudProviders={CLOUD_PROVIDERS}
localeMessages={this.combinedMessages}
onExportToCloudSuccess={onExportFileSuccess}
onLoadCloudMapSuccess={onLoadCloudMapSuccess}
featureFlags={DEFAULT_FEATURE_FLAGS}
/>
)}
</AutoSizer>
</div>
</ScreenshotWrapper>
</GlobalStyle>
</ThemeProvider>
);
Expand Down
25 changes: 22 additions & 3 deletions examples/demo-app/src/factories/map-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
EffectControlFactory,
EffectManagerFactory
} from '@kepler.gl/components';
import {AiAssistantControlFactory, AiAssistantManagerFactory} from '@kepler.gl/ai-assistant';
import {SampleMapPanel} from '../components/map-control/map-control';

const StyledMapControlPanel = styled.div`
Expand Down Expand Up @@ -54,22 +55,40 @@ const StyledMapControlOverlay = styled.div`
CustomMapControlFactory.deps = [
EffectControlFactory,
EffectManagerFactory,
AiAssistantControlFactory,
AiAssistantManagerFactory,
...MapControlFactory.deps
];
function CustomMapControlFactory(EffectControl, EffectManager, ...deps) {
function CustomMapControlFactory(
EffectControl,
EffectManager,
AiAssistantControl,
AiAssistantManager,
...deps
) {
const MapControl = MapControlFactory(...deps);
const actionComponents = [...(MapControl.defaultActionComponents ?? []), EffectControl];
const actionComponents = [
...(MapControl.defaultActionComponents ?? []),
EffectControl,
AiAssistantControl
];

const CustomMapControl = props => {
const showEffects = Boolean(props.mapControls?.effect?.active);
const showAiAssistant = Boolean(props.mapControls?.aiAssistant?.active);
return (
<StyledMapControlOverlay top={props.top} rightPanelVisible={showEffects}>
<StyledMapControlOverlay
top={props.top}
rightPanelVisible={showEffects || showAiAssistant}
fullHeight={showAiAssistant}
>
<StyledMapControlPanel>
{!props.isExport && props.currentSample ? <SampleMapPanel {...props} /> : null}
<MapControl {...props} top={0} actionComponents={actionComponents} />
</StyledMapControlPanel>
<StyledMapControlContextPanel>
{showEffects ? <EffectManager /> : null}
{showAiAssistant ? <AiAssistantManager /> : null}
</StyledMapControlContextPanel>
</StyledMapControlOverlay>
);
Expand Down
4 changes: 3 additions & 1 deletion examples/demo-app/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import keplerGlReducer, {combinedUpdaters, uiStateUpdaters} from '@kepler.gl/red
import {processGeojson, processRowObject, processArrowTable} from '@kepler.gl/processors';
import KeplerGlSchema from '@kepler.gl/schemas';
import {EXPORT_MAP_FORMATS} from '@kepler.gl/constants';
import {aiAssistantReducer} from '@kepler.gl/ai-assistant';

import {
INIT,
Expand Down Expand Up @@ -76,7 +77,8 @@ const demoReducer = combineReducers({
loadOptions: {} // Add additional loaders.gl loader options here
}
}),
app: appReducer
app: appReducer,
aiAssistant: aiAssistantReducer
});

// this can be moved into a action and call kepler.gl action
Expand Down
7 changes: 6 additions & 1 deletion examples/demo-app/src/utils/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,10 @@ parseUri.options = {
* @param str
*/
export function validateUrl(str) {
return str?.match(/^(ftp|http|https?):\/\/+(www\.)?[a-z0-9\-.]{3,}\.[a-z]{3}$/);
try {
new URL(str);
return true;
} catch {
return false;
}
}
11 changes: 10 additions & 1 deletion examples/demo-app/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ __metadata:
languageName: node
linkType: hard

"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.9.2":
version: 7.25.0
resolution: "@babel/runtime@npm:7.25.0"
dependencies:
Expand All @@ -127,6 +127,15 @@ __metadata:
languageName: node
linkType: hard

"@babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.7":
version: 7.26.0
resolution: "@babel/runtime@npm:7.26.0"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 10c0/12c01357e0345f89f4f7e8c0e81921f2a3e3e101f06e8eaa18a382b517376520cd2fa8c237726eb094dab25532855df28a7baaf1c26342b52782f6936b07c287
languageName: node
linkType: hard

"@babel/template@npm:^7.25.0":
version: 7.25.0
resolution: "@babel/template@npm:7.25.0"
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"./src/actions",
"./src/effects",
"./src/reducers",
"./src/components"
"./src/components",
"./src/ai-assistant"
],
"repository": {
"type": "git",
Expand Down Expand Up @@ -71,7 +72,7 @@
"start:web": "yarn install-and-start website start",
"start:https": "yarn install-and-start examples/demo-app start-local-https",
"start:e2e": "yarn install-and-start examples/demo-app start-local-e2e",
"build": "NODE_OPTIONS=--openssl-legacy-provider rm -fr dist && babel src/{actions,components,reducers,cloud-providers,localization,tasks} --out-dir dist --source-maps inline --extensions '.ts,.tsx,.js,.jsx' --ignore '**/*.d.ts'",
"build": "NODE_OPTIONS=--openssl-legacy-provider rm -fr dist && babel src/{actions,components,reducers,cloud-providers,localization,tasks,ai-assistant} --out-dir dist --source-maps inline --extensions '.ts,.tsx,.js,.jsx' --ignore '**/*.d.ts'",
"build:umd": "NODE_OPTIONS=--openssl-legacy-provider webpack --config ./webpack/umd.js --progress --env.prod",
"build:types": "tsc --project tsconfig.production.json",
"analyze": "yarn analyze:bundle",
Expand Down Expand Up @@ -145,6 +146,7 @@
"@types/geojson": "^7946.0.8",
"@types/jsdom": "^21.1.1",
"@types/redux-actions": "^2.6.2",
"@types/redux-logger": "^3",
"@types/supercluster": "^7.1.0",
"@typescript-eslint/eslint-plugin": "5.57.0",
"@typescript-eslint/parser": "^5.57.1",
Expand Down Expand Up @@ -184,6 +186,7 @@
"react-dom": "^18.2.0",
"react-hot-loader": "^4.13.0",
"readdirp": "^2.1.0",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.2.1",
"sinon": "^2.4.1",
"sinon-stub-promise": "^4.0.0",
Expand All @@ -197,7 +200,7 @@
"tape": "^4.9.2",
"tape-catch": "^1.0.6",
"typedoc-plugin-markdown": "^3.0.11",
"typescript": "4.5.5",
"typescript": "4.7.2",
"url-loader": "^4.1.1",
"watchify": "^3.6.1",
"webpack": "^4.29.0",
Expand Down
51 changes: 51 additions & 0 deletions src/ai-assistant/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

const KeplerPackage = require('./package');

const PRESETS = ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'];
const PLUGINS = [
['@babel/plugin-transform-typescript', {isTSX: true, allowDeclareFields: true}],
'@babel/plugin-transform-modules-commonjs',
'@babel/plugin-transform-class-properties',
'@babel/plugin-transform-optional-chaining',
'@babel/plugin-transform-logical-assignment-operators',
'@babel/plugin-transform-nullish-coalescing-operator',
'@babel/plugin-transform-export-namespace-from',
[
'@babel/transform-runtime',
{
regenerator: true
}
],
[
'search-and-replace',
{
rules: [
{
search: '__PACKAGE_VERSION__',
replace: KeplerPackage.version
}
]
}
]
];
const ENV = {
test: {
plugins: ['istanbul']
},
debug: {
sourceMaps: 'inline',
retainLines: true
}
};

module.exports = function babel(api) {
api.cache(true);

return {
presets: PRESETS,
plugins: PLUGINS,
env: ENV
};
};
Loading

0 comments on commit 6fd4f88

Please sign in to comment.