@redsift/table 9.2.4-muiv5 → 9.3.0-muiv5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +12 -1
- package/index.js +65 -98
- package/index.js.map +1 -1
- package/package.json +2 -2
package/index.d.ts
CHANGED
|
@@ -82,11 +82,22 @@ type StyledDataGridProps = {
|
|
|
82
82
|
|
|
83
83
|
declare const DataGrid: Comp<DataGridProps, HTMLDivElement>;
|
|
84
84
|
|
|
85
|
+
type CompletionResponse = {
|
|
86
|
+
linkOperator: 'and' | 'or';
|
|
87
|
+
items: [
|
|
88
|
+
{
|
|
89
|
+
columnField: string;
|
|
90
|
+
operatorValue: string;
|
|
91
|
+
value?: string;
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
};
|
|
85
95
|
interface FilterConfig {
|
|
86
96
|
columns: object[];
|
|
87
97
|
typeOperators: object;
|
|
88
98
|
notes: string;
|
|
89
|
-
openaiApiKey
|
|
99
|
+
openaiApiKey?: string;
|
|
100
|
+
completionFunc?: (nlpFilterConfig: FilterConfig, prompt: string, model: string) => Promise<CompletionResponse>;
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
type GridToolbarColumnsProps = Omit<typeof GridToolbarColumnsButton, 'ref'>;
|
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { GRID_DETAIL_PANEL_TOGGLE_COL_DEF, getGridNumericOperators as getGridNum
|
|
|
2
2
|
export { getGridBooleanOperators, getGridDateOperators, getGridSingleSelectOperators } from '@mui/x-data-grid-pro';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import React__default, { Children, isValidElement, cloneElement, useLayoutEffect, useEffect, useRef, forwardRef, useState, useCallback, createElement } from 'react';
|
|
5
|
-
import { Icon, baseContainer, useId as useId$2, partitionComponents, isComponent, Flexbox, TextField as TextField$2, Button, Switch, CtasColorPalette, NotificationsColorPalette, IconButton as IconButton$2, Checkbox,
|
|
5
|
+
import { Icon, baseContainer, useId as useId$2, partitionComponents, isComponent, Flexbox, TextField as TextField$2, Button, Switch, Text, CtasColorPalette, NotificationsColorPalette, IconButton as IconButton$2, Checkbox, LinkButton, Shield } from '@redsift/design-system';
|
|
6
6
|
import { mdiSync, mdiFilterVariant, mdiViewColumn, mdiChevronUp, mdiChevronDown, mdiViewHeadline, mdiViewSequential, mdiViewStream, mdiChevronRight, mdiTrayArrowDown } from '@redsift/icons';
|
|
7
7
|
import emStyled from '@emotion/styled';
|
|
8
8
|
import { Global, ThemeContext, keyframes } from '@emotion/react';
|
|
@@ -21660,12 +21660,7 @@ const StyledGridToolbarFilterSemanticField = styled$3.form`
|
|
|
21660
21660
|
const API_URL = 'https://api.openai.com/v1/chat/completions';
|
|
21661
21661
|
async function getCompletion(text, role, openai_api_key) {
|
|
21662
21662
|
let model = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'gpt-3.5-turbo-0613';
|
|
21663
|
-
let verbose = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
|
|
21664
|
-
/**
|
|
21665
|
-
* Returns the text responded by GPT.
|
|
21666
|
-
*/
|
|
21667
21663
|
try {
|
|
21668
|
-
const t0 = new Date();
|
|
21669
21664
|
const messages = [{
|
|
21670
21665
|
role: 'system',
|
|
21671
21666
|
content: role
|
|
@@ -21673,15 +21668,8 @@ async function getCompletion(text, role, openai_api_key) {
|
|
|
21673
21668
|
role: 'user',
|
|
21674
21669
|
content: text
|
|
21675
21670
|
}];
|
|
21676
|
-
|
|
21677
|
-
|
|
21678
|
-
console.log('prompt:', text);
|
|
21679
|
-
console.log('model:', model);
|
|
21680
|
-
console.log('characters count (4 chars ~ 1 token)');
|
|
21681
|
-
console.log(`- system=${role.length}`);
|
|
21682
|
-
console.log(`- user=${text.length}`);
|
|
21683
|
-
}
|
|
21684
|
-
const response = await fetch(API_URL, {
|
|
21671
|
+
const url = API_URL;
|
|
21672
|
+
const response = await fetch(url, {
|
|
21685
21673
|
method: 'POST',
|
|
21686
21674
|
headers: {
|
|
21687
21675
|
'Content-Type': 'application/json',
|
|
@@ -21694,15 +21682,6 @@ async function getCompletion(text, role, openai_api_key) {
|
|
|
21694
21682
|
})
|
|
21695
21683
|
});
|
|
21696
21684
|
const data = await response.json();
|
|
21697
|
-
if (verbose) {
|
|
21698
|
-
const t1 = new Date();
|
|
21699
|
-
const t = (role.length + text.length + data.choices[0].message.content.length) / 4;
|
|
21700
|
-
console.log(`- response=${data.choices[0].message.content.length}`);
|
|
21701
|
-
console.log(`total is about ${t.toFixed(0)} tokens and took ${// @ts-ignore
|
|
21702
|
-
((t1 - t0) / 1000).toFixed(0)} seconds to complete`);
|
|
21703
|
-
console.log('response:');
|
|
21704
|
-
console.log(data.choices[0].message.content);
|
|
21705
|
-
}
|
|
21706
21685
|
return data.choices[0].message.content;
|
|
21707
21686
|
} catch (error) {
|
|
21708
21687
|
return '';
|
|
@@ -21713,77 +21692,66 @@ const _excluded$e = ["className", "nlpFilterConfig", "onFilterModelChange"];
|
|
|
21713
21692
|
const COMPONENT_NAME$2 = 'GridToolbarFilterSemanticField';
|
|
21714
21693
|
const CLASSNAME$2 = 'redsift-datagrid-toolbar-nlp-filter-field';
|
|
21715
21694
|
const DATE_FORMAT = 'yyyy-mm-dd';
|
|
21695
|
+
const DEFAULT_GPT_MODEL = 'gpt-4-0613';
|
|
21716
21696
|
const DEFAULT_FILTER = {
|
|
21717
21697
|
items: []
|
|
21718
21698
|
};
|
|
21719
|
-
const
|
|
21699
|
+
const getRole = config => {
|
|
21700
|
+
const today = new Date().toDateString();
|
|
21701
|
+
const columns = `[${config.columns.map(_ref => {
|
|
21702
|
+
let {
|
|
21703
|
+
field
|
|
21704
|
+
} = _ref;
|
|
21705
|
+
return `"${field}"`;
|
|
21706
|
+
}).join(', ')}]`;
|
|
21707
|
+
const operators = Object.entries(config.typeOperators).map(_ref2 => {
|
|
21708
|
+
let [k, values] = _ref2;
|
|
21709
|
+
return values.length === 1 ? ` - For "${k}" data type, operator must only be "${values[0]}"` : ` - For "${k}" data type, operator must be one of [${values.map(v => `"${v}"`).join(', ')}]`;
|
|
21710
|
+
}).join('\n');
|
|
21711
|
+
const column_description = config.columns.map(_ref3 => {
|
|
21712
|
+
let {
|
|
21713
|
+
field,
|
|
21714
|
+
type,
|
|
21715
|
+
description
|
|
21716
|
+
} = _ref3;
|
|
21717
|
+
return `- "${field}": "${type}" data type; ${description ? description.trim() : ''}`;
|
|
21718
|
+
}).join('\n');
|
|
21719
|
+
return `The AI assistant parses user input to generate a JSON object that will be used as a row filter for a data table MUI Data Grid.
|
|
21720
21720
|
The filter supports mulitple conditions using only two logical operator "and", "or". It only allows "and" between all conditions or "or" between all conditions. It can't mix the two types.
|
|
21721
21721
|
The AI assistant extracts information from the user input and generates a JSON object with exactly the two keys "linkOperator" and "items":
|
|
21722
21722
|
- "linkOperator": the logical operator, only "and" or "or" are allowed. If there is only one condition in the "items", use "and".
|
|
21723
21723
|
- "items": a list of conditions, each is an object with exactly the three keys "columnField", "operatorValue" and "value":
|
|
21724
|
-
- "columnField": the column name, must be one of {
|
|
21725
|
-
- "value":
|
|
21724
|
+
- "columnField": the column name, must be one of ${columns}
|
|
21725
|
+
- "value":
|
|
21726
21726
|
- this can be skipped if the "operatorValue" is either "isEmpty" or "isNotEmpty"
|
|
21727
21727
|
- a list of multiple values if the "operatorValue" ends with "AnyOf"
|
|
21728
|
-
- otherwise, it's a single value represented as a string: "true" instead of true, "false" instead of false, "0.6" instead of 0.6.
|
|
21729
|
-
For "date" data type, use ${DATE_FORMAT}. If relative date is input, convert to the actual date given today is {
|
|
21728
|
+
- otherwise, it's a single value represented as a string: "true" instead of true, "false" instead of false, "0.6" instead of 0.6.
|
|
21729
|
+
For "date" data type, use ${DATE_FORMAT}. If relative date is input, convert to the actual date given today is ${today}.
|
|
21730
21730
|
- "operatorValue": the comparison operator, accepted values depend on the data type of the column
|
|
21731
|
-
{
|
|
21731
|
+
${operators}
|
|
21732
21732
|
|
|
21733
21733
|
Below is the datatype in square bracket, constraints on the data range if any, followed by the description of each column used in the data table:
|
|
21734
|
-
{
|
|
21734
|
+
${column_description}
|
|
21735
21735
|
|
|
21736
21736
|
Notes:
|
|
21737
21737
|
- For "boolean" data type, use "is" operator with value "false" instead of "isEmpty".
|
|
21738
|
-
{
|
|
21738
|
+
${config.notes.trim()}
|
|
21739
21739
|
|
|
21740
21740
|
Pay close attention to the the data type, description and supported operators above to make a valid selection of fields.
|
|
21741
21741
|
Think step by step and check carefully if the chosen operator is supported by the chosen data type.
|
|
21742
21742
|
Return just the JSON object without any extra text, explanation or note.
|
|
21743
|
-
If the user input can't be parsed, return a JSON object to indicate the error and the reason {"code":"error", "reason":"explain why it was failed to parse"}
|
|
21744
|
-
|
|
21745
|
-
|
|
21746
|
-
|
|
21747
|
-
|
|
21748
|
-
|
|
21749
|
-
|
|
21750
|
-
const
|
|
21751
|
-
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
|
|
21755
|
-
}).join(', ') + ']';
|
|
21756
|
-
role = role.replace('{{columns}}', columnText);
|
|
21757
|
-
|
|
21758
|
-
// Type operators
|
|
21759
|
-
const opreatorText = Object.entries(config.typeOperators).map(_ref2 => {
|
|
21760
|
-
let [k, values] = _ref2;
|
|
21761
|
-
if (values.length == 1) {
|
|
21762
|
-
return ` - For "${k}" data type, operator must only be "${values[0]}"`;
|
|
21763
|
-
} else {
|
|
21764
|
-
const types = '[' + values.map(v => `"${v}"`).join(', ') + ']';
|
|
21765
|
-
return ` - For "${k}" data type, operator must be one of ${types}`;
|
|
21766
|
-
}
|
|
21767
|
-
}).join('\n');
|
|
21768
|
-
role = role.replace('{{type operators}}', opreatorText);
|
|
21769
|
-
|
|
21770
|
-
// Column description
|
|
21771
|
-
// const descriptionText = config.columns.map(({field, type, description}) => `- "${field}": "${type}" data type; operators must be one of [${config.typeOperators[type].map(v => '"' + v + '"').join(', ')}]; ${description.trim()}`).join('\n');
|
|
21772
|
-
const descriptionText = config.columns.map(_ref3 => {
|
|
21773
|
-
let {
|
|
21774
|
-
field,
|
|
21775
|
-
type,
|
|
21776
|
-
description
|
|
21777
|
-
} = _ref3;
|
|
21778
|
-
return `- "${field}": "${type}" data type; ${description ? description.trim() : ''}`;
|
|
21779
|
-
}).join('\n');
|
|
21780
|
-
role = role.replace('{{column description}}', descriptionText);
|
|
21781
|
-
|
|
21782
|
-
// Notes
|
|
21783
|
-
role = role.replace('{{notes}}', config.notes.trim());
|
|
21784
|
-
|
|
21785
|
-
// console.log(role);
|
|
21786
|
-
return role;
|
|
21743
|
+
If the user input can't be parsed, return a JSON object to indicate the error and the reason {"code":"error", "reason":"explain why it was failed to parse"}.
|
|
21744
|
+
`;
|
|
21745
|
+
};
|
|
21746
|
+
async function getOpenAICompletion(config, prompt, model) {
|
|
21747
|
+
const text = 'Parse the text delimited by triple backticks: ```' + prompt.trim() + '``` and make sure the output is a valid JSON object';
|
|
21748
|
+
const role = getRole(config);
|
|
21749
|
+
const completion = await getCompletion(text, role, config.openaiApiKey, model);
|
|
21750
|
+
const response = JSON.parse(completion);
|
|
21751
|
+
if ('code' in response) {
|
|
21752
|
+
throw new Error(response.reason);
|
|
21753
|
+
}
|
|
21754
|
+
return response;
|
|
21787
21755
|
}
|
|
21788
21756
|
|
|
21789
21757
|
/**
|
|
@@ -21798,7 +21766,8 @@ const GridToolbarFilterSemanticField = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
21798
21766
|
} = props,
|
|
21799
21767
|
forwardedProps = _objectWithoutProperties(props, _excluded$e);
|
|
21800
21768
|
const [prompt, setPrompt] = useState('');
|
|
21801
|
-
const
|
|
21769
|
+
const modelRef = useRef(DEFAULT_GPT_MODEL);
|
|
21770
|
+
const showErrorRef = useRef(false);
|
|
21802
21771
|
const [isLoading, setIsLoading] = useState(false);
|
|
21803
21772
|
const handlePromptSubmit = async event => {
|
|
21804
21773
|
event.preventDefault();
|
|
@@ -21810,36 +21779,33 @@ const GridToolbarFilterSemanticField = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
21810
21779
|
const response = sessionStorage.getItem(prompt);
|
|
21811
21780
|
if (response && response !== JSON.stringify(DEFAULT_FILTER)) {
|
|
21812
21781
|
filter = JSON.parse(response);
|
|
21813
|
-
console.log('filter from cache:', filter);
|
|
21814
21782
|
} else {
|
|
21815
21783
|
setIsLoading(true);
|
|
21816
|
-
|
|
21817
|
-
const text = 'Parse the text delimited by triple backticks: ```' + prompt.trim() + '``` and make sure the output is a valid JSON object';
|
|
21818
|
-
const output_str = await getCompletion(text, role, nlpFilterConfig.openaiApiKey, model);
|
|
21784
|
+
showErrorRef.current = false;
|
|
21819
21785
|
try {
|
|
21820
|
-
|
|
21821
|
-
|
|
21822
|
-
console.log('GPT does not understand input', filter.reason);
|
|
21823
|
-
filter = DEFAULT_FILTER;
|
|
21786
|
+
if (nlpFilterConfig.completionFunc !== undefined) {
|
|
21787
|
+
filter = await nlpFilterConfig.completionFunc(nlpFilterConfig, prompt, modelRef.current);
|
|
21824
21788
|
} else {
|
|
21825
|
-
|
|
21789
|
+
filter = await getOpenAICompletion(nlpFilterConfig, prompt, modelRef.current);
|
|
21826
21790
|
}
|
|
21791
|
+
sessionStorage.setItem(prompt, JSON.stringify(filter));
|
|
21827
21792
|
} catch (error) {
|
|
21828
|
-
|
|
21793
|
+
showErrorRef.current = true;
|
|
21829
21794
|
filter = DEFAULT_FILTER;
|
|
21830
21795
|
}
|
|
21831
21796
|
|
|
21832
21797
|
// MUI requires different id
|
|
21833
|
-
filter.items.forEach((d, i) =>
|
|
21834
|
-
d.id = i;
|
|
21835
|
-
});
|
|
21836
|
-
console.log('filter:', filter);
|
|
21798
|
+
filter.items.forEach((d, i) => d.id = i);
|
|
21837
21799
|
}
|
|
21838
21800
|
onFilterModelChange(filter);
|
|
21839
21801
|
setIsLoading(false);
|
|
21840
21802
|
}
|
|
21841
21803
|
};
|
|
21842
|
-
return /*#__PURE__*/React__default.createElement(
|
|
21804
|
+
return /*#__PURE__*/React__default.createElement(Flexbox, {
|
|
21805
|
+
flexDirection: "column",
|
|
21806
|
+
gap: "0",
|
|
21807
|
+
width: "100%"
|
|
21808
|
+
}, /*#__PURE__*/React__default.createElement(StyledGridToolbarFilterSemanticField, _extends$2({}, forwardedProps, {
|
|
21843
21809
|
className: classNames(GridToolbarFilterSemanticField.className, className),
|
|
21844
21810
|
ref: fieldRef,
|
|
21845
21811
|
onSubmit: handlePromptSubmit
|
|
@@ -21857,13 +21823,14 @@ const GridToolbarFilterSemanticField = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
21857
21823
|
"aria-label": "Submit",
|
|
21858
21824
|
type: "submit",
|
|
21859
21825
|
isLoading: isLoading
|
|
21860
|
-
}, "Run")), /*#__PURE__*/React__default.createElement(Tooltip, null, /*#__PURE__*/React__default.createElement(Tooltip.Trigger, null, /*#__PURE__*/React__default.createElement(Switch
|
|
21861
|
-
// style={{"display":"none"}} // Hide it for the demo
|
|
21862
|
-
, {
|
|
21826
|
+
}, "Run")), /*#__PURE__*/React__default.createElement(Tooltip, null, /*#__PURE__*/React__default.createElement(Tooltip.Trigger, null, /*#__PURE__*/React__default.createElement(Switch, {
|
|
21863
21827
|
width: "175px",
|
|
21864
|
-
isSelected:
|
|
21865
|
-
onChange: value =>
|
|
21866
|
-
}, "Power mode")), /*#__PURE__*/React__default.createElement(Tooltip.Content, null, "The Power mode can get better results but is slower.")))
|
|
21828
|
+
isSelected: modelRef.current === 'gpt-4-0613',
|
|
21829
|
+
onChange: value => modelRef.current = value ? 'gpt-4-0613' : 'gpt-3.5-turbo-0613'
|
|
21830
|
+
}, "Power mode")), /*#__PURE__*/React__default.createElement(Tooltip.Content, null, "The Power mode can get better results but is slower."))), showErrorRef.current && /*#__PURE__*/React__default.createElement(Text, {
|
|
21831
|
+
color: "error",
|
|
21832
|
+
marginLeft: "8px"
|
|
21833
|
+
}, "Unable to find a valid filter, please try again with a more specific prompt."));
|
|
21867
21834
|
});
|
|
21868
21835
|
GridToolbarFilterSemanticField.className = CLASSNAME$2;
|
|
21869
21836
|
GridToolbarFilterSemanticField.displayName = COMPONENT_NAME$2;
|