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