@nethru/kit 1.1.13 → 1.1.15
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/dist/components/chat/AiChat.js +1 -1
- package/dist/components/chat/contexts/ChatContext.js +25 -5
- package/dist/components/chat/{ChatInput.js → input/ChatInput.js} +20 -10
- package/dist/components/chat/{ModelSelect.js → input/ModelSelect.js} +3 -4
- package/dist/components/chat/input/Options.js +72 -0
- package/dist/js/promptIntent.js +0 -29
- package/package.json +1 -1
- package/dist/components/chat/mock.js +0 -1153
|
@@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
|
|
|
2
2
|
import { Stack } from "@mui/material";
|
|
3
3
|
import { useChatContext } from './contexts/ChatContext';
|
|
4
4
|
import ChatMessages from './ChatMessages';
|
|
5
|
-
import ChatInput from './ChatInput';
|
|
5
|
+
import ChatInput from './input/ChatInput';
|
|
6
6
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
7
|
function AiChat({
|
|
8
8
|
mainPageRef,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
1
|
+
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { getConfig } from "../../../js/config";
|
|
3
|
-
import
|
|
3
|
+
import AutoStoriesIcon from "@mui/icons-material/AutoStories";
|
|
4
|
+
import WysiwygIcon from "@mui/icons-material/Wysiwyg";
|
|
4
5
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
6
|
const ChatContext = /*#__PURE__*/createContext();
|
|
6
7
|
export function ChatProvider({
|
|
@@ -16,6 +17,8 @@ export function ChatProvider({
|
|
|
16
17
|
const [provider, setProvider] = useState(defaultProvider);
|
|
17
18
|
const [providers, setProviders] = useState([]);
|
|
18
19
|
const [model, setModel] = useState(defaultModel);
|
|
20
|
+
const [option, setOption] = useState();
|
|
21
|
+
const options = useMemo(() => defaultOptions);
|
|
19
22
|
const [tools, setTools] = useState([]);
|
|
20
23
|
const chatContainerRef = useRef(null);
|
|
21
24
|
const mainPageRef = useRef(null);
|
|
@@ -68,7 +71,8 @@ export function ChatProvider({
|
|
|
68
71
|
conversationId: conversationId,
|
|
69
72
|
message: message,
|
|
70
73
|
provider: provider,
|
|
71
|
-
model: model
|
|
74
|
+
model: model,
|
|
75
|
+
option: option
|
|
72
76
|
})
|
|
73
77
|
});
|
|
74
78
|
return await response.json();
|
|
@@ -110,7 +114,7 @@ export function ChatProvider({
|
|
|
110
114
|
setInputValue('');
|
|
111
115
|
setIsLoading(true);
|
|
112
116
|
try {
|
|
113
|
-
let data =
|
|
117
|
+
let data = option === 'ANALYZE' ? await analyzePage(message) : await answer(message);
|
|
114
118
|
setConversationId(data.conversationId);
|
|
115
119
|
setMessages(prev => [...prev, {
|
|
116
120
|
role: data.role,
|
|
@@ -145,6 +149,9 @@ export function ChatProvider({
|
|
|
145
149
|
setProvider,
|
|
146
150
|
model,
|
|
147
151
|
setModel,
|
|
152
|
+
options,
|
|
153
|
+
option,
|
|
154
|
+
setOption,
|
|
148
155
|
tools,
|
|
149
156
|
chatContainerRef,
|
|
150
157
|
mainPageRef,
|
|
@@ -164,4 +171,17 @@ export function useChatContext() {
|
|
|
164
171
|
}
|
|
165
172
|
return context;
|
|
166
173
|
}
|
|
167
|
-
export default ChatContext;
|
|
174
|
+
export default ChatContext;
|
|
175
|
+
const defaultOptions = [{
|
|
176
|
+
value: 'ANALYZE',
|
|
177
|
+
label: '페이지 요약 및 분석',
|
|
178
|
+
icon: /*#__PURE__*/_jsx(WysiwygIcon, {
|
|
179
|
+
fontSize: "small"
|
|
180
|
+
})
|
|
181
|
+
}, {
|
|
182
|
+
value: 'RAG',
|
|
183
|
+
label: '지식 베이스에서 문의',
|
|
184
|
+
icon: /*#__PURE__*/_jsx(AutoStoriesIcon, {
|
|
185
|
+
fontSize: "small"
|
|
186
|
+
})
|
|
187
|
+
}];
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { Box,
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { Box, IconButton, Stack, TextField } from '@mui/material';
|
|
3
3
|
import { Send as SendIcon } from '@mui/icons-material';
|
|
4
|
-
import { useChatContext } from "
|
|
4
|
+
import { useChatContext } from "../contexts/ChatContext";
|
|
5
5
|
import ModelSelect from "./ModelSelect";
|
|
6
|
+
import Options from "./Options";
|
|
6
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
8
|
const ChatInput = () => {
|
|
8
9
|
const inputRef = useRef(null);
|
|
@@ -11,7 +12,10 @@ const ChatInput = () => {
|
|
|
11
12
|
inputValue,
|
|
12
13
|
setInputValue,
|
|
13
14
|
isLoading,
|
|
14
|
-
sendMessage
|
|
15
|
+
sendMessage,
|
|
16
|
+
option,
|
|
17
|
+
setOption,
|
|
18
|
+
options
|
|
15
19
|
} = useChatContext();
|
|
16
20
|
const handleKeyPress = e => {
|
|
17
21
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
@@ -21,7 +25,7 @@ const ChatInput = () => {
|
|
|
21
25
|
};
|
|
22
26
|
useEffect(() => {
|
|
23
27
|
if (!isLoading && !selectOpen && inputRef.current) inputRef.current.focus();
|
|
24
|
-
}, [isLoading, selectOpen]);
|
|
28
|
+
}, [isLoading, selectOpen, option]);
|
|
25
29
|
return /*#__PURE__*/_jsx(Box, {
|
|
26
30
|
sx: styles.container,
|
|
27
31
|
children: /*#__PURE__*/_jsxs(Stack, {
|
|
@@ -45,15 +49,21 @@ const ChatInput = () => {
|
|
|
45
49
|
}), /*#__PURE__*/_jsxs(Stack, {
|
|
46
50
|
direction: "row",
|
|
47
51
|
gap: 2,
|
|
48
|
-
justifyContent: "
|
|
52
|
+
justifyContent: "space-between",
|
|
49
53
|
alignItems: "center",
|
|
50
|
-
children: [/*#__PURE__*/
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
children: [/*#__PURE__*/_jsxs(Stack, {
|
|
55
|
+
direction: "row",
|
|
56
|
+
alignItems: "center",
|
|
57
|
+
gap: 2,
|
|
58
|
+
children: [/*#__PURE__*/_jsx(Options, {
|
|
59
|
+
options: options,
|
|
60
|
+
option: option,
|
|
61
|
+
setOption: setOption
|
|
62
|
+
}), /*#__PURE__*/_jsx(ModelSelect, {
|
|
53
63
|
open: selectOpen,
|
|
54
64
|
onOpen: () => setSelectOpen(true),
|
|
55
65
|
onClose: () => setSelectOpen(false)
|
|
56
|
-
})
|
|
66
|
+
})]
|
|
57
67
|
}), /*#__PURE__*/_jsx(IconButton, {
|
|
58
68
|
onClick: sendMessage,
|
|
59
69
|
disabled: isLoading || !inputValue.trim(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Divider, MenuItem, Select } from "@mui/material";
|
|
3
|
-
import { useChatContext } from "
|
|
3
|
+
import { useChatContext } from "../contexts/ChatContext";
|
|
4
4
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
5
|
export default function ModelSelect({
|
|
6
6
|
...props
|
|
@@ -70,12 +70,11 @@ function findModel(providers, value) {
|
|
|
70
70
|
const styles = {
|
|
71
71
|
select: {
|
|
72
72
|
color: '#666',
|
|
73
|
-
fontSize: '
|
|
74
|
-
fontWeight: 400
|
|
73
|
+
fontSize: '11px'
|
|
75
74
|
},
|
|
76
75
|
name: {
|
|
77
76
|
fontWeight: 400,
|
|
78
|
-
fontSize: '
|
|
77
|
+
fontSize: '12px'
|
|
79
78
|
},
|
|
80
79
|
desc: {
|
|
81
80
|
fontSize: '11px',
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ToggleButton, toggleButtonClasses, ToggleButtonGroup, toggleButtonGroupClasses, Tooltip, Typography } from "@mui/material";
|
|
3
|
+
import { styled } from '@mui/material/styles';
|
|
4
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({
|
|
6
|
+
theme
|
|
7
|
+
}) => ({
|
|
8
|
+
gap: '5px',
|
|
9
|
+
[`& .${toggleButtonGroupClasses.firstButton}, & .${toggleButtonGroupClasses.middleButton}`]: {
|
|
10
|
+
borderTopRightRadius: '6px',
|
|
11
|
+
borderBottomRightRadius: '6px'
|
|
12
|
+
},
|
|
13
|
+
[`& .${toggleButtonGroupClasses.lastButton}, & .${toggleButtonGroupClasses.middleButton}`]: {
|
|
14
|
+
borderTopLeftRadius: '6px',
|
|
15
|
+
borderBottomLeftRadius: '6px'
|
|
16
|
+
},
|
|
17
|
+
[`& .${toggleButtonGroupClasses.lastButton}.${toggleButtonClasses.disabled}, & .${toggleButtonGroupClasses.middleButton}.${toggleButtonClasses.disabled}`]: {}
|
|
18
|
+
}));
|
|
19
|
+
export default function Options({
|
|
20
|
+
options = [],
|
|
21
|
+
option,
|
|
22
|
+
setOption,
|
|
23
|
+
...props
|
|
24
|
+
}) {
|
|
25
|
+
return /*#__PURE__*/_jsx(StyledToggleButtonGroup, {
|
|
26
|
+
exclusive: true,
|
|
27
|
+
value: option,
|
|
28
|
+
onChange: (_, value) => setOption(value),
|
|
29
|
+
children: options.map(o => /*#__PURE__*/_jsx(Tooltip, {
|
|
30
|
+
title: option === o.value ? "" : o.label,
|
|
31
|
+
slotProps: {
|
|
32
|
+
tooltip: {
|
|
33
|
+
sx: styles.tooltip
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
children: /*#__PURE__*/_jsxs(ToggleButton, {
|
|
37
|
+
value: o.value,
|
|
38
|
+
size: "small",
|
|
39
|
+
sx: styles.button(option === o.value),
|
|
40
|
+
...props,
|
|
41
|
+
children: [o.icon, option === o.value && /*#__PURE__*/_jsx(Typography, {
|
|
42
|
+
sx: styles.text,
|
|
43
|
+
children: o.label
|
|
44
|
+
})]
|
|
45
|
+
})
|
|
46
|
+
}, o.value))
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const styles = {
|
|
50
|
+
button: selected => ({
|
|
51
|
+
border: 'none',
|
|
52
|
+
width: selected ? 'auto' : '25px',
|
|
53
|
+
height: '25px',
|
|
54
|
+
minWidth: 0,
|
|
55
|
+
padding: selected ? '0 8px' : '0',
|
|
56
|
+
'&.Mui-selected': {
|
|
57
|
+
backgroundColor: '#333',
|
|
58
|
+
color: 'white',
|
|
59
|
+
'&:hover': {
|
|
60
|
+
backgroundColor: 'black'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
text: {
|
|
65
|
+
ml: 1,
|
|
66
|
+
fontSize: '11px',
|
|
67
|
+
whiteSpace: 'nowrap'
|
|
68
|
+
},
|
|
69
|
+
tooltip: {
|
|
70
|
+
fontSize: '10px'
|
|
71
|
+
}
|
|
72
|
+
};
|
package/dist/js/promptIntent.js
CHANGED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export function isPageAnalysisIntent(userPrompt) {
|
|
2
|
-
if (!userPrompt || userPrompt.trim() === "") return false;
|
|
3
|
-
const analysisKeywords = ["요약", "정리", "분석", "페이지", "화면", "글", "기사", "문서", "내용", "포스트", "블로그", "뭐야", "뭔가", "무엇", "어떤", "핵심", "주요", "포인트", "요점", "이", "현재", "지금", "여기", "summarize", "summary", "analyze", "analysis", "explain", "this page", "current page", "this article", "this post"];
|
|
4
|
-
const excludeKeywords = ["http://", "https://", "www.", "날씨", "weather", "뉴스", "news", "코드", "code", "프로그램", "program"];
|
|
5
|
-
const prompt = userPrompt.toLowerCase();
|
|
6
|
-
|
|
7
|
-
// 제외 키워드 포함 시, "이 페이지" 패턴만 예외 허용
|
|
8
|
-
if (excludeKeywords.some(k => prompt.includes(k))) {
|
|
9
|
-
if (!(prompt.includes("이") && prompt.includes("페이지"))) {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
const matchedKeywords = analysisKeywords.filter(k => prompt.includes(k));
|
|
14
|
-
|
|
15
|
-
// 최소 2개 이상의 키워드 매칭
|
|
16
|
-
if (matchedKeywords.length >= 2) return true;
|
|
17
|
-
|
|
18
|
-
// 단일 키워드지만 강한 패턴
|
|
19
|
-
// const strongPatterns = [
|
|
20
|
-
// "요약해", "정리해", "분석해",
|
|
21
|
-
// "요약 해", "정리 해", "분석 해",
|
|
22
|
-
// "페이지 요약", "페이지 정리", "페이지 분석",
|
|
23
|
-
// "summarize this", "analyze this", "explain this"
|
|
24
|
-
// ];
|
|
25
|
-
//
|
|
26
|
-
// if(strongPatterns.some(p => prompt.includes(p))) return true;
|
|
27
|
-
|
|
28
|
-
return false;
|
|
29
|
-
}
|