@stack-spot/ai-chat-widget 2.0.0-betacitric.8 → 2.0.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/CHANGELOG.md +68 -0
- package/dist/StackspotAIWidget.d.ts.map +1 -1
- package/dist/StackspotAIWidget.js +2 -1
- package/dist/StackspotAIWidget.js.map +1 -1
- package/dist/app-metadata.json +17 -5
- package/dist/chat-interceptors/quick-commands.d.ts.map +1 -1
- package/dist/chat-interceptors/quick-commands.js +9 -3
- package/dist/chat-interceptors/quick-commands.js.map +1 -1
- package/dist/components/HistoryList.d.ts.map +1 -1
- package/dist/components/HistoryList.js +2 -0
- package/dist/components/HistoryList.js.map +1 -1
- package/dist/components/form/DescribedRadioGroup.d.ts.map +1 -1
- package/dist/components/form/DescribedRadioGroup.js +1 -1
- package/dist/components/form/DescribedRadioGroup.js.map +1 -1
- package/dist/hooks/midnight-update-view.d.ts +5 -0
- package/dist/hooks/midnight-update-view.d.ts.map +1 -0
- package/dist/hooks/midnight-update-view.js +30 -0
- package/dist/hooks/midnight-update-view.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout.css +1 -1
- package/dist/state/WidgetState.d.ts +1 -1
- package/dist/state/WidgetState.d.ts.map +1 -1
- package/dist/state/constants.d.ts.map +1 -1
- package/dist/state/constants.js +3 -2
- package/dist/state/constants.js.map +1 -1
- package/dist/utils/tools.d.ts +9 -2
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +7 -1
- package/dist/utils/tools.js.map +1 -1
- package/dist/utils/upload/FileUpload.d.ts.map +1 -1
- package/dist/utils/upload/FileUpload.js +1 -2
- package/dist/utils/upload/FileUpload.js.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.js +21 -13
- package/dist/views/Agents/AgentDescription.js.map +1 -1
- package/dist/views/Agents/dictionary.d.ts +1 -1
- package/dist/views/Agents/dictionary.d.ts.map +1 -1
- package/dist/views/Agents/dictionary.js +2 -0
- package/dist/views/Agents/dictionary.js.map +1 -1
- package/dist/views/Agents/styled.d.ts.map +1 -1
- package/dist/views/Agents/styled.js +14 -3
- package/dist/views/Agents/styled.js.map +1 -1
- package/dist/views/Agents/useAgentFavorites.js +3 -3
- package/dist/views/Agents/useAgentFavorites.js.map +1 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +12 -8
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/styled.d.ts.map +1 -1
- package/dist/views/Chat/styled.js +24 -0
- package/dist/views/Chat/styled.js.map +1 -1
- package/dist/views/MessageInput/ContextBar.d.ts.map +1 -1
- package/dist/views/MessageInput/ContextBar.js +1 -1
- package/dist/views/MessageInput/ContextBar.js.map +1 -1
- package/dist/views/MessageInput/SelectContent.d.ts.map +1 -1
- package/dist/views/MessageInput/SelectContent.js +6 -0
- package/dist/views/MessageInput/SelectContent.js.map +1 -1
- package/dist/views/MessageInput/UploadBar.d.ts.map +1 -1
- package/dist/views/MessageInput/UploadBar.js +29 -2
- package/dist/views/MessageInput/UploadBar.js.map +1 -1
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/dist/views/MessageInput/dictionary.d.ts.map +1 -1
- package/dist/views/MessageInput/dictionary.js +4 -0
- package/dist/views/MessageInput/dictionary.js.map +1 -1
- package/dist/views/MessageInput/styled.d.ts.map +1 -1
- package/dist/views/MessageInput/styled.js +12 -0
- package/dist/views/MessageInput/styled.js.map +1 -1
- package/dist/views/Resources.d.ts +2 -0
- package/dist/views/Resources.d.ts.map +1 -0
- package/dist/views/Resources.js +59 -0
- package/dist/views/Resources.js.map +1 -0
- package/dist/views/Steps/dictionary.d.ts +1 -1
- package/dist/views/Tools.js +4 -2
- package/dist/views/Tools.js.map +1 -1
- package/package.json +13 -10
- package/src/StackspotAIWidget.tsx +2 -0
- package/src/app-metadata.json +17 -5
- package/src/chat-interceptors/quick-commands.ts +11 -5
- package/src/components/HistoryList.tsx +3 -0
- package/src/components/form/DescribedRadioGroup.tsx +0 -1
- package/src/hooks/midnight-update-view.ts +36 -0
- package/src/index.ts +4 -3
- package/src/layout.css +1 -1
- package/src/state/WidgetState.ts +1 -1
- package/src/state/constants.ts +3 -2
- package/src/utils/tools.ts +15 -3
- package/src/utils/upload/FileUpload.ts +1 -2
- package/src/views/Agents/AgentDescription.tsx +50 -23
- package/src/views/Agents/dictionary.ts +2 -0
- package/src/views/Agents/styled.ts +14 -3
- package/src/views/Agents/useAgentFavorites.ts +3 -3
- package/src/views/Chat/ChatMessage.tsx +41 -7
- package/src/views/Chat/styled.ts +24 -0
- package/src/views/MessageInput/ContextBar.tsx +1 -2
- package/src/views/MessageInput/SelectContent.tsx +6 -0
- package/src/views/MessageInput/UploadBar.tsx +40 -2
- package/src/views/MessageInput/dictionary.ts +4 -0
- package/src/views/MessageInput/styled.ts +12 -0
- package/src/views/Resources.tsx +99 -0
- package/src/views/Tools.tsx +18 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Icon } from '@stack-spot/citric-icons'
|
|
2
|
+
import { Badge, Card, IconBox, ImageBox, ImageWithFallback, Skeleton, Text } from '@stack-spot/citric-react'
|
|
2
3
|
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
3
4
|
import { useMemo } from 'react'
|
|
4
|
-
import { ToolBadge } from '../../components/ToolBadge'
|
|
5
5
|
import { toolById } from '../../utils/tools'
|
|
6
6
|
import { useAgentsDictionary } from './dictionary'
|
|
7
7
|
import { AgentDescriptionBox } from './styled'
|
|
@@ -10,11 +10,16 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
10
10
|
const t = useAgentsDictionary()
|
|
11
11
|
const [agent, , , { isLoading }] = agentToolsClient.agent.useStatefulQuery({ agentId: agentId! }, { enabled: !!agentId })
|
|
12
12
|
const [toolKits, , , { isLoading: isLoadingToolKit }] = agentToolsClient.tools.useStatefulQuery({})
|
|
13
|
-
const numberOfKnowledgeSources = agent?.
|
|
13
|
+
const numberOfKnowledgeSources = agent?.knowledge_sources_config?.knowledge_sources.length ?? 0
|
|
14
14
|
|
|
15
15
|
const knowledgeSources = useMemo(
|
|
16
|
-
() => agent?.
|
|
17
|
-
<li key={index}
|
|
16
|
+
() => agent?.knowledge_sources_config?.knowledge_sources_details?.map((ks, index) => (
|
|
17
|
+
<li key={index}>
|
|
18
|
+
<Card gap="10px" direction="row" flex={1} size="xxs" bgLevel={500} justifyContent="space-between">
|
|
19
|
+
<Text color="light.contrastText">{ks.name}</Text>
|
|
20
|
+
<Badge colorScheme="inverse" appearance="square">{ks.type}</Badge>
|
|
21
|
+
</Card>
|
|
22
|
+
</li>
|
|
18
23
|
)),
|
|
19
24
|
[agent],
|
|
20
25
|
)
|
|
@@ -25,32 +30,50 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
25
30
|
}
|
|
26
31
|
return loadingKS
|
|
27
32
|
}, [numberOfKnowledgeSources])
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
33
|
+
|
|
34
|
+
const { tools, multiAgents } = useMemo(() => {
|
|
35
|
+
const tools: React.ReactElement[] = []
|
|
36
|
+
const multiAgents: React.ReactElement[] = []
|
|
37
|
+
const builtInTools = agent?.toolkits?.builtin_toolkits ?? []
|
|
31
38
|
const customToolkits = agent?.toolkits?.custom_toolkits ?? []
|
|
32
|
-
for (const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
for (const toolkit of builtInTools) {
|
|
40
|
+
for (const tool of toolkit.tools ?? []) {
|
|
41
|
+
if (toolkit.id == 'UTILITIES'){
|
|
42
|
+
const toolWithImage = toolById(tool.id, toolKits)
|
|
43
|
+
tools.push(
|
|
44
|
+
<li key={tool.id}>
|
|
45
|
+
<Card gap="10px" direction="row" flex={1} size="xxs" bgLevel={500}>
|
|
46
|
+
<ImageBox><ImageWithFallback src={toolWithImage?.image} fallback={<Icon icon="Cog" />} /></ImageBox>
|
|
47
|
+
<Text color="light.contrastText">{toolWithImage?.name || toolWithImage?.id || 'unknown'}</Text>
|
|
48
|
+
</Card>
|
|
49
|
+
</li>,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
if (toolkit.id == 'MULTI_AGENTS'){
|
|
53
|
+
multiAgents.push(
|
|
54
|
+
<li key={tool.id}>
|
|
55
|
+
<Card gap="10px" direction="row" flex={1} size="xxs" bgLevel={500}>
|
|
56
|
+
<IconBox icon="Agent" />
|
|
57
|
+
<Text color="light.contrastText">{tool.name}</Text>
|
|
58
|
+
</Card>
|
|
59
|
+
</li>,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
43
63
|
}
|
|
44
64
|
for (const toolkit of customToolkits) {
|
|
45
65
|
for (const tool of toolkit.tools) {
|
|
46
|
-
|
|
66
|
+
tools.push(
|
|
47
67
|
<li key={tool.id}>
|
|
48
|
-
<
|
|
68
|
+
<Card gap="10px" direction="row" flex={1} size="xxs" bgLevel={500}>
|
|
69
|
+
<ImageBox><ImageWithFallback src={toolkit.avatar ?? undefined} fallback={<Icon icon="Cog" />} /></ImageBox>
|
|
70
|
+
<Text color="light.contrastText">{tool.name}</Text>
|
|
71
|
+
</Card>
|
|
49
72
|
</li>,
|
|
50
73
|
)
|
|
51
74
|
}
|
|
52
75
|
}
|
|
53
|
-
return
|
|
76
|
+
return { tools, multiAgents }
|
|
54
77
|
}, [agent])
|
|
55
78
|
|
|
56
79
|
return (
|
|
@@ -63,10 +86,14 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
63
86
|
<Text appearance="microtext1" className="title">Knowledge sources</Text>
|
|
64
87
|
<ul>{isLoading || isLoadingToolKit ? skeleton : knowledgeSources}</ul>
|
|
65
88
|
</section>}
|
|
66
|
-
{!!tools
|
|
89
|
+
{!!tools?.length && <section>
|
|
67
90
|
<Text appearance="microtext1" className="title">{t.tools}</Text>
|
|
68
91
|
<ul>{tools}</ul>
|
|
69
92
|
</section>}
|
|
93
|
+
{!!multiAgents?.length && <section>
|
|
94
|
+
<Text appearance="microtext1" className="title">{t.multiAgent}</Text>
|
|
95
|
+
<ul>{multiAgents}</ul>
|
|
96
|
+
</section>}
|
|
70
97
|
{agent?.model_name && <section>
|
|
71
98
|
<Text appearance="microtext1" className="title">LLM</Text>
|
|
72
99
|
<Badge colorPalette="orange" appearance="square">{agent?.model_name}</Badge>
|
|
@@ -18,6 +18,7 @@ const dictionary = {
|
|
|
18
18
|
favorites: 'Favorites',
|
|
19
19
|
tools: 'Tools',
|
|
20
20
|
spots: 'Spots',
|
|
21
|
+
multiAgent: 'Multi-agents',
|
|
21
22
|
},
|
|
22
23
|
pt: {
|
|
23
24
|
title: 'Agentes',
|
|
@@ -36,6 +37,7 @@ const dictionary = {
|
|
|
36
37
|
favorites: 'Favoritos',
|
|
37
38
|
tools: 'Ferramentas',
|
|
38
39
|
spots: 'Spots',
|
|
40
|
+
multiAgent: 'Multi-agents',
|
|
39
41
|
|
|
40
42
|
},
|
|
41
43
|
} satisfies Dictionary
|
|
@@ -18,9 +18,11 @@ export const AgentLabel = styled.div`
|
|
|
18
18
|
export const AgentDescriptionBox = styled.div`
|
|
19
19
|
color: ${theme.color.light[700]};
|
|
20
20
|
line-height: 18px;
|
|
21
|
+
background-color: ${theme.color.light[400]};
|
|
22
|
+
margin: -10px;
|
|
23
|
+
padding: 8px;
|
|
21
24
|
|
|
22
25
|
section {
|
|
23
|
-
border-bottom: 1px solid ${theme.color.light[600]};
|
|
24
26
|
padding-bottom: 10px;
|
|
25
27
|
margin-bottom: 10px;
|
|
26
28
|
|
|
@@ -34,6 +36,14 @@ export const AgentDescriptionBox = styled.div`
|
|
|
34
36
|
display: block;
|
|
35
37
|
margin-bottom: 6px;
|
|
36
38
|
}
|
|
39
|
+
|
|
40
|
+
.card-agent-info {
|
|
41
|
+
background-color: ${theme.color.light[500]};
|
|
42
|
+
border: 1px solid ${theme.color.light[600]};
|
|
43
|
+
border-radius: 3px;
|
|
44
|
+
padding: 4px;
|
|
45
|
+
width: 100%;
|
|
46
|
+
}
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
ul {
|
|
@@ -44,10 +54,11 @@ export const AgentDescriptionBox = styled.div`
|
|
|
44
54
|
flex-direction: row;
|
|
45
55
|
flex-wrap: wrap;
|
|
46
56
|
white-space: nowrap;
|
|
47
|
-
gap: 6px;
|
|
48
57
|
|
|
49
58
|
li {
|
|
50
|
-
margin: 3px
|
|
59
|
+
margin-bottom: 3px;
|
|
60
|
+
display: flex;
|
|
61
|
+
width: 100%;
|
|
51
62
|
}
|
|
52
63
|
}
|
|
53
64
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable filenames/match-regex */
|
|
2
|
-
import {
|
|
2
|
+
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
3
3
|
|
|
4
4
|
export function useAgentFavorites() {
|
|
5
5
|
const useFavorites = () => agentToolsClient.agents.useQuery({ visibility: 'favorite' })
|
|
6
|
-
const [addFavorite, pendingAddFav] =
|
|
7
|
-
const [removeFavorite, pendingRemoveFav] =
|
|
6
|
+
const [addFavorite, pendingAddFav] = agentToolsClient.addFavorite.useMutation()
|
|
7
|
+
const [removeFavorite, pendingRemoveFav] = agentToolsClient.removeFavorite.useMutation()
|
|
8
8
|
|
|
9
9
|
const removeFavoriteAgent = async (idOrSlug?: string) => {
|
|
10
10
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Icon } from '@stack-spot/citric-icons'
|
|
2
|
-
import { Alert, Badge, Button, CheckboxGroup, Column, IconButton, Input, RadioGroup, Row, Text, Tooltip } from '@stack-spot/citric-react'
|
|
2
|
+
import { Alert, Badge, Button, CheckboxGroup, Column, IconButton, ImageBox, ImageWithFallback, Input, RadioGroup, Row, Text, Tooltip } from '@stack-spot/citric-react'
|
|
3
3
|
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
4
4
|
import { listToClass, theme } from '@stack-spot/portal-theme'
|
|
5
5
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
@@ -9,8 +9,8 @@ import { PhoneInput } from 'react-international-phone'
|
|
|
9
9
|
import 'react-international-phone/style.css'
|
|
10
10
|
import { FileDescription } from '../../components/FileDescription'
|
|
11
11
|
import { Markdown } from '../../components/Markdown'
|
|
12
|
-
import { StackedBadge } from '../../components/StackedBadge'
|
|
13
12
|
import { useChatEntry, useCurrentChat, useWidget } from '../../context/hooks'
|
|
13
|
+
import { useMidnightUpdateView } from '../../hooks/midnight-update-view'
|
|
14
14
|
import { ChatEntry, SerializableAction, TextChatEntry } from '../../state/ChatEntry'
|
|
15
15
|
import { useDateFormatter } from '../../utils/date'
|
|
16
16
|
import { toolById } from '../../utils/tools'
|
|
@@ -214,10 +214,13 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
214
214
|
const chat = useCurrentChat()
|
|
215
215
|
const agentId = entry.agent?.id ?? ''
|
|
216
216
|
const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!agentId })
|
|
217
|
+
const [agentsTools] = agentToolsClient.agentsByIds.useStatefulQuery(
|
|
218
|
+
{ searchAgentsRequest: { ids: entry.tools || [''] } }, { enabled: !!entry.tools })
|
|
217
219
|
const [copied, setCopied] = useState(false)
|
|
218
220
|
const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
|
|
219
221
|
|
|
220
222
|
useChatScrollToBottomEffect(ref, [entry])
|
|
223
|
+
useMidnightUpdateView()
|
|
221
224
|
|
|
222
225
|
const detailKS = useCallback(({ name, slug, documentScore, documentId }: Required<TextChatEntry>['knowledgeSources'][number]) => {
|
|
223
226
|
widget.set('currentKSInPanel', { name, slug, score: documentScore, documentId })
|
|
@@ -335,12 +338,11 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
335
338
|
</form>
|
|
336
339
|
}
|
|
337
340
|
|
|
338
|
-
function
|
|
341
|
+
function openResourcesPanel() {
|
|
339
342
|
widget.set('currentMessageInPanel', { chatId: chat.id, messageId: message.id })
|
|
340
|
-
widget.set('panel', '
|
|
343
|
+
widget.set('panel', 'resources')
|
|
341
344
|
}
|
|
342
345
|
|
|
343
|
-
|
|
344
346
|
return (entry.content || entry.error || !!entry.steps?.length || entry.upload?.length) && (
|
|
345
347
|
<li key={entry.messageId} className={entry.agentType} ref={ref}>
|
|
346
348
|
<div className="chat-message-container"
|
|
@@ -363,7 +365,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
363
365
|
{entry.error && <Alert type="error">{entry.error}</Alert>}
|
|
364
366
|
</div>
|
|
365
367
|
{afterMessage && createElement(afterMessage, { message })}
|
|
366
|
-
{!!entry.tools?.length && <StackedBadge
|
|
368
|
+
{/* {!!entry.tools?.length && <StackedBadge
|
|
367
369
|
aria-label={t.openToolsPanel}
|
|
368
370
|
title={t.openToolsPanel}
|
|
369
371
|
tabIndex={0}
|
|
@@ -376,7 +378,8 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
376
378
|
})}
|
|
377
379
|
onClick={openToolsPanel}
|
|
378
380
|
style={{ marginTop: '12px', width: 'fit-content' }}
|
|
379
|
-
/>}
|
|
381
|
+
/>} */}
|
|
382
|
+
|
|
380
383
|
{!!entry.knowledgeSources?.length && <div className="ks-box">
|
|
381
384
|
<Text appearance="microtext1" color="light.700">Knowledge Sources:</Text>
|
|
382
385
|
<ul>{entry.knowledgeSources.map((ks, index) => (
|
|
@@ -385,6 +388,35 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
385
388
|
</li>
|
|
386
389
|
))}</ul>
|
|
387
390
|
</div>}
|
|
391
|
+
|
|
392
|
+
{(!!agentsTools?.length || !!entry.tools?.length) && <div className="tools-box">
|
|
393
|
+
<Button appearance="none" onClick={openResourcesPanel} aria-label={t.openResourcesPanel}>
|
|
394
|
+
{agentsTools?.map((agent) => (
|
|
395
|
+
<ImageBox key={agent.id} className="agent-info-avatar-resource">
|
|
396
|
+
<ImageWithFallback
|
|
397
|
+
src={agent.avatar ?? undefined}
|
|
398
|
+
fallback={<Icon icon="Agent" />}
|
|
399
|
+
alt={agent.name}
|
|
400
|
+
aria-label={agent.name}
|
|
401
|
+
title={agent.name}
|
|
402
|
+
/>
|
|
403
|
+
</ImageBox>
|
|
404
|
+
))}
|
|
405
|
+
{entry.tools?.map((id) => {
|
|
406
|
+
const tool = toolById(id, toolKits)
|
|
407
|
+
return (
|
|
408
|
+
<ImageBox key={id} className="agent-info-avatar-resource">
|
|
409
|
+
<ImageWithFallback
|
|
410
|
+
src={tool?.image}
|
|
411
|
+
fallback={<Icon icon="Cog" />}
|
|
412
|
+
alt={tool?.name}
|
|
413
|
+
aria-label={tool?.name}
|
|
414
|
+
title={tool?.name}
|
|
415
|
+
/>
|
|
416
|
+
</ImageBox>
|
|
417
|
+
)})}
|
|
418
|
+
</Button>
|
|
419
|
+
</div>}
|
|
388
420
|
|
|
389
421
|
{shouldShowFooter && <div className="message-footer">
|
|
390
422
|
{entry.agentType === 'bot' && !entry.error && <div className="message-actions">
|
|
@@ -467,6 +499,7 @@ const dictionary = {
|
|
|
467
499
|
dislike: 'Dislike',
|
|
468
500
|
tools: 'Tools',
|
|
469
501
|
openToolsPanel: 'Open the tools panel to see more details.',
|
|
502
|
+
openResourcesPanel: 'Open the resources panel to see more details.',
|
|
470
503
|
copied: 'Copied',
|
|
471
504
|
},
|
|
472
505
|
pt: {
|
|
@@ -475,6 +508,7 @@ const dictionary = {
|
|
|
475
508
|
dislike: 'Não gostei',
|
|
476
509
|
tools: 'Ferramentas',
|
|
477
510
|
openToolsPanel: 'Abrir o painel de ferramentas para ver mais detalhes.',
|
|
511
|
+
openResourcesPanel: 'Abrir o painel de recursos para ver mais detalhes.',
|
|
478
512
|
copied: 'Copiado',
|
|
479
513
|
},
|
|
480
514
|
} satisfies Dictionary
|
package/src/views/Chat/styled.ts
CHANGED
|
@@ -218,6 +218,30 @@ export const ChatList: IStyledComponentBase<
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
.tools-box {
|
|
222
|
+
|
|
223
|
+
> ul {
|
|
224
|
+
display: flex;
|
|
225
|
+
flex-direction: row;
|
|
226
|
+
flex-wrap: wrap;
|
|
227
|
+
white-space: nowrap;
|
|
228
|
+
margin: 0;
|
|
229
|
+
margin-top: 8px;
|
|
230
|
+
padding: 0;
|
|
231
|
+
list-style: none;
|
|
232
|
+
gap: 6px;
|
|
233
|
+
|
|
234
|
+
&:hover{
|
|
235
|
+
cursor: pointer;
|
|
236
|
+
|
|
237
|
+
.agent-info-avatar-resource {
|
|
238
|
+
transition: margin-left 0.3s ease-in;
|
|
239
|
+
margin-left: 0px;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
221
245
|
.steps {
|
|
222
246
|
ul {
|
|
223
247
|
list-style: none;
|
|
@@ -22,8 +22,7 @@ const ContextBadge = ({ label, color, dismiss, onDismiss }: ContextBadgeProps) =
|
|
|
22
22
|
onClick={onDismiss}
|
|
23
23
|
title={dismiss}
|
|
24
24
|
arial-label={dismiss}
|
|
25
|
-
style={{ padding: '2px 0 2px 2px' }}
|
|
26
|
-
colorPalette={color}
|
|
25
|
+
style={{ padding: '2px 0 2px 2px', color: 'inherit' }}
|
|
27
26
|
/>}
|
|
28
27
|
</Badge>
|
|
29
28
|
)
|
|
@@ -15,6 +15,7 @@ export const SelectContent = () => {
|
|
|
15
15
|
|
|
16
16
|
const itemConfigs = useMemo<(MenuAction & { key: keyof typeof features, section: 0 | 1 })[]>(() => ([
|
|
17
17
|
{
|
|
18
|
+
role: 'button',
|
|
18
19
|
key: 'agent',
|
|
19
20
|
section: 0,
|
|
20
21
|
label: t.chatAgent,
|
|
@@ -25,6 +26,7 @@ export const SelectContent = () => {
|
|
|
25
26
|
},
|
|
26
27
|
},
|
|
27
28
|
{
|
|
29
|
+
role: 'button',
|
|
28
30
|
key: 'quickCommands',
|
|
29
31
|
section: 0,
|
|
30
32
|
label: 'Quick Commands',
|
|
@@ -35,6 +37,7 @@ export const SelectContent = () => {
|
|
|
35
37
|
},
|
|
36
38
|
},
|
|
37
39
|
{
|
|
40
|
+
role: 'button',
|
|
38
41
|
key: 'knowledgeSource',
|
|
39
42
|
section: 0,
|
|
40
43
|
label: 'Knowledge Sources',
|
|
@@ -42,6 +45,7 @@ export const SelectContent = () => {
|
|
|
42
45
|
onClick: () => widget.set('panel', 'ks'),
|
|
43
46
|
},
|
|
44
47
|
{
|
|
48
|
+
role: 'button',
|
|
45
49
|
key: 'stack',
|
|
46
50
|
section: 0,
|
|
47
51
|
label: 'Stacks AI',
|
|
@@ -49,6 +53,7 @@ export const SelectContent = () => {
|
|
|
49
53
|
onClick: () => widget.set('panel', 'stack'),
|
|
50
54
|
},
|
|
51
55
|
{
|
|
56
|
+
role: 'button',
|
|
52
57
|
key: 'workspace',
|
|
53
58
|
section: 0,
|
|
54
59
|
label: 'Spots',
|
|
@@ -56,6 +61,7 @@ export const SelectContent = () => {
|
|
|
56
61
|
onClick: () => widget.set('panel', 'workspace'),
|
|
57
62
|
},
|
|
58
63
|
{
|
|
64
|
+
role: 'button',
|
|
59
65
|
key: 'upload',
|
|
60
66
|
section: 1,
|
|
61
67
|
label: t.upload,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
2
|
import { interpolate } from '@stack-spot/portal-translate'
|
|
3
|
-
import { useMemo } from 'react'
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
import { FadingOverflow } from '../../components/FadingOverflow'
|
|
5
5
|
import { FileDescription } from '../../components/FileDescription'
|
|
6
6
|
import { useCurrentChat } from '../../context/hooks'
|
|
@@ -22,6 +22,7 @@ const UploadItem = ({ upload }: UploadedFileProps) => {
|
|
|
22
22
|
const uploadManager = useUploadManager()
|
|
23
23
|
const icon = upload.file.type.toLowerCase().startsWith('image/') ? createImageFromFile(upload.file) : undefined
|
|
24
24
|
const status = useUploadStatus(upload)
|
|
25
|
+
|
|
25
26
|
return <FileDescription
|
|
26
27
|
fileName={upload.file.name}
|
|
27
28
|
icon={icon}
|
|
@@ -37,6 +38,29 @@ export const UploadBar = () => {
|
|
|
37
38
|
const t = useMessageInputDictionary()
|
|
38
39
|
const chat = useCurrentChat()
|
|
39
40
|
const visible = !!uploads.length
|
|
41
|
+
const [ariaMessage, setAriaMessage] = useState('')
|
|
42
|
+
const prevUploadsRef = useRef<FileUpload[]>([])
|
|
43
|
+
const announcedUploadsRef = useRef(new Set())
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const prevUploads = prevUploadsRef.current
|
|
47
|
+
const newUploads = uploads.filter(
|
|
48
|
+
up => !prevUploads.some(prev => prev.id === up.id),
|
|
49
|
+
)
|
|
50
|
+
newUploads.forEach((up) => {
|
|
51
|
+
if (!announcedUploadsRef.current.has(up.id)) {
|
|
52
|
+
setAriaMessage('')
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
setAriaMessage(`${t.uploadSuccessStatus}: ${up.file.name}`)
|
|
55
|
+
setTimeout(() => setAriaMessage(''), 2500)
|
|
56
|
+
}, 100)
|
|
57
|
+
announcedUploadsRef.current.add(up.id)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
if (uploads.some(up => up.status === 'error')) {
|
|
61
|
+
setTimeout(() => setAriaMessage(''), 2000)
|
|
62
|
+
}
|
|
63
|
+
}, [uploads])
|
|
40
64
|
|
|
41
65
|
useUploadErrorEffect((errors) => {
|
|
42
66
|
const sizeErrors = errors.filter(e => e instanceof FileIsTooLarge)
|
|
@@ -53,11 +77,25 @@ export const UploadBar = () => {
|
|
|
53
77
|
lines.push(`${interpolate(t.uploadItemLimitError, maxItems)}\n- ${maxItemsErrorsNames.join('\n- ')}`)
|
|
54
78
|
}
|
|
55
79
|
if (!lines.length) return
|
|
80
|
+
setAriaMessage(lines.join(' '))
|
|
81
|
+
setTimeout(() => setAriaMessage(''), 2000)
|
|
56
82
|
chat.pushMessage(new ChatEntry({ agentType: 'system', type: 'md', content: lines.join('\n\n') }))
|
|
57
|
-
})
|
|
58
83
|
|
|
84
|
+
if (lines.length) {
|
|
85
|
+
setAriaMessage(lines.join(' '))
|
|
86
|
+
setTimeout(() => setAriaMessage(''), 2000)
|
|
87
|
+
chat.pushMessage(new ChatEntry({ agentType: 'system', type: 'md', content: lines.join('\n\n') }))
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
59
91
|
return (
|
|
60
92
|
<div className={listToClass(['info-bar', 'upload-bar', visible && 'visible'])}>
|
|
93
|
+
<div className="aria-live"
|
|
94
|
+
aria-live="polite"
|
|
95
|
+
aria-atomic="true"
|
|
96
|
+
>
|
|
97
|
+
{ariaMessage}
|
|
98
|
+
</div>
|
|
61
99
|
<div className="space"></div>
|
|
62
100
|
<div className="content">
|
|
63
101
|
<FadingOverflow className="list-overflow" scroll="arrows" enableHorizontalScrollWithVerticalWheel>
|
|
@@ -33,6 +33,8 @@ const dictionary = {
|
|
|
33
33
|
cantSendBecauseOfEmptyContent: 'You can\'t send empty messages. Please write some text or upload a file.',
|
|
34
34
|
cantSendBecausePromptMaxLength: 'You can\'t send messages longer than $0 characters. Please, shorten your message.',
|
|
35
35
|
chatAgent: 'Agents',
|
|
36
|
+
uploadSuccessStatus: 'File sent successfully',
|
|
37
|
+
chatViewMenu: 'Chat options menu',
|
|
36
38
|
},
|
|
37
39
|
pt: {
|
|
38
40
|
stack: 'Selecionar stack',
|
|
@@ -66,6 +68,8 @@ const dictionary = {
|
|
|
66
68
|
cantSendBecauseOfEmptyContent: 'Não é possível enviar mensagens vazias. Por favor, escreva algum texto ou envie um arquivo.',
|
|
67
69
|
cantSendBecausePromptMaxLength: 'Você não pode enviar mensagens com mais de $0 caracteres. Por favor, reduza sua mensagem.',
|
|
68
70
|
chatAgent: 'Agentes',
|
|
71
|
+
uploadSuccessStatus: 'Arquivo anexado com sucesso',
|
|
72
|
+
chatViewMenu: 'Menu de opções do chat',
|
|
69
73
|
},
|
|
70
74
|
} satisfies Dictionary
|
|
71
75
|
|
|
@@ -64,6 +64,18 @@ export const MessageInputBox = styled.div`
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
> .aria-live {
|
|
68
|
+
position: absolute;
|
|
69
|
+
width: 1px;
|
|
70
|
+
height: 1px;
|
|
71
|
+
margin: -1px;
|
|
72
|
+
padding: 0;
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
clip: rect(0 0 0 0);
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
border: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
> .space {
|
|
68
80
|
height: 0;
|
|
69
81
|
transition: height 0.4s;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Accordion, Icon, ImageBox, ImageWithFallback, Row, Text } from '@stack-spot/citric-react'
|
|
2
|
+
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
3
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
4
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
5
|
+
import { useEffect, useMemo } from 'react'
|
|
6
|
+
import { styled } from 'styled-components'
|
|
7
|
+
import { useWidget, useWidgetState } from '../context/hooks'
|
|
8
|
+
import { useRightPanel } from '../right-panel/hooks'
|
|
9
|
+
import { toolById } from '../utils/tools'
|
|
10
|
+
import { AgentDescription } from './Agents/AgentDescription'
|
|
11
|
+
|
|
12
|
+
export const Resources = () => {
|
|
13
|
+
const t = useTranslate(dictionary)
|
|
14
|
+
const panel = useWidgetState('panel')
|
|
15
|
+
const message = useWidgetState('currentMessageInPanel')
|
|
16
|
+
const { open } = useRightPanel()
|
|
17
|
+
const widget = useWidget()
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (panel === 'resources' && message) open(
|
|
21
|
+
<ResourcesPanel key={message.messageId} />,
|
|
22
|
+
{ title: t.title, description: t.description, onClose: () => widget.set('panel', undefined) },
|
|
23
|
+
)
|
|
24
|
+
}, [panel, t])
|
|
25
|
+
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const StyledAccordion = styled(Accordion)`
|
|
30
|
+
&[data-citric="accordion"] {
|
|
31
|
+
background-color: ${theme.color.light[400]};
|
|
32
|
+
}
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
const ResourcesPanel = () => {
|
|
36
|
+
const { chatId, messageId } = useWidgetState('currentMessageInPanel') ?? {}
|
|
37
|
+
const widget = useWidget()
|
|
38
|
+
const message = useMemo(() => {
|
|
39
|
+
const chat = widget.chatTabs.getAll().find(c => c.id === chatId)
|
|
40
|
+
return chat?.getMessages().find(m => m.id === messageId)?.getValue()
|
|
41
|
+
}, [messageId])
|
|
42
|
+
|
|
43
|
+
const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!message?.agent?.id })
|
|
44
|
+
const [agent] = agentToolsClient.agent.useStatefulQuery({ agentId: message?.agent?.id || '' },
|
|
45
|
+
{ enabled: !!message?.agent?.id })
|
|
46
|
+
const tools = useMemo(() => message?.tools?.map(id => toolById(id, toolKits)), [messageId, toolKits])
|
|
47
|
+
const customTools = useMemo(() => message?.tools?.map(id => toolById(id, agent?.toolkits?.custom_toolkits)),
|
|
48
|
+
[messageId, agent?.toolkits?.custom_toolkits])
|
|
49
|
+
|
|
50
|
+
const [agentsTools] = agentToolsClient.agentsByIds.useStatefulQuery({ searchAgentsRequest:{ ids: message?.tools || [] } })
|
|
51
|
+
const hasAgentTool = useMemo(() => message?.tools?.some(id => agentsTools?.find((agent) => agent.id === id)), [messageId, toolKits])
|
|
52
|
+
|
|
53
|
+
const header = (image?: string, label?: string) => (
|
|
54
|
+
<Row gap="10px">
|
|
55
|
+
<ImageBox>
|
|
56
|
+
<ImageWithFallback src={image} fallback={<Icon icon="Agent" />} aria-label={label} title={label} />
|
|
57
|
+
</ImageBox>
|
|
58
|
+
<Text>{label}</Text>
|
|
59
|
+
</Row>
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return !!(tools?.length || customTools?.length) && (
|
|
63
|
+
<>
|
|
64
|
+
<>
|
|
65
|
+
{[...(tools || []), ...(customTools || [])].map(
|
|
66
|
+
(tool) =>
|
|
67
|
+
tool && (
|
|
68
|
+
<StyledAccordion key={tool.id} header={header(tool?.image, tool?.name)} appearance="card">
|
|
69
|
+
{tool?.description}
|
|
70
|
+
</StyledAccordion>
|
|
71
|
+
))}
|
|
72
|
+
</>
|
|
73
|
+
{
|
|
74
|
+
hasAgentTool &&
|
|
75
|
+
<>
|
|
76
|
+
{message?.tools?.map((id) => {
|
|
77
|
+
const agentTool = agentsTools?.find((agent) => agent.id === id)
|
|
78
|
+
return (
|
|
79
|
+
<StyledAccordion key={id} header={header(agentTool?.avatar || undefined, agentTool?.name)}>
|
|
80
|
+
<AgentDescription agentId={id} />
|
|
81
|
+
</StyledAccordion>
|
|
82
|
+
)},
|
|
83
|
+
)}
|
|
84
|
+
</>
|
|
85
|
+
}
|
|
86
|
+
</>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const dictionary = {
|
|
91
|
+
en: {
|
|
92
|
+
title: 'Resources',
|
|
93
|
+
description: 'These are the resources used to generate this answer.',
|
|
94
|
+
},
|
|
95
|
+
pt: {
|
|
96
|
+
title: 'Recursos',
|
|
97
|
+
description: 'Esses são os recursos usados pra gerar essa resposta.',
|
|
98
|
+
},
|
|
99
|
+
} satisfies Dictionary
|
package/src/views/Tools.tsx
CHANGED
|
@@ -42,19 +42,25 @@ const ToolsPanel = () => {
|
|
|
42
42
|
}, [messageId])
|
|
43
43
|
|
|
44
44
|
const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!message?.agent?.id })
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const [agent] = agentToolsClient.agent.useStatefulQuery({ agentId: message?.agent?.id || '' },
|
|
46
|
+
{ enabled: !!message?.agent?.id })
|
|
47
|
+
const tools = useMemo(() => message?.tools?.map(id => toolById(id, toolKits)), [messageId, toolKits])
|
|
48
|
+
const customTools = useMemo(() => message?.tools?.map(id => toolById(id, agent?.toolkits?.custom_toolkits)),
|
|
49
|
+
[messageId, agent?.toolkits?.custom_toolkits])
|
|
50
|
+
return !!(tools?.length || customTools?.length) && (
|
|
48
51
|
<ToolList>
|
|
49
|
-
{tools
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
{[...(tools || []), ...(customTools || [])].map(
|
|
53
|
+
(tool) =>
|
|
54
|
+
tool && (
|
|
55
|
+
<li key={tool.id}>
|
|
56
|
+
<ToolBadge
|
|
57
|
+
name={tool.name || tool.id}
|
|
58
|
+
image={tool.image ?? ''}
|
|
59
|
+
description={tool.description ?? ''}
|
|
60
|
+
/>
|
|
61
|
+
</li>
|
|
62
|
+
),
|
|
63
|
+
)}
|
|
58
64
|
</ToolList>
|
|
59
65
|
)
|
|
60
66
|
}
|