@stack-spot/ai-chat-widget 1.36.3-beta.1 → 1.36.3
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 +32 -35
- 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 +4 -4
- package/dist/components/FileDescription.d.ts +0 -1
- package/dist/components/FileDescription.d.ts.map +1 -1
- package/dist/components/FileDescription.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/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/state/WidgetState.d.ts +1 -1
- package/dist/state/WidgetState.d.ts.map +1 -1
- package/dist/utils/tools.d.ts +2 -1
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +12 -1
- package/dist/utils/tools.js.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.js +26 -14
- 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 +17 -9
- 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/SelectContent.d.ts.map +1 -1
- package/dist/views/MessageInput/SelectContent.js +1 -1
- 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 +2 -3
- 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 +2 -4
- package/dist/views/MessageInput/dictionary.js.map +1 -1
- package/dist/views/MessageInput/styled.js +8 -8
- package/dist/views/Resources.d.ts +2 -0
- package/dist/views/Resources.d.ts.map +1 -0
- package/dist/views/Resources.js +56 -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 +3 -3
- package/src/StackspotAIWidget.tsx +2 -0
- package/src/app-metadata.json +4 -4
- package/src/components/FileDescription.tsx +0 -1
- package/src/components/HistoryList.tsx +3 -0
- package/src/hooks/midnight-update-view.ts +36 -0
- package/src/state/WidgetState.ts +1 -1
- package/src/utils/tools.ts +23 -2
- package/src/views/Agents/AgentDescription.tsx +64 -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 +33 -19
- package/src/views/Chat/styled.ts +24 -0
- package/src/views/MessageInput/SelectContent.tsx +3 -4
- package/src/views/MessageInput/UploadBar.tsx +2 -5
- package/src/views/MessageInput/dictionary.ts +2 -4
- package/src/views/MessageInput/styled.ts +8 -8
- package/src/views/Resources.tsx +99 -0
- package/src/views/Tools.tsx +19 -13
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useManualRender } from '@stack-spot/portal-components'
|
|
2
|
+
import { useEffect } from 'react'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the number of milliseconds remaining until the next midnight.
|
|
6
|
+
*/
|
|
7
|
+
function getTimeUntilMidnight() {
|
|
8
|
+
const now = new Date()
|
|
9
|
+
const midnight = new Date(now)
|
|
10
|
+
midnight.setHours(24, 0, 0, 0)
|
|
11
|
+
return midnight.getTime() - now.getTime()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Hook that triggers a component re-render at midnight every day.
|
|
16
|
+
*/
|
|
17
|
+
export function useMidnightUpdateView() {
|
|
18
|
+
const { repaint } = useManualRender()
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
22
|
+
|
|
23
|
+
const scheduleUpdate = () => {
|
|
24
|
+
const timeUntilMidnight = getTimeUntilMidnight()
|
|
25
|
+
|
|
26
|
+
timer = setTimeout(() => {
|
|
27
|
+
repaint()
|
|
28
|
+
scheduleUpdate()
|
|
29
|
+
}, timeUntilMidnight)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
scheduleUpdate()
|
|
33
|
+
|
|
34
|
+
return () => { timer && clearTimeout(timer) }
|
|
35
|
+
}, [])
|
|
36
|
+
}
|
package/src/state/WidgetState.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface WidgetProperties {
|
|
|
13
13
|
/**
|
|
14
14
|
* Current content of the right panel. Undefined for closed right panel.
|
|
15
15
|
*/
|
|
16
|
-
panel?: 'stack' | 'workspace' | 'agent' | 'ks' | 'editor' | 'history' | 'ks-details' | 'steps' | 'tools',
|
|
16
|
+
panel?: 'stack' | 'workspace' | 'agent' | 'ks' | 'editor' | 'history' | 'ks-details' | 'steps' | 'tools' | 'resources',
|
|
17
17
|
/**
|
|
18
18
|
* KS to use when the right panel "ks-details" is open.
|
|
19
19
|
*/
|
package/src/utils/tools.ts
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import { BuiltinToolkitResponse, BuiltinToolResponse } from '@stack-spot/portal-network/api/agent'
|
|
2
|
+
import { CustomToolkitDto } from '@stack-spot/portal-network/api/agent-tools'
|
|
2
3
|
import { keyBy } from 'lodash'
|
|
3
4
|
|
|
4
5
|
export type ToolWithImage = BuiltinToolResponse & { id: string, image?: string }
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
function isBuiltinToolkit(
|
|
8
|
+
toolkit: BuiltinToolkitResponse | CustomToolkitDto,
|
|
9
|
+
): toolkit is BuiltinToolkitResponse {
|
|
10
|
+
return 'image_url' in toolkit
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export function toolById(
|
|
15
|
+
id: string,
|
|
16
|
+
toolkits: (BuiltinToolkitResponse | CustomToolkitDto)[] | undefined,
|
|
17
|
+
): ToolWithImage | undefined {
|
|
18
|
+
const tools = toolkits
|
|
19
|
+
?.map((toolkit) =>
|
|
20
|
+
toolkit.tools?.map((tool) => ({
|
|
21
|
+
...tool,
|
|
22
|
+
id: tool.id!,
|
|
23
|
+
image: isBuiltinToolkit(toolkit)
|
|
24
|
+
? toolkit.image_url ?? undefined
|
|
25
|
+
: (toolkit as CustomToolkitDto).avatar ?? undefined,
|
|
26
|
+
})),
|
|
27
|
+
)
|
|
28
|
+
.flat()
|
|
8
29
|
return keyBy(tools, 'id')[id]
|
|
9
30
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Text } from '@citric/core'
|
|
2
|
-
import {
|
|
1
|
+
import { Box, Flex, IconBox, Text } from '@citric/core'
|
|
2
|
+
import { Agent, Cog } from '@citric/icons'
|
|
3
|
+
import { Avatar, Badge, Skeleton } from '@citric/ui'
|
|
3
4
|
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
4
5
|
import { useMemo } from 'react'
|
|
5
|
-
import { ToolBadge } from '../../components/ToolBadge'
|
|
6
6
|
import { toolById } from '../../utils/tools'
|
|
7
7
|
import { useAgentsDictionary } from './dictionary'
|
|
8
8
|
import { AgentDescriptionBox } from './styled'
|
|
@@ -15,43 +15,80 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
15
15
|
|
|
16
16
|
const knowledgeSources = useMemo(
|
|
17
17
|
() => agent?.knowledge_sources_config?.knowledge_sources_details?.map((ks, index) => (
|
|
18
|
-
<li key={index}
|
|
18
|
+
<li key={index}>
|
|
19
|
+
<Box className="card-agent-info">
|
|
20
|
+
<Flex alignItems="center" justifyContent="space-between" pl={2}>
|
|
21
|
+
<Text colorScheme="light.contrastText">{ks.name}</Text>
|
|
22
|
+
<Badge palette="gray" appearance="square">{ks.type}</Badge>
|
|
23
|
+
</Flex>
|
|
24
|
+
</Box>
|
|
25
|
+
</li>
|
|
19
26
|
)),
|
|
20
27
|
[agent],
|
|
21
28
|
)
|
|
22
29
|
const skeleton = useMemo(() => {
|
|
23
30
|
const loadingKS: React.ReactElement[] = []
|
|
24
31
|
for (let i = 0; i < numberOfKnowledgeSources; i++) {
|
|
25
|
-
loadingKS.push(<li key={i}><
|
|
32
|
+
loadingKS.push(<li key={i}><Skeleton className="ks-skeleton" /></li>)
|
|
26
33
|
}
|
|
27
34
|
return loadingKS
|
|
28
35
|
}, [numberOfKnowledgeSources])
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
36
|
+
|
|
37
|
+
const { tools, multiAgents } = useMemo(() => {
|
|
38
|
+
const tools: React.ReactElement[] = []
|
|
39
|
+
const multiAgents: React.ReactElement[] = []
|
|
40
|
+
const builtInTools = agent?.toolkits?.builtin_toolkits ?? []
|
|
32
41
|
const customToolkits = agent?.toolkits?.custom_toolkits ?? []
|
|
33
|
-
for (const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
for (const toolkit of builtInTools) {
|
|
43
|
+
for (const tool of toolkit.tools ?? []) {
|
|
44
|
+
if (toolkit.id == 'UTILITIES'){
|
|
45
|
+
const toolWithImage = toolById(tool.id, toolKits)
|
|
46
|
+
tools.push(
|
|
47
|
+
<li key={tool.id}>
|
|
48
|
+
<Box className="card-agent-info">
|
|
49
|
+
<Flex alignItems="center">
|
|
50
|
+
{toolWithImage?.image ?
|
|
51
|
+
<Avatar size="xxs" sx={{ mr: 3 }}> <img src={toolWithImage?.image} /></Avatar> :
|
|
52
|
+
<IconBox colorIcon="light" sx={{ mr: 3 }}><Cog /></IconBox>}
|
|
53
|
+
<Text colorScheme="light.contrastText">{toolWithImage?.name || toolWithImage?.id || 'unknown'}</Text>
|
|
54
|
+
</Flex>
|
|
55
|
+
</Box>
|
|
56
|
+
</li>,
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
if (toolkit.id == 'MULTI_AGENTS'){
|
|
60
|
+
multiAgents.push(
|
|
61
|
+
<li key={tool.id}>
|
|
62
|
+
<Box className="card-agent-info">
|
|
63
|
+
<Flex alignItems="center">
|
|
64
|
+
<IconBox colorIcon="light" sx={{ mr: 3 }}><Agent /></IconBox>
|
|
65
|
+
<Text colorScheme="light.contrastText">
|
|
66
|
+
{tool.name}
|
|
67
|
+
</Text>
|
|
68
|
+
</Flex>
|
|
69
|
+
</Box>
|
|
70
|
+
</li>,
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
44
74
|
}
|
|
45
75
|
for (const toolkit of customToolkits) {
|
|
46
76
|
for (const tool of toolkit.tools) {
|
|
47
|
-
|
|
77
|
+
tools.push(
|
|
48
78
|
<li key={tool.id}>
|
|
49
|
-
<
|
|
79
|
+
<Box className="card-agent-info">
|
|
80
|
+
<Flex alignItems="center">
|
|
81
|
+
{toolkit.avatar ?
|
|
82
|
+
<Avatar size="xxs" sx={{ mr: 3 }}><img src={toolkit.avatar} /></Avatar> :
|
|
83
|
+
<IconBox colorIcon="light" sx={{ mr: 3 }}><Cog /></IconBox>}
|
|
84
|
+
<Text colorScheme="light.contrastText">{tool.name}</Text>
|
|
85
|
+
</Flex>
|
|
86
|
+
</Box>
|
|
50
87
|
</li>,
|
|
51
88
|
)
|
|
52
89
|
}
|
|
53
90
|
}
|
|
54
|
-
return
|
|
91
|
+
return { tools, multiAgents }
|
|
55
92
|
}, [agent])
|
|
56
93
|
|
|
57
94
|
return (
|
|
@@ -64,10 +101,14 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
64
101
|
<Text appearance="microtext1" className="title">Knowledge sources</Text>
|
|
65
102
|
<ul>{isLoading || isLoadingToolKit ? skeleton : knowledgeSources}</ul>
|
|
66
103
|
</section>}
|
|
67
|
-
{!!tools
|
|
104
|
+
{!!tools?.length && <section>
|
|
68
105
|
<Text appearance="microtext1" className="title">{t.tools}</Text>
|
|
69
106
|
<ul>{tools}</ul>
|
|
70
107
|
</section>}
|
|
108
|
+
{!!multiAgents?.length && <section>
|
|
109
|
+
<Text appearance="microtext1" className="title">{t.multiAgent}</Text>
|
|
110
|
+
<ul>{multiAgents}</ul>
|
|
111
|
+
</section>}
|
|
71
112
|
{agent?.model_name && <section>
|
|
72
113
|
<Text appearance="microtext1" className="title">LLM</Text>
|
|
73
114
|
<Badge palette="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,6 +1,6 @@
|
|
|
1
1
|
import { Box, Button, Checkbox, Flex, IconBox, Input, Label, Radio, Text } from '@citric/core'
|
|
2
|
-
import { Check, Cog, Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
|
|
3
|
-
import { Badge, IconButton, Tooltip } from '@citric/ui'
|
|
2
|
+
import { Agent, Check, Cog, Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
|
|
3
|
+
import { Avatar, AvatarGroup, Badge, IconButton, Tooltip } from '@citric/ui'
|
|
4
4
|
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
5
5
|
import { listToClass } from '@stack-spot/portal-theme'
|
|
6
6
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
@@ -10,8 +10,8 @@ import { PhoneInput } from 'react-international-phone'
|
|
|
10
10
|
import 'react-international-phone/style.css'
|
|
11
11
|
import { FileDescription } from '../../components/FileDescription'
|
|
12
12
|
import { Markdown } from '../../components/Markdown'
|
|
13
|
-
import { StackedBadge } from '../../components/StackedBadge'
|
|
14
13
|
import { useChatEntry, useCurrentChat, useWidget } from '../../context/hooks'
|
|
14
|
+
import { useMidnightUpdateView } from '../../hooks/midnight-update-view'
|
|
15
15
|
import { ChatEntry, SerializableAction, TextChatEntry } from '../../state/ChatEntry'
|
|
16
16
|
import { useDateFormatter } from '../../utils/date'
|
|
17
17
|
import { toolById } from '../../utils/tools'
|
|
@@ -194,10 +194,13 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
|
|
|
194
194
|
const chat = useCurrentChat()
|
|
195
195
|
const agentId = entry.agent?.id ?? ''
|
|
196
196
|
const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!agentId })
|
|
197
|
+
const [agentsTools] = agentToolsClient.agentsByIds.useStatefulQuery(
|
|
198
|
+
{ searchAgentsRequest: { ids: entry.tools || [''] } }, { enabled: !!entry.tools })
|
|
197
199
|
const [copied, setCopied] = useState(false)
|
|
198
200
|
const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
|
|
199
201
|
|
|
200
202
|
useChatScrollToBottomEffect(ref, [entry])
|
|
203
|
+
useMidnightUpdateView()
|
|
201
204
|
|
|
202
205
|
const detailKS = useCallback(({ name, slug, documentScore, documentId }: Required<TextChatEntry>['knowledgeSources'][number]) => {
|
|
203
206
|
widget.set('currentKSInPanel', { name, slug, score: documentScore, documentId })
|
|
@@ -307,12 +310,11 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
|
|
|
307
310
|
</form>
|
|
308
311
|
}
|
|
309
312
|
|
|
310
|
-
function
|
|
313
|
+
function openResourcesPanel() {
|
|
311
314
|
widget.set('currentMessageInPanel', { chatId: chat.id, messageId: message.id })
|
|
312
|
-
widget.set('panel', '
|
|
315
|
+
widget.set('panel', 'resources')
|
|
313
316
|
}
|
|
314
317
|
|
|
315
|
-
|
|
316
318
|
return (entry.content || entry.error || !!entry.steps?.length || entry.upload?.length) && (
|
|
317
319
|
<li key={entry.messageId} className={entry.agentType} ref={ref}>
|
|
318
320
|
<div className="chat-message-container"
|
|
@@ -340,19 +342,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
|
|
|
340
342
|
)}
|
|
341
343
|
</div>
|
|
342
344
|
{afterMessage && createElement(afterMessage, { message })}
|
|
343
|
-
|
|
344
|
-
aria-label={t.openToolsPanel}
|
|
345
|
-
title={t.openToolsPanel}
|
|
346
|
-
tabIndex={0}
|
|
347
|
-
role="button"
|
|
348
|
-
className="tools-badge"
|
|
349
|
-
label={t.tools}
|
|
350
|
-
images={entry.tools.slice(0, 3).map((id) => {
|
|
351
|
-
const tool = toolById(id, toolKits)
|
|
352
|
-
return { key: id, name: tool?.name || id, icon: <Cog />, url: tool?.image }
|
|
353
|
-
})}
|
|
354
|
-
onClick={openToolsPanel}
|
|
355
|
-
/>}
|
|
345
|
+
|
|
356
346
|
{!!entry.knowledgeSources?.length && <div className="ks-box">
|
|
357
347
|
<Text appearance="microtext1" colorScheme="light.700">Knowledge Sources:</Text>
|
|
358
348
|
<ul>{entry.knowledgeSources.map((ks, index) => (
|
|
@@ -361,6 +351,28 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
|
|
|
361
351
|
</li>
|
|
362
352
|
))}</ul>
|
|
363
353
|
</div>}
|
|
354
|
+
|
|
355
|
+
{(!!agentsTools?.length || !!entry.tools?.length) && <div className="tools-box">
|
|
356
|
+
<AvatarGroup onClick={openResourcesPanel} aria-label={t.openResourcesPanel} role="button">
|
|
357
|
+
{agentsTools?.map((agent) => (
|
|
358
|
+
<Avatar size="xxs" key={agent.id} className="agent-info-avatar-resource">
|
|
359
|
+
{agent.avatar ?
|
|
360
|
+
<img alt={agent.name} aria-label={agent.name} title={agent.name} src={agent.avatar} /> :
|
|
361
|
+
<IconBox appearance="circle" aria-label={agent.name} title={agent.name} color="gray"><Agent /></IconBox>}
|
|
362
|
+
</Avatar>
|
|
363
|
+
))}
|
|
364
|
+
{entry.tools?.map((id) => {
|
|
365
|
+
const tool = toolById(id, toolKits)
|
|
366
|
+
return (
|
|
367
|
+
<Avatar size="xxs" key={id} className="agent-info-avatar-resource">
|
|
368
|
+
{tool?.image ?
|
|
369
|
+
<img alt={tool.name} aria-label={tool.name} title={tool.name} src={tool.image} /> :
|
|
370
|
+
<IconBox appearance="circle" aria-label={tool?.name} title={tool?.name} color="gray"><Cog /></IconBox>}
|
|
371
|
+
</Avatar>
|
|
372
|
+
)})}
|
|
373
|
+
|
|
374
|
+
</AvatarGroup>
|
|
375
|
+
</div>}
|
|
364
376
|
|
|
365
377
|
{shouldShowFooter && <div className="message-footer">
|
|
366
378
|
{entry.agentType === 'bot' && !entry.error && <div className="message-actions">
|
|
@@ -450,6 +462,7 @@ const dictionary = {
|
|
|
450
462
|
dislike: 'Dislike',
|
|
451
463
|
tools: 'Tools',
|
|
452
464
|
openToolsPanel: 'Open the tools panel to see more details.',
|
|
465
|
+
openResourcesPanel: 'Open the resources panel to see more details.',
|
|
453
466
|
copied: 'Copied',
|
|
454
467
|
},
|
|
455
468
|
pt: {
|
|
@@ -458,6 +471,7 @@ const dictionary = {
|
|
|
458
471
|
dislike: 'Não gostei',
|
|
459
472
|
tools: 'Ferramentas',
|
|
460
473
|
openToolsPanel: 'Abrir o painel de ferramentas para ver mais detalhes.',
|
|
474
|
+
openResourcesPanel: 'Abrir o painel de recursos para ver mais detalhes.',
|
|
461
475
|
copied: 'Copiado',
|
|
462
476
|
},
|
|
463
477
|
} satisfies Dictionary
|
package/src/views/Chat/styled.ts
CHANGED
|
@@ -219,6 +219,30 @@ export const ChatList: IStyledComponentBase<
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
.tools-box {
|
|
223
|
+
|
|
224
|
+
> ul {
|
|
225
|
+
display: flex;
|
|
226
|
+
flex-direction: row;
|
|
227
|
+
flex-wrap: wrap;
|
|
228
|
+
white-space: nowrap;
|
|
229
|
+
margin: 0;
|
|
230
|
+
margin-top: 8px;
|
|
231
|
+
padding: 0;
|
|
232
|
+
list-style: none;
|
|
233
|
+
gap: 6px;
|
|
234
|
+
|
|
235
|
+
&:hover{
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
|
|
238
|
+
.agent-info-avatar-resource {
|
|
239
|
+
transition: margin-left 0.3s ease-in;
|
|
240
|
+
margin-left: 0px;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
222
246
|
.steps {
|
|
223
247
|
ul {
|
|
224
248
|
list-style: none;
|
|
@@ -91,10 +91,9 @@ export const SelectContent = () => {
|
|
|
91
91
|
id="button-content-select"
|
|
92
92
|
color="light"
|
|
93
93
|
appearance="square"
|
|
94
|
-
|
|
95
|
-
title={visibleMenu ? t.collapse && t.chatViewMenu : t.expand}
|
|
94
|
+
title={t.chatViewMenu}
|
|
96
95
|
data-test-hint="button-options"
|
|
97
|
-
aria-label={
|
|
96
|
+
aria-label={t.chatViewMenu}
|
|
98
97
|
aria-controls="chatMessageMenu"
|
|
99
98
|
aria-expanded={visibleMenu}
|
|
100
99
|
onClick={() => setVisibleMenu(state => !state)}>
|
|
@@ -106,7 +105,7 @@ export const SelectContent = () => {
|
|
|
106
105
|
visible={visibleMenu}
|
|
107
106
|
onHide={() => setVisibleMenu(false)}
|
|
108
107
|
items={listItems}
|
|
109
|
-
aria-label=
|
|
108
|
+
aria-label={t.chatViewMenu}
|
|
110
109
|
/>
|
|
111
110
|
</>
|
|
112
111
|
) : (
|
|
@@ -22,7 +22,6 @@ 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
|
-
const errorMessage = status === 'error' ? (upload.error?.message || 'Falha ao anexar o arquivo') : undefined
|
|
26
25
|
|
|
27
26
|
return <FileDescription
|
|
28
27
|
fileName={upload.file.name}
|
|
@@ -30,7 +29,6 @@ const UploadItem = ({ upload }: UploadedFileProps) => {
|
|
|
30
29
|
onRemove={() => uploadManager.remove(upload)}
|
|
31
30
|
onRetry={() => upload.retry()}
|
|
32
31
|
status={status}
|
|
33
|
-
errorMessage={errorMessage}
|
|
34
32
|
/>
|
|
35
33
|
}
|
|
36
34
|
|
|
@@ -62,7 +60,7 @@ export const UploadBar = () => {
|
|
|
62
60
|
if (uploads.some(up => up.status === 'error')) {
|
|
63
61
|
setTimeout(() => setAriaMessage(''), 2000)
|
|
64
62
|
}
|
|
65
|
-
}, [uploads
|
|
63
|
+
}, [uploads])
|
|
66
64
|
|
|
67
65
|
useUploadErrorEffect((errors) => {
|
|
68
66
|
const sizeErrors = errors.filter(e => e instanceof FileIsTooLarge)
|
|
@@ -89,8 +87,7 @@ export const UploadBar = () => {
|
|
|
89
87
|
chat.pushMessage(new ChatEntry({ agentType: 'system', type: 'md', content: lines.join('\n\n') }))
|
|
90
88
|
}
|
|
91
89
|
})
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
|
|
94
91
|
return (
|
|
95
92
|
<div className={listToClass(['info-bar', 'upload-bar', visible && 'visible'])}>
|
|
96
93
|
<div className="aria-live"
|
|
@@ -34,8 +34,7 @@ const dictionary = {
|
|
|
34
34
|
cantSendBecausePromptMaxLength: 'You can\'t send messages longer than $0 characters. Please, shorten your message.',
|
|
35
35
|
chatAgent: 'Agents',
|
|
36
36
|
uploadSuccessStatus: 'File sent successfully',
|
|
37
|
-
|
|
38
|
-
chatViewMenu: 'Viewing menu options',
|
|
37
|
+
chatViewMenu: 'Chat options menu',
|
|
39
38
|
},
|
|
40
39
|
pt: {
|
|
41
40
|
stack: 'Selecionar stack',
|
|
@@ -70,8 +69,7 @@ const dictionary = {
|
|
|
70
69
|
cantSendBecausePromptMaxLength: 'Você não pode enviar mensagens com mais de $0 caracteres. Por favor, reduza sua mensagem.',
|
|
71
70
|
chatAgent: 'Agentes',
|
|
72
71
|
uploadSuccessStatus: 'Arquivo anexado com sucesso',
|
|
73
|
-
|
|
74
|
-
chatViewMenu: 'Visualizando as opções de menu',
|
|
72
|
+
chatViewMenu: 'Menu de opções do chat',
|
|
75
73
|
},
|
|
76
74
|
} satisfies Dictionary
|
|
77
75
|
|
|
@@ -67,14 +67,14 @@ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
|
|
|
67
67
|
|
|
68
68
|
> .aria-live {
|
|
69
69
|
position: absolute;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
width: 1px;
|
|
71
|
+
height: 1px;
|
|
72
|
+
margin: -1px;
|
|
73
|
+
padding: 0;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
clip: rect(0 0 0 0);
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
border: 0;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
> .space {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Box, Flex, IconBox, Text } from '@citric/core'
|
|
2
|
+
import { Agent } from '@citric/icons'
|
|
3
|
+
import { Avatar } from '@citric/ui'
|
|
4
|
+
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
5
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
6
|
+
import { useEffect, useMemo } from 'react'
|
|
7
|
+
import { Accordion } from '../components/Accordion'
|
|
8
|
+
import { useWidget, useWidgetState } from '../context/hooks'
|
|
9
|
+
import { useRightPanel } from '../right-panel/hooks'
|
|
10
|
+
import { toolById } from '../utils/tools'
|
|
11
|
+
import { AgentDescription } from './Agents/AgentDescription'
|
|
12
|
+
|
|
13
|
+
export const Resources = () => {
|
|
14
|
+
const t = useTranslate(dictionary)
|
|
15
|
+
const panel = useWidgetState('panel')
|
|
16
|
+
const message = useWidgetState('currentMessageInPanel')
|
|
17
|
+
const { open } = useRightPanel()
|
|
18
|
+
const widget = useWidget()
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (panel === 'resources' && message) open(
|
|
22
|
+
<ResourcesPanel key={message.messageId} />,
|
|
23
|
+
{ title: t.title, description: t.description, onClose: () => widget.set('panel', undefined) },
|
|
24
|
+
)
|
|
25
|
+
}, [panel, t])
|
|
26
|
+
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ResourcesPanel = () => {
|
|
31
|
+
const { chatId, messageId } = useWidgetState('currentMessageInPanel') ?? {}
|
|
32
|
+
const widget = useWidget()
|
|
33
|
+
const message = useMemo(() => {
|
|
34
|
+
const chat = widget.chatTabs.getAll().find(c => c.id === chatId)
|
|
35
|
+
return chat?.getMessages().find(m => m.id === messageId)?.getValue()
|
|
36
|
+
}, [messageId])
|
|
37
|
+
|
|
38
|
+
const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!message?.agent?.id })
|
|
39
|
+
const [agent] = agentToolsClient.agent.useStatefulQuery({ agentId: message?.agent?.id || '' },
|
|
40
|
+
{ enabled: !!message?.agent?.id })
|
|
41
|
+
const tools = useMemo(() => message?.tools?.map(id => toolById(id, toolKits)), [messageId, toolKits])
|
|
42
|
+
const customTools = useMemo(() => message?.tools?.map(id => toolById(id, agent?.toolkits?.custom_toolkits)),
|
|
43
|
+
[messageId, agent?.toolkits?.custom_toolkits])
|
|
44
|
+
|
|
45
|
+
const [agentsTools] = agentToolsClient.agentsByIds.useStatefulQuery({ searchAgentsRequest:{ ids: message?.tools || [] } })
|
|
46
|
+
const hasAgentTool = useMemo(() => message?.tools?.some(id => agentsTools?.find((agent) => agent.id === id)), [messageId, toolKits])
|
|
47
|
+
|
|
48
|
+
const header = (image?: string, label?: string) => (
|
|
49
|
+
<Flex alignItems="center">
|
|
50
|
+
{image ? <Avatar size="xxs">
|
|
51
|
+
<img title={label} src={image} />
|
|
52
|
+
</Avatar> :
|
|
53
|
+
<IconBox appearance="circle" title={label} color="gray"><Agent /></IconBox>}
|
|
54
|
+
<Text ml={3}>{label}</Text>
|
|
55
|
+
</Flex>
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return !!(tools?.length || customTools?.length) && (
|
|
59
|
+
<>
|
|
60
|
+
<>
|
|
61
|
+
{[...(tools || []), ...(customTools || [])].map(
|
|
62
|
+
(tool) =>
|
|
63
|
+
tool && (
|
|
64
|
+
<Box key={tool.id} mt={3}>
|
|
65
|
+
<Accordion header={header(tool?.image, tool?.name)}>
|
|
66
|
+
<Box sx={{ backgroundColor: 'light.400', m: '-10px', p: '16px' }}>
|
|
67
|
+
{tool?.description}
|
|
68
|
+
</Box>
|
|
69
|
+
</Accordion>
|
|
70
|
+
</Box>))}
|
|
71
|
+
</>
|
|
72
|
+
{
|
|
73
|
+
hasAgentTool &&
|
|
74
|
+
<>
|
|
75
|
+
{message?.tools?.map((id) => {
|
|
76
|
+
const agentTool = agentsTools?.find((agent) => agent.id === id)
|
|
77
|
+
return (
|
|
78
|
+
<Box key={id} mt={3}>
|
|
79
|
+
<Accordion header={header(agentTool?.avatar || undefined, agentTool?.name)}>
|
|
80
|
+
<AgentDescription agentId={id} />
|
|
81
|
+
</Accordion>
|
|
82
|
+
</Box>)},
|
|
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,20 +42,26 @@ 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
|
-
|
|
58
|
-
|
|
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
|
+
backgroundColor="light.500"
|
|
61
|
+
/>
|
|
62
|
+
</li>
|
|
63
|
+
),
|
|
64
|
+
)}
|
|
59
65
|
</ToolList>
|
|
60
66
|
)
|
|
61
67
|
}
|