@stack-spot/ai-chat-widget 1.37.0-beta.1 → 1.38.1-beta.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +20 -11
  2. package/dist/StackspotAIWidget.d.ts.map +1 -1
  3. package/dist/StackspotAIWidget.js +2 -1
  4. package/dist/StackspotAIWidget.js.map +1 -1
  5. package/dist/app-metadata.json +4 -4
  6. package/dist/chat-interceptors/quick-commands.js +2 -2
  7. package/dist/chat-interceptors/quick-commands.js.map +1 -1
  8. package/dist/components/FileDescription.d.ts +0 -1
  9. package/dist/components/FileDescription.d.ts.map +1 -1
  10. package/dist/components/FileDescription.js.map +1 -1
  11. package/dist/state/WidgetState.d.ts +1 -1
  12. package/dist/state/WidgetState.d.ts.map +1 -1
  13. package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
  14. package/dist/views/Agents/AgentDescription.js +26 -14
  15. package/dist/views/Agents/AgentDescription.js.map +1 -1
  16. package/dist/views/Agents/dictionary.d.ts +1 -1
  17. package/dist/views/Agents/dictionary.d.ts.map +1 -1
  18. package/dist/views/Agents/dictionary.js +2 -0
  19. package/dist/views/Agents/dictionary.js.map +1 -1
  20. package/dist/views/Agents/styled.d.ts.map +1 -1
  21. package/dist/views/Agents/styled.js +14 -3
  22. package/dist/views/Agents/styled.js.map +1 -1
  23. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  24. package/dist/views/Chat/ChatMessage.js +19 -9
  25. package/dist/views/Chat/ChatMessage.js.map +1 -1
  26. package/dist/views/Chat/styled.d.ts.map +1 -1
  27. package/dist/views/Chat/styled.js +28 -0
  28. package/dist/views/Chat/styled.js.map +1 -1
  29. package/dist/views/MessageInput/styled.js +8 -8
  30. package/dist/views/Resources.d.ts +2 -0
  31. package/dist/views/Resources.d.ts.map +1 -0
  32. package/dist/views/Resources.js +56 -0
  33. package/dist/views/Resources.js.map +1 -0
  34. package/dist/views/Steps/dictionary.d.ts +1 -1
  35. package/package.json +3 -3
  36. package/src/StackspotAIWidget.tsx +2 -0
  37. package/src/app-metadata.json +4 -4
  38. package/src/chat-interceptors/quick-commands.ts +2 -2
  39. package/src/components/FileDescription.tsx +0 -1
  40. package/src/state/WidgetState.ts +1 -1
  41. package/src/views/Agents/AgentDescription.tsx +65 -23
  42. package/src/views/Agents/dictionary.ts +2 -0
  43. package/src/views/Agents/styled.ts +14 -3
  44. package/src/views/Chat/ChatMessage.tsx +40 -7
  45. package/src/views/Chat/styled.ts +28 -0
  46. package/src/views/MessageInput/styled.ts +8 -8
  47. package/src/views/Resources.tsx +98 -0
@@ -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,7 +10,6 @@ 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'
15
14
  import { ChatEntry, SerializableAction, TextChatEntry } from '../../state/ChatEntry'
16
15
  import { useDateFormatter } from '../../utils/date'
@@ -194,6 +193,8 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
194
193
  const chat = useCurrentChat()
195
194
  const agentId = entry.agent?.id ?? ''
196
195
  const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!agentId })
196
+ const [agentsTools] = agentToolsClient.agentsByIds.useStatefulQuery(
197
+ { searchAgentsRequest: { ids: entry.tools || [''] } }, { enabled: !!entry.tools })
197
198
  const [copied, setCopied] = useState(false)
198
199
  const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
199
200
 
@@ -307,12 +308,18 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
307
308
  </form>
308
309
  }
309
310
 
310
- function openToolsPanel() {
311
+ // function openToolsPanel() {
312
+ // widget.set('currentMessageInPanel', { chatId: chat.id, messageId: message.id })
313
+ // widget.set('panel', 'tools')
314
+ // }
315
+
316
+ function openResourcesPanel() {
311
317
  widget.set('currentMessageInPanel', { chatId: chat.id, messageId: message.id })
312
- widget.set('panel', 'tools')
318
+ widget.set('panel', 'resources')
313
319
  }
314
320
 
315
321
 
322
+
316
323
  return (entry.content || entry.error || !!entry.steps?.length || entry.upload?.length) && (
317
324
  <li key={entry.messageId} className={entry.agentType} ref={ref}>
318
325
  <div className="chat-message-container"
@@ -340,7 +347,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
340
347
  )}
341
348
  </div>
