Skip to content

Commit 235f09a

Browse files
committed
feat: add components
1 parent ea1110a commit 235f09a

File tree

15 files changed

+882
-2
lines changed

15 files changed

+882
-2
lines changed

packages/studio-explore/src/app.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import {
4343
FloatTabs,
4444
Placeholder,
4545
CypherQuery,
46+
Copilot,
47+
RunAI,
4648
} from './components';
4749
import {
4850
BgColorsOutlined,
@@ -55,10 +57,12 @@ import {
5557
TabletOutlined,
5658
BranchesOutlined,
5759
CopyrightOutlined,
60+
OpenAIOutlined,
5861
} from '@ant-design/icons';
5962
import { Divider, Flex, theme, Segmented, Tabs, Typography } from 'antd';
6063
import { getDefaultServices } from './services';
6164
import TableView from './components/TableView';
65+
import ExploreLocales from './locales';
6266

6367
interface ExploreProps {
6468
id?: string;
@@ -76,7 +80,18 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
7680
return (
7781
<div ref={containerRef} style={{ position: 'absolute', top: '0px', left: '0px', bottom: '0px', right: '0px' }}>
7882
<GraphProvider id={id} services={services}>
79-
<StudioProvier locales={locales}>
83+
<StudioProvier
84+
locales={{
85+
'zh-CN': {
86+
...locales['zh-CN'],
87+
...ExploreLocales['zh-CN'],
88+
},
89+
'en-US': {
90+
...locales['en-US'],
91+
...ExploreLocales['en-US'],
92+
},
93+
}}
94+
>
8095
<Section
8196
splitBorder
8297
rightSide={
@@ -152,6 +167,12 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
152167
children: <CypherQuery />,
153168
key: 'CypherQuery',
154169
},
170+
{
171+
label: <Typography.Title level={3}>Copilot</Typography.Title>,
172+
icon: <OpenAIOutlined />,
173+
children: <Copilot />,
174+
key: 'Copilot',
175+
},
155176
{
156177
label: <Typography.Title level={3}>Style Setting</Typography.Title>,
157178
icon: <BgColorsOutlined />,
@@ -194,6 +215,7 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
194215
</HoverMenu> */}
195216

196217
<ContextMenu>
218+
<RunAI />
197219
<NeighborQuery />
198220
{/* <CommonNeighbor /> */}
199221
<DeleteLeafNodes />
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as React from 'react';
2+
import { Typography, Button } from 'antd';
3+
import { useContext } from '@graphscope/studio-graph';
4+
import { ShareAltOutlined, DeleteOutlined, OpenAIOutlined } from '@ant-design/icons';
5+
6+
interface INeighborQueryProps {}
7+
8+
const RunAI: React.FunctionComponent<INeighborQueryProps> = props => {
9+
const { store, updateStore } = useContext();
10+
const { selectNodes } = store;
11+
12+
const handleClick = () => {
13+
const scripts = `write a related work section about the given data, you should focus on challenges only : ${JSON.stringify(selectNodes, null, 2)}`;
14+
//@ts-ignore
15+
window.runAI(scripts);
16+
};
17+
return (
18+
<Button
19+
onClick={handleClick}
20+
icon={<OpenAIOutlined />}
21+
type="text"
22+
style={{ width: '100%', justifyContent: 'left' }}
23+
>
24+
Run Copilot
25+
</Button>
26+
);
27+
};
28+
29+
export default RunAI;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
export const AssistantIcon = ({ style }: { style: React.CSSProperties }) => {
3+
return (
4+
<svg
5+
width="41px"
6+
height="62px"
7+
viewBox="0 0 41 62"
8+
version="1.1"
9+
xmlns="http://www.w3.org/2000/svg"
10+
xmlnsXlink="http://www.w3.org/1999/xlink"
11+
style={style}
12+
>
13+
<title>logo image</title>
14+
<g id="页面-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
15+
<g id="Medium" transform="translate(-332, -376)" fillRule="nonzero">
16+
<g id="编组" transform="translate(332, 376)">
17+
<path
18+
d="M20.9765256,25 L20.9743827,19.8935471 L22.5478011,18.78271 L26,16.3451158 L25.9983928,13 L18.4270199,18.1683792 C16.9071738,19.2057816 15.8019773,20.29689 15,21.3704617 L15,21.4778737 L20.9765256,25 Z"
19+
id="路径"
20+
fill="#2281F2"
21+
></path>
22+
<path
23+
d="M6.93795336,20.7840634 C8.51291573,17.334308 13.2733365,13.6674352 13.2733365,13.6674352 L29,3.26992071 L22.9018725,0 L10.9685669,7.75591857 C10.9685669,7.75591857 -0.854857679,14.9756228 0.049338003,26.8260698 C0.761651772,36.1593797 12.3046335,39 12.3046335,39 C5.52316592,36.2295591 4.72994467,25.6215065 6.93795336,20.7840634 Z"
24+
id="路径"
25+
fill="#2281F2"
26+
></path>
27+
<path
28+
d="M29.2439473,3 L13.4603304,13.6082752 C13.4603304,13.6082752 8.68266998,17.3489173 7.10200396,20.8691596 C4.7340225,26.1425307 5.81651263,38.201864 13.9672848,39.9124792 C13.9672848,39.9124792 17.3656344,40.7454111 23.3969644,37.2721576 C26.5517126,35.4552583 30.7231127,39.070597 30.7231127,39.070597 C35.4705973,35.3685528 31.9076521,31.827613 28.0846459,30.5007395 C24.3395483,29.2007168 21.5847673,31.781743 17.5346192,32.8378717 C13.4844711,33.8940003 12.4480677,29.1934447 12.4480677,29.1934447 C12.4480677,29.1934447 10.5497323,23.0871416 17.786999,18.1633893 L25.5410959,12.8877806 L25.5427418,16.3022982 L20.3958387,19.9243496 L20.3980333,25.1367472 L29.4222592,19.0125436 L29.2439473,3 Z"
29+
id="路径"
30+
fill="#1FB2FD"
31+
></path>
32+
<path
33+
d="M36.7904047,27.0251597 C32.1878283,23.2677091 25.8158521,23.5156909 20.7707852,25.0562844 C18.0303799,25.8934309 12.0176813,28.5163788 12.0176813,28.5163788 C12.0176813,28.5163788 12.0918982,28.8392544 12.2746294,29.2936102 C12.3094889,29.3801542 12.3471596,29.4705815 12.3904528,29.565447 C12.4309348,29.6547648 12.476477,29.747966 12.5248305,29.8422768 C12.5928627,29.9759761 12.6687665,30.1130041 12.7536662,30.2516963 C12.8082044,30.3415689 12.8649916,30.4314415 12.9268391,30.5207593 C13.016799,30.6516848 13.1146305,30.7815007 13.2208957,30.9079881 C13.3007352,31.0034083 13.3873216,31.0960548 13.4767193,31.1864821 C13.8635471,31.5770395 14.3408972,31.9132295 14.9284481,32.1068439 C15.0189704,32.1368014 15.1117416,32.1628756 15.2078863,32.1856211 C15.7825055,32.3220943 16.4521449,32.3265324 17.2302986,32.1301442 C17.2404191,32.1279251 17.2494151,32.1245965 17.2595356,32.1218227 C19.9341576,31.4411211 21.9492606,30.1523927 23.9930384,29.7130156 C25.2822771,29.3890305 26.5895077,29.3241225 28.042361,29.8123192 C31.9601172,31.1282314 35.611367,34.640474 30.74622,38.3113806 L30.74622,38.3113806 L30.74622,38.3113806 L17.8768858,46.5019905 L17.877448,46.5014357 L14.3510177,44.4709811 L22.2523132,39.6783315 L17.0768044,37.0060091 L4,44.7372702 L4.00056225,44.737825 L13.0353533,49.5842871 L13.0342288,49.5848419 L19.4039561,53 L37.3717709,41.2716281 C37.3706464,41.2732924 45.9106577,34.4712695 36.7904047,27.0251597 Z"
34+
id="路径"
35+
fill="#37EDD7"
36+
></path>
37+
<path
38+
d="M40.9961107,35 C40.9955641,35 40.994471,35.0005545 40.9939244,35.0011089 C40.9928312,35.0238418 40.9884586,35.0454657 40.9873655,35.0676442 C40.8900752,39.0492238 37.4668795,41.8592287 37.4668795,41.8592287 L20,53.5810744 L20.0032794,62 L38.0440621,49.2618285 C41.053501,47.4803475 41,43.5348078 41,43.5348078 L40.9961107,35 Z"
39+
id="路径"
40+
fill="#1FB2FD"
41+
></path>
42+
<polygon
43+
id="路径"
44+
fill="#2281F2"
45+
points="22.9982925 41 22.9982925 41 22.9982925 41 15 45.9410083 18.498008 47.9919928 18.6237906 48 23 45.1631669"
46+
></polygon>
47+
<polygon id="路径" fill="#2281F2" points="18.9961684 53.4177665 4 45 4.00328419 53.0147944 19 62"></polygon>
48+
</g>
49+
</g>
50+
</g>
51+
</svg>
52+
);
53+
};
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import React, { useEffect, useRef, useState } from 'react';
2+
import { Message } from './utils/message';
3+
4+
import { getWelcomeMessage, prompt as defaultPrompt, defaultWelcome } from './utils/prompt';
5+
import { Input, Button, Flex, Typography, Space, Skeleton, theme } from 'antd';
6+
import { useController } from './useController';
7+
import { useContext } from '@graphscope/studio-graph';
8+
import { extractTextWithPlaceholders } from './utils/extractTextWithPlaceholders';
9+
import { PlayCircleOutlined, ClearOutlined, SearchOutlined, BulbOutlined, SettingOutlined } from '@ant-design/icons';
10+
import MessageItem from './message';
11+
import Setting from './setting';
12+
import { useIntl } from 'react-intl';
13+
import { Utils } from '@graphscope/studio-components';
14+
import { query } from './query';
15+
interface IGPTStatementsProps {
16+
prompt?: string;
17+
welcome?: string;
18+
}
19+
20+
const { useToken } = theme;
21+
const GPTStatements: React.FunctionComponent<IGPTStatementsProps> = props => {
22+
const { prompt = defaultPrompt } = props;
23+
const [state, updateState] = useState<{ messages: Message[]; isLoading: boolean; OPENAI_KEY_FOR_GS: string | null }>({
24+
messages: [],
25+
isLoading: false,
26+
OPENAI_KEY_FOR_GS: localStorage.getItem('OPENAI_KEY_FOR_GS'),
27+
});
28+
const { messages, isLoading, OPENAI_KEY_FOR_GS } = state;
29+
const InputRef = useRef(null);
30+
const { token } = useToken();
31+
const { store } = useContext();
32+
const { schema: schemaData } = store;
33+
34+
const controller = useController();
35+
const { updateStore } = useContext();
36+
const intl = useIntl();
37+
const recommended_messages = [
38+
// intl.formatMessage({
39+
// id: 'recommend 5 interesting query statements',
40+
// }),
41+
// intl.formatMessage({
42+
// id: 'query any subgraph',
43+
// }),
44+
// intl.formatMessage({
45+
// id: 'insight the statistical distribution of vertex labels in the graph',
46+
// }),
47+
];
48+
const welcome = intl.formatMessage({
49+
id: 'query.copilot.welcome',
50+
});
51+
52+
useEffect(() => {
53+
if (schemaData) {
54+
updateState(pre => {
55+
return {
56+
...pre,
57+
messages: [...getWelcomeMessage(welcome, prompt, schemaData)],
58+
};
59+
});
60+
}
61+
}, []);
62+
63+
const handleSubmit = async (script?: string) => {
64+
if (InputRef.current) {
65+
//@ts-ignore
66+
const { value } = InputRef.current.input;
67+
const message = new Message({
68+
role: 'user',
69+
content: script || value,
70+
});
71+
72+
updateState(pre => {
73+
return {
74+
...pre,
75+
messages: [...pre.messages, message],
76+
isLoading: true,
77+
};
78+
});
79+
80+
const response = await query([...messages, message], OPENAI_KEY_FOR_GS!, controller.signal);
81+
if (!response) {
82+
updateState(preState => {
83+
return {
84+
...preState,
85+
isLoading: false,
86+
};
87+
});
88+
return false;
89+
}
90+
// addMessage(
91+
// new Message({
92+
// role: response.message.role,
93+
// content: response.message.content,
94+
// status: 'success',
95+
// }),
96+
// );
97+
updateState(pre => {
98+
return {
99+
...pre,
100+
messages: [
101+
...pre.messages,
102+
new Message({
103+
role: response.message.role,
104+
content: response.message.content,
105+
status: 'success',
106+
}),
107+
],
108+
isLoading: false,
109+
};
110+
});
111+
}
112+
};
113+
//@ts-ignore
114+
window.runAI = handleSubmit;
115+
const onQuery = (content: string) => {
116+
console.log('content');
117+
updateStore(draft => {});
118+
};
119+
const handleClear = () => {
120+
updateState(pre => {
121+
return {
122+
...pre,
123+
messages: [...getWelcomeMessage(welcome, prompt, schemaData)],
124+
};
125+
});
126+
};
127+
128+
const handleSave = value => {
129+
updateState(pre => {
130+
return {
131+
...pre,
132+
OPENAI_KEY_FOR_GS: value,
133+
};
134+
});
135+
};
136+
137+
return (
138+
<Flex vertical justify="space-between">
139+
<Flex vertical gap={12} flex={1}>
140+
<Flex justify="space-between" align="center">
141+
<Typography.Text type="secondary" italic>
142+
Think like a bot
143+
</Typography.Text>
144+
<Setting onChange={handleSave} />
145+
</Flex>
146+
<Flex vertical gap={12} style={{ overflowY: 'scroll', height: 'calc(100vh - 170px)' }}>
147+
{messages
148+
.filter(m => m.role !== 'system')
149+
.map(item => {
150+
return <MessageItem key={item.timestamp} {...item} onQuery={onQuery} />;
151+
})}
152+
{isLoading && <Skeleton style={{ padding: '0px 6px' }} />}
153+
</Flex>
154+
</Flex>
155+
<Flex vertical>
156+
<Flex vertical gap={12}>
157+
{recommended_messages.map((item, index) => {
158+
return (
159+
<div
160+
key={index}
161+
style={{
162+
cursor: 'pointer',
163+
background: token.colorBgLayout,
164+
padding: '4px',
165+
borderRadius: '6px',
166+
margin: '6px 0px',
167+
}}
168+
>
169+
<BulbOutlined style={{ fontSize: '12px', marginRight: '4px' }} />
170+
<Typography.Text
171+
key={index}
172+
onClick={() => {
173+
handleSubmit(item);
174+
}}
175+
style={{
176+
fontSize: '12px',
177+
}}
178+
>
179+
{item}
180+
</Typography.Text>
181+
</div>
182+
);
183+
})}
184+
</Flex>
185+
<Flex gap={8}>
186+
<Input ref={InputRef}></Input>
187+
<Button onClick={() => handleSubmit()} icon={<SearchOutlined />}></Button>
188+
<Button onClick={handleClear} icon={<ClearOutlined />}></Button>
189+
</Flex>
190+
</Flex>
191+
</Flex>
192+
);
193+
};
194+
195+
export default GPTStatements;

0 commit comments

Comments
 (0)