342
349
  {afterMessage && createElement(afterMessage, { message })}
343
- {!!entry.tools?.length && <StackedBadge
350
+ {/* {!!entry.tools?.length && <StackedBadge
344
351
  aria-label={t.openToolsPanel}
345
352
  title={t.openToolsPanel}
346
353
  tabIndex={0}
@@ -352,7 +359,8 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
352
359
  return { key: id, name: tool?.name || id, icon: <Cog />, url: tool?.image }
353
360
  })}
354
361
  onClick={openToolsPanel}
355
- />}
362
+ />} */}
363
+
356
364
  {!!entry.knowledgeSources?.length && <div className="ks-box">
357
365
  <Text appearance="microtext1" colorScheme="light.700">Knowledge Sources:</Text>
358
366
  <ul>{entry.knowledgeSources.map((ks, index) => (
@@ -361,6 +369,28 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
361
369
  </li>
362
370
  ))}</ul>
363
371
  </div>}
372
+
373
+ {(!!agentsTools?.length || !!entry.tools?.length) && <div className="tools-box">
374
+ <AvatarGroup onClick={openResourcesPanel} aria-label={t.openResourcesPanel} role="button">
375
+ {agentsTools?.map((agent) => (
376
+ <Avatar size="xxs" key={agent.id} className="agent-info-avatar-resource">
377
+ {agent.avatar ?
378
+ <img alt={agent.name} aria-label={agent.name} title={agent.name} src={agent.avatar} /> :
379
+ <IconBox appearance="circle" aria-label={agent.name} title={agent.name} color="gray"><Agent /></IconBox>}
380
+ </Avatar>
381
+ ))}
382
+ {entry.tools?.map((id) => {
383
+ const tool = toolById(id, toolKits)
384
+ return (
385
+ <Avatar size="xxs" key={id} className="agent-info-avatar-resource">
386
+ {tool?.image ?
387
+ <img alt={tool.name} aria-label={tool.name} title={tool.name} src={tool.image} /> :
388
+ <IconBox appearance="circle" aria-label={tool?.name} title={tool?.name} color="gray"><Cog /></IconBox>}
389
+ </Avatar>
390
+ )})}
391
+
392
+ </AvatarGroup>
393
+ </div>}
364
394
 
365
395
  {shouldShowFooter && <div className="message-footer">
366
396
  {entry.agentType === 'bot' && !entry.error && <div className="message-actions">
@@ -450,6 +480,8 @@ const dictionary = {
450
480
  dislike: 'Dislike',
451
481
  tools: 'Tools',
452
482
  openToolsPanel: 'Open the tools panel to see more details.',
483
+ openResourcesPanel: 'Open the resources panel to see more details.',
484
+
453
485
  copied: 'Copied',
454
486
  },
455
487
  pt: {
@@ -458,6 +490,7 @@ const dictionary = {
458
490
  dislike: 'Não gostei',
459
491
  tools: 'Ferramentas',
460
492
  openToolsPanel: 'Abrir o painel de ferramentas para ver mais detalhes.',
493
+ openResourcesPanel: 'Abrir o painel de recursos para ver mais detalhes.',
461
494
  copied: 'Copiado',
462
495
  },
463
496
  } satisfies Dictionary
@@ -219,6 +219,34 @@ 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
+ }
246
+ }
247
+
248
+
249
+
222
250
  .steps {
223
251
  ul {
224
252
  list-style: none;
@@ -67,14 +67,14 @@ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
67
67
 
68
68
  > .aria-live {
69
69
  position: absolute;
70
- width: 1;
71
- height: 1;
72
- margin: -1;
73
- padding: 0;
74
- overflow: hidden;
75
- clip: rect(0 0 0 0);
76
- white-Space: nowrap;
77
- border: 0;
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,98 @@
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
+ {hasAgentTool &&
73
+ <>
74
+ {message?.tools?.map((id) => {
75
+ const agentTool = agentsTools?.find((agent) => agent.id === id)
76
+ return (
77
+ <Box key={id} mt={3}>
78
+ <Accordion header={header(agentTool?.avatar || undefined, agentTool?.name)}>
79
+ <AgentDescription agentId={id} />
80
+ </Accordion>
81
+ </Box>)},
82
+ )}
83
+ </>}
84
+ </>
85
+
86
+ )
87
+ }
88
+
89
+ const dictionary = {
90
+ en: {
91
+ title: 'Resources',
92
+ description: 'These are the resources used to generate this answer.',
93
+ },
94
+ pt: {
95
+ title: 'Recursos',
96
+ description: 'Esses são os recursos usados pra gerar essa resposta.',
97
+ },
98
+ } satisfies Dictionary