@stack-spot/ai-chat-widget 1.13.2-beta.0 → 1.13.2

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 (192) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/StackspotAIWidget.d.ts.map +1 -1
  3. package/dist/StackspotAIWidget.js +5 -2
  4. package/dist/StackspotAIWidget.js.map +1 -1
  5. package/dist/app-metadata.json +3 -3
  6. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  7. package/dist/chat-interceptors/send-message.js +2 -0
  8. package/dist/chat-interceptors/send-message.js.map +1 -1
  9. package/dist/components/Markdown.d.ts.map +1 -1
  10. package/dist/components/Markdown.js +1 -1
  11. package/dist/components/Markdown.js.map +1 -1
  12. package/dist/components/StackedBadge.d.ts +14 -0
  13. package/dist/components/StackedBadge.d.ts.map +1 -0
  14. package/dist/components/StackedBadge.js +56 -0
  15. package/dist/components/StackedBadge.js.map +1 -0
  16. package/dist/components/ToolBadge.d.ts +14 -0
  17. package/dist/components/ToolBadge.d.ts.map +1 -0
  18. package/dist/components/ToolBadge.js +108 -0
  19. package/dist/components/ToolBadge.js.map +1 -0
  20. package/dist/state/ChatEntry.d.ts +9 -16
  21. package/dist/state/ChatEntry.d.ts.map +1 -1
  22. package/dist/state/ChatEntry.js.map +1 -1
  23. package/dist/state/ChatState.d.ts +0 -1
  24. package/dist/state/ChatState.d.ts.map +1 -1
  25. package/dist/state/WidgetState.d.ts +3 -4
  26. package/dist/state/WidgetState.d.ts.map +1 -1
  27. package/dist/state/WidgetState.js +1 -12
  28. package/dist/state/WidgetState.js.map +1 -1
  29. package/dist/utils/check-is-trial.d.ts +2 -0
  30. package/dist/utils/check-is-trial.d.ts.map +1 -0
  31. package/dist/utils/check-is-trial.js +6 -0
  32. package/dist/utils/check-is-trial.js.map +1 -0
  33. package/dist/utils/tools.d.ts +7 -0
  34. package/dist/utils/tools.d.ts.map +1 -0
  35. package/dist/utils/tools.js +9 -0
  36. package/dist/utils/tools.js.map +1 -0
  37. package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
  38. package/dist/views/Agents/AgentDescription.js +11 -1
  39. package/dist/views/Agents/AgentDescription.js.map +1 -1
  40. package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
  41. package/dist/views/Agents/AgentsPanel.js +13 -6
  42. package/dist/views/Agents/AgentsPanel.js.map +1 -1
  43. package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
  44. package/dist/views/Agents/AgentsTab.js +10 -8
  45. package/dist/views/Agents/AgentsTab.js.map +1 -1
  46. package/dist/views/Agents/dictionary.d.ts +1 -1
  47. package/dist/views/Agents/dictionary.d.ts.map +1 -1
  48. package/dist/views/Agents/dictionary.js +2 -0
  49. package/dist/views/Agents/dictionary.js.map +1 -1
  50. package/dist/views/Agents/styled.js +1 -1
  51. package/dist/views/Chat/AgentInfo.js +3 -3
  52. package/dist/views/Chat/AgentInfo.js.map +1 -1
  53. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  54. package/dist/views/Chat/ChatMessage.js +19 -4
  55. package/dist/views/Chat/ChatMessage.js.map +1 -1
  56. package/dist/views/Chat/StepsList.d.ts +2 -2
  57. package/dist/views/Chat/StepsList.d.ts.map +1 -1
  58. package/dist/views/Chat/StepsList.js +2 -2
  59. package/dist/views/Chat/StepsList.js.map +1 -1
  60. package/dist/views/Chat/styled.d.ts.map +1 -1
  61. package/dist/views/Chat/styled.js +21 -12
  62. package/dist/views/Chat/styled.js.map +1 -1
  63. package/dist/views/ChatTabSelection.d.ts.map +1 -1
  64. package/dist/views/ChatTabSelection.js +16 -2
  65. package/dist/views/ChatTabSelection.js.map +1 -1
  66. package/dist/views/KnowledgeSources.d.ts.map +1 -1
  67. package/dist/views/KnowledgeSources.js +10 -5
  68. package/dist/views/KnowledgeSources.js.map +1 -1
  69. package/dist/views/MessageInput/AgentSelector.d.ts +2 -1
  70. package/dist/views/MessageInput/AgentSelector.d.ts.map +1 -1
  71. package/dist/views/MessageInput/AgentSelector.js +15 -8
  72. package/dist/views/MessageInput/AgentSelector.js.map +1 -1
  73. package/dist/views/MessageInput/QuickCommandSelector.d.ts +2 -1
  74. package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
  75. package/dist/views/MessageInput/QuickCommandSelector.js +2 -2
  76. package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
  77. package/dist/views/MessageInput/index.d.ts.map +1 -1
  78. package/dist/views/MessageInput/index.js +4 -2
  79. package/dist/views/MessageInput/index.js.map +1 -1
  80. package/dist/views/Stacks.d.ts.map +1 -1
  81. package/dist/views/Stacks.js +10 -5
  82. package/dist/views/Stacks.js.map +1 -1
  83. package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.d.ts.map +1 -1
  84. package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.js.map +1 -1
  85. package/dist/views/{Tools → Steps}/FlowChart/NodeStep.d.ts.map +1 -1
  86. package/dist/views/Steps/FlowChart/NodeStep.js +13 -0
  87. package/dist/views/Steps/FlowChart/NodeStep.js.map +1 -0
  88. package/dist/views/Steps/FlowChart/index.d.ts +10 -0
  89. package/dist/views/Steps/FlowChart/index.d.ts.map +1 -0
  90. package/dist/views/{Tools → Steps}/FlowChart/index.js +1 -5
  91. package/dist/views/{Tools → Steps}/FlowChart/index.js.map +1 -1
  92. package/dist/views/{Tools → Steps}/FlowChart/layout.d.ts.map +1 -1
  93. package/dist/views/{Tools → Steps}/FlowChart/layout.js.map +1 -1
  94. package/dist/views/{Tools → Steps}/FlowChart/styled.d.ts.map +1 -1
  95. package/dist/views/{Tools → Steps}/FlowChart/styled.js +0 -49
  96. package/dist/views/{Tools → Steps}/FlowChart/styled.js.map +1 -1
  97. package/dist/views/{Tools → Steps}/FlowChart/types.d.ts +3 -3
  98. package/dist/views/Steps/FlowChart/types.d.ts.map +1 -0
  99. package/dist/views/Steps/FlowChart/types.js.map +1 -0
  100. package/dist/views/Steps/StepModal.d.ts +9 -0
  101. package/dist/views/Steps/StepModal.d.ts.map +1 -0
  102. package/dist/views/Steps/StepModal.js +124 -0
  103. package/dist/views/Steps/StepModal.js.map +1 -0
  104. package/dist/views/Steps/StepsPanel.d.ts +6 -0
  105. package/dist/views/Steps/StepsPanel.d.ts.map +1 -0
  106. package/dist/views/{Tools/ToolsPanel.js → Steps/StepsPanel.js} +3 -3
  107. package/dist/views/{Tools/ToolsPanel.js.map → Steps/StepsPanel.js.map} +1 -1
  108. package/dist/views/{Tools → Steps}/dictionary.d.ts +9 -11
  109. package/dist/views/Steps/dictionary.d.ts.map +1 -0
  110. package/dist/views/{Tools → Steps}/dictionary.js +9 -11
  111. package/dist/views/Steps/dictionary.js.map +1 -0
  112. package/dist/views/{Tools → Steps}/index.d.ts +1 -1
  113. package/dist/views/Steps/index.d.ts.map +1 -0
  114. package/dist/views/{Tools → Steps}/index.js +10 -10
  115. package/dist/views/{Tools → Steps}/index.js.map +1 -1
  116. package/dist/views/Steps/utils.d.ts +6 -0
  117. package/dist/views/Steps/utils.d.ts.map +1 -0
  118. package/dist/views/{Tools → Steps}/utils.js.map +1 -1
  119. package/dist/views/Tools.d.ts +2 -0
  120. package/dist/views/Tools.d.ts.map +1 -0
  121. package/dist/views/Tools.js +51 -0
  122. package/dist/views/Tools.js.map +1 -0
  123. package/package.json +2 -2
  124. package/src/StackspotAIWidget.tsx +8 -3
  125. package/src/app-metadata.json +3 -3
  126. package/src/chat-interceptors/send-message.ts +2 -0
  127. package/src/components/Markdown.tsx +1 -0
  128. package/src/components/StackedBadge.tsx +77 -0
  129. package/src/components/ToolBadge.tsx +146 -0
  130. package/src/state/ChatEntry.ts +9 -17
  131. package/src/state/ChatState.ts +1 -1
  132. package/src/state/WidgetState.ts +4 -16
  133. package/src/utils/check-is-trial.ts +6 -0
  134. package/src/utils/tools.ts +11 -0
  135. package/src/views/Agents/AgentDescription.tsx +14 -0
  136. package/src/views/Agents/AgentsPanel.tsx +11 -3
  137. package/src/views/Agents/AgentsTab.tsx +11 -10
  138. package/src/views/Agents/dictionary.ts +2 -0
  139. package/src/views/Agents/styled.ts +1 -1
  140. package/src/views/Chat/AgentInfo.tsx +4 -4
  141. package/src/views/Chat/ChatMessage.tsx +30 -4
  142. package/src/views/Chat/StepsList.tsx +6 -6
  143. package/src/views/Chat/styled.ts +21 -12
  144. package/src/views/ChatTabSelection.tsx +22 -4
  145. package/src/views/KnowledgeSources.tsx +9 -3
  146. package/src/views/MessageInput/AgentSelector.tsx +19 -8
  147. package/src/views/MessageInput/QuickCommandSelector.tsx +3 -2
  148. package/src/views/MessageInput/index.tsx +5 -4
  149. package/src/views/Stacks.tsx +10 -4
  150. package/src/views/{Tools → Steps}/FlowChart/NodeStep.tsx +11 -23
  151. package/src/views/{Tools → Steps}/FlowChart/index.tsx +3 -7
  152. package/src/views/{Tools → Steps}/FlowChart/styled.ts +0 -49
  153. package/src/views/{Tools → Steps}/FlowChart/types.ts +3 -3
  154. package/src/views/Steps/StepModal.tsx +216 -0
  155. package/src/views/{Tools/ToolsPanel.tsx → Steps/StepsPanel.tsx} +2 -2
  156. package/src/views/{Tools → Steps}/dictionary.ts +9 -11
  157. package/src/views/{Tools → Steps}/index.tsx +10 -10
  158. package/src/views/{Tools → Steps}/utils.tsx +4 -4
  159. package/src/views/Tools.tsx +71 -0
  160. package/dist/utils/agent.d.ts +0 -2
  161. package/dist/utils/agent.d.ts.map +0 -1
  162. package/dist/utils/agent.js +0 -2
  163. package/dist/utils/agent.js.map +0 -1
  164. package/dist/views/Tools/FlowChart/NodeStep.js +0 -15
  165. package/dist/views/Tools/FlowChart/NodeStep.js.map +0 -1
  166. package/dist/views/Tools/FlowChart/index.d.ts +0 -9
  167. package/dist/views/Tools/FlowChart/index.d.ts.map +0 -1
  168. package/dist/views/Tools/FlowChart/types.d.ts.map +0 -1
  169. package/dist/views/Tools/FlowChart/types.js.map +0 -1
  170. package/dist/views/Tools/StepModal.d.ts +0 -9
  171. package/dist/views/Tools/StepModal.d.ts.map +0 -1
  172. package/dist/views/Tools/StepModal.js +0 -156
  173. package/dist/views/Tools/StepModal.js.map +0 -1
  174. package/dist/views/Tools/ToolsPanel.d.ts +0 -6
  175. package/dist/views/Tools/ToolsPanel.d.ts.map +0 -1
  176. package/dist/views/Tools/dictionary.d.ts.map +0 -1
  177. package/dist/views/Tools/dictionary.js.map +0 -1
  178. package/dist/views/Tools/index.d.ts.map +0 -1
  179. package/dist/views/Tools/utils.d.ts +0 -6
  180. package/dist/views/Tools/utils.d.ts.map +0 -1
  181. package/src/utils/agent.ts +0 -1
  182. package/src/views/Tools/StepModal.tsx +0 -247
  183. /package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.d.ts +0 -0
  184. /package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.js +0 -0
  185. /package/dist/views/{Tools → Steps}/FlowChart/NodeStep.d.ts +0 -0
  186. /package/dist/views/{Tools → Steps}/FlowChart/layout.d.ts +0 -0
  187. /package/dist/views/{Tools → Steps}/FlowChart/layout.js +0 -0
  188. /package/dist/views/{Tools → Steps}/FlowChart/styled.d.ts +0 -0
  189. /package/dist/views/{Tools → Steps}/FlowChart/types.js +0 -0
  190. /package/dist/views/{Tools → Steps}/utils.js +0 -0
  191. /package/src/views/{Tools → Steps}/FlowChart/HandleGroup.tsx +0 -0
  192. /package/src/views/{Tools → Steps}/FlowChart/layout.ts +0 -0
@@ -0,0 +1,146 @@
1
+ import { IconBox, Text } from '@citric/core'
2
+ import { ChevronDown, Cog } from '@citric/icons'
3
+ import { IconButton } from '@citric/ui'
4
+ import { AnimatedHeight } from '@stack-spot/portal-components/AnimatedHeight'
5
+ import { ColorSchemeKey, listToClass, theme } from '@stack-spot/portal-theme'
6
+ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
+ import { get } from 'lodash'
8
+ import { DetailedHTMLProps, HTMLAttributes, useState } from 'react'
9
+ import { styled } from 'styled-components'
10
+ import { FastOmit } from 'styled-components/dist/types'
11
+ import { toPrecision } from '../views/Steps/utils'
12
+
13
+ interface Props extends FastOmit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never> {
14
+ name: string,
15
+ duration?: number,
16
+ image?: string,
17
+ description?: string,
18
+ appearance?: 'round' | 'square',
19
+ backgroundColor?: ColorSchemeKey,
20
+ }
21
+
22
+ const Styled = styled.div<{ $backgroundColor: ColorSchemeKey }>`
23
+ .tool-header-wrapper {
24
+ background-color: ${({ $backgroundColor }) => get(theme.color, $backgroundColor)};
25
+ border-radius: 8px;
26
+ }
27
+
28
+ .tool-header {
29
+ padding: 4px 8px 4px 4px;
30
+ display: flex;
31
+ flex-direction: column;
32
+ gap: 12px;
33
+ position: relative;
34
+
35
+ .title {
36
+ display: flex;
37
+ gap: 8px;
38
+ align-items: center;
39
+ .tool-image {
40
+ width: 24px;
41
+ height: 24px;
42
+ border-radius: 50%;
43
+ overflow: hidden;
44
+ flex-shrink: 0;
45
+ }
46
+ .name {
47
+ flex: 1;
48
+ }
49
+ }
50
+
51
+ .btn-expand {
52
+ display: none;
53
+ }
54
+
55
+ .duration {
56
+ opacity: 0.7;
57
+ text-align: right;
58
+ }
59
+ }
60
+
61
+ &.with-description {
62
+ .tool-header {
63
+ &:before {
64
+ content: '';
65
+ top: 32px;
66
+ bottom: 8px;
67
+ left: 15px;
68
+ width: 1px;
69
+ background-color: ${theme.color.light[700]};
70
+ opacity: 0.3;
71
+ position: absolute;
72
+ }
73
+
74
+ .btn-expand {
75
+ display: flex;
76
+ transition: transform 0.3s ease-in-out;
77
+ &.open {
78
+ transform: rotate(180deg);
79
+ }
80
+ }
81
+
82
+ .description {
83
+ padding: 0 0 10px 20px;
84
+ opacity: 0.7;
85
+ }
86
+ }
87
+ }
88
+
89
+ &.round {
90
+ .tool-header-wrapper {
91
+ border-radius: 30px;
92
+ }
93
+ &:not(.with-description) {
94
+ .tool-header-wrapper {
95
+ padding-right: 10px;
96
+ }
97
+ }
98
+ }
99
+ `
100
+
101
+ export const ToolBadge = (
102
+ { name, duration, image, description, appearance = 'square', backgroundColor = 'light.300', className, ...props }: Props,
103
+ ) => {
104
+ const t = useTranslate(dictionary)
105
+ const [showDescription, setShowDescription] = useState(false)
106
+ const rootClass = listToClass([className, appearance, description && 'with-description'])
107
+ const wrapperClass = 'tool-header-wrapper'
108
+
109
+ const content = (
110
+ <div className="tool-header">
111
+ <div className="title">
112
+ {image ? <img src={image} className="tool-image" /> : <IconBox className="tool-image"><Cog /></IconBox>}
113
+ <Text colorScheme="light.700" className="name">{name}</Text>
114
+ {duration !== undefined && <Text colorScheme="light.700" className="duration">
115
+ {toPrecision(duration)}s
116
+ </Text>}
117
+ <IconButton
118
+ size="xs"
119
+ className={listToClass(['btn-expand', showDescription && 'open'])}
120
+ onClick={() => setShowDescription(v => !v)}
121
+ aria-label={showDescription ? t.close : t.open}
122
+ >
123
+ <ChevronDown />
124
+ </IconButton>
125
+ </div>
126
+ {showDescription && <Text className="description" colorScheme="light.700">{description}</Text>}
127
+ </div>
128
+ )
129
+
130
+ return (
131
+ <Styled {...props} $backgroundColor={backgroundColor} className={rootClass}>
132
+ {description ? <AnimatedHeight className={wrapperClass}>{content}</AnimatedHeight> : <div className={wrapperClass}>{content}</div>}
133
+ </Styled>
134
+ )
135
+ }
136
+
137
+ const dictionary = {
138
+ en: {
139
+ open: 'Open',
140
+ close: 'Close',
141
+ },
142
+ pt: {
143
+ open: 'Abrir',
144
+ close: 'Fechar',
145
+ },
146
+ } satisfies Dictionary
@@ -1,4 +1,5 @@
1
1
  import { OneOfColorSchemes } from '@citric/core'
2
+ import { ChatStep } from '@stack-spot/portal-network'
2
3
  import { ColorPaletteName } from '@stack-spot/portal-theme'
3
4
  import { pull } from 'lodash'
4
5
  import { LabeledWithImage } from './types'
@@ -57,24 +58,11 @@ export interface KnowledgeSource {
57
58
  documentId: string,
58
59
  }
59
60
 
60
- export interface AgentTool {
61
+ export interface ChatTool {
61
62
  id: string,
62
- name: string,
63
- description?: string,
64
63
  image?: string,
65
- duration?: number,
66
- input?: string,
67
- output?: string,
68
- }
69
-
70
- export interface ChatEntryStep {
71
- id: string,
72
- type: 'planning' | 'step' | 'answer',
73
- input?: string,
74
- output?: string,
75
- status: 'pending' | 'running' | 'success' | 'error',
76
- duration?: number,
77
- tools?: AgentTool[],
64
+ name: string,
65
+ description: string,
78
66
  }
79
67
 
80
68
  export interface TextChatEntry {
@@ -131,7 +119,11 @@ export interface TextChatEntry {
131
119
  /**
132
120
  * This entry may contain steps. If so, specify them in this array.
133
121
  */
134
- steps?: ChatEntryStep[],
122
+ steps?: ChatStep[],
123
+ /**
124
+ * If any tool was used to generate the response, its id is returned in this list.
125
+ */
126
+ tools?: string[],
135
127
  /*
136
128
  * Options for radio, checkbox or button type.
137
129
  */
@@ -14,7 +14,7 @@ export interface ChatPropertiesWithOptionalFeatures {
14
14
  /**
15
15
  * The current AI agent.
16
16
  */
17
- agent?: LabeledWithImage & { builtIn: boolean, slug?: string },
17
+ agent?: LabeledWithImage & { builtIn: boolean },
18
18
  /**
19
19
  * The current workspace.
20
20
  */
@@ -13,15 +13,15 @@ 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' | 'tools',
16
+ panel?: 'stack' | 'workspace' | 'agent' | 'ks' | 'editor' | 'history' | 'ks-details' | 'steps' | 'tools',
17
17
  /**
18
18
  * KS to use when the right panel "ks-details" is open.
19
19
  */
20
20
  currentKSInPanel?: { name: string, slug: string, score: number, documentId: string },
21
21
  /**
22
- * The message to show in the tools panel.
22
+ * The message to show in the tools panel or the steps panel.
23
23
  */
24
- currentMessageInToolsPanel?: { chatId: string, messageId: number },
24
+ currentMessageInPanel?: { chatId: string, messageId: number },
25
25
  /**
26
26
  * Whether or not the widget is in its minimized version.
27
27
  */
@@ -58,8 +58,6 @@ interface Options {
58
58
  chatTabs?: ChatTabsController,
59
59
  }
60
60
 
61
- const untitledChatPrefix = 'Chat '
62
-
63
61
  /**
64
62
  * Holds the full state of the AI Chat Widget.
65
63
  *
@@ -90,16 +88,6 @@ export class WidgetState extends ObservableState<WidgetProperties> {
90
88
  this.createChat()
91
89
  }
92
90
 
93
- private getNextUntitledChatIndex() {
94
- let max = 0
95
- for (const chat of this.chatTabs.getAll()) {
96
- const [, match] = chat.get('label').match(`${untitledChatPrefix}(\\d+)`) ?? []
97
- const index = parseInt(match)
98
- if (index > max) max = index
99
- }
100
- return max + 1
101
- }
102
-
103
91
  /**
104
92
  * Utility function for adding a new chat to the chat tabs. This also selects the new tab.
105
93
  * @param properties the chat properties (initial state).
@@ -109,7 +97,7 @@ export class WidgetState extends ObservableState<WidgetProperties> {
109
97
  createChat({ label, ...properties }: Partial<ChatPropertiesWithOptionalFeatures> = {}, entries: ChatEntry[] = []) {
110
98
  const chat = new ChatState({
111
99
  id: ulid(),
112
- initial: { label: label || `${untitledChatPrefix}${this.getNextUntitledChatIndex()}`, features: this.chatFeatures, ...properties },
100
+ initial: { label: label || 'Chat', features: this.chatFeatures, ...properties },
113
101
  entries,
114
102
  interceptors: this.interceptors,
115
103
  untitled: !label,
@@ -0,0 +1,6 @@
1
+ import { NetworkClient } from '@stack-spot/portal-network'
2
+
3
+ export const checkIsTrial = () => {
4
+ const trialInfo = NetworkClient.sessionManager?.getSession()?.getTokenData()?.trial_account_status
5
+ return !!trialInfo
6
+ }
@@ -0,0 +1,11 @@
1
+ import { BuiltinToolResponse, ToolkitsInAgentResponse } from '@stack-spot/portal-network/api/agent'
2
+
3
+ export type ToolWithImage = BuiltinToolResponse & { id: string, image?: string }
4
+
5
+ export function toolById(id: string, toolkits: ToolkitsInAgentResponse | undefined): ToolWithImage | undefined {
6
+ for (const toolkit of toolkits?.builtins ?? []) {
7
+ for (const tool of toolkit.tools ?? []) {
8
+ if (tool.id === id) return { ...tool, id, image: toolkit.image_url }
9
+ }
10
+ }
11
+ }
@@ -2,6 +2,7 @@ import { Text } from '@citric/core'
2
2
  import { Badge, Skeleton } from '@citric/ui'
3
3
  import { agentClient } from '@stack-spot/portal-network'
4
4
  import { useMemo } from 'react'
5
+ import { ToolBadge } from '../../components/ToolBadge'
5
6
  import { useAgentsDictionary } from './dictionary'
6
7
  import { AgentDescriptionBox } from './styled'
7
8
 
@@ -28,6 +29,15 @@ export const AgentDescription = ({ agentId, llm, description, numberOfKnowledgeS
28
29
  }
29
30
  return loadingKS
30
31
  }, [numberOfKnowledgeSources])
32
+ const tools = useMemo(() => {
33
+ const result: React.ReactElement[] = []
34
+ for (const kit of agent?.toolkits?.builtins ?? []) {
35
+ for (const tool of kit.tools ?? []) {
36
+ result.push(<li key={tool.id}><ToolBadge name={tool.name || tool.id || 'unknown'} image={kit.image_url} /></li>)
37
+ }
38
+ }
39
+ return result
40
+ }, [agent])
31
41
 
32
42
  return (
33
43
  <AgentDescriptionBox>
@@ -39,6 +49,10 @@ export const AgentDescription = ({ agentId, llm, description, numberOfKnowledgeS
39
49
  <Text appearance="microtext1" className="title">Knowledge sources</Text>
40
50
  <ul>{isLoading ? skeleton : knowledgeSources}</ul>
41
51
  </section>}
52
+ {!!tools.length && <section>
53
+ <Text appearance="microtext1" className="title">{t.tools}</Text>
54
+ <ul>{tools}</ul>
55
+ </section>}
42
56
  {llm && <section>
43
57
  <Text appearance="microtext1" className="title">LLM</Text>
44
58
  <Badge palette="orange" appearance="square">{llm}</Badge>
@@ -1,5 +1,7 @@
1
+ import { useMemo } from 'react'
1
2
  import { RightPanelTabs } from '../../components/RightPanelTabs'
2
3
  import { useCurrentChat } from '../../context/hooks'
4
+ import { checkIsTrial } from '../../utils/check-is-trial'
3
5
  import { AgentsTab } from './AgentsTab'
4
6
  import { useAgentsDictionary } from './dictionary'
5
7
 
@@ -9,11 +11,17 @@ import { useAgentsDictionary } from './dictionary'
9
11
  export const AgentsPanel = () => {
10
12
  const t = useAgentsDictionary()
11
13
  const chat = useCurrentChat()
12
-
13
- return <RightPanelTabs key={chat.id} tabs={[
14
+ const isTrial = checkIsTrial()
15
+
16
+ const tabs= useMemo(() => isTrial ? [
17
+ { title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" /> },
18
+ { title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
19
+ ]: [
14
20
  { title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" /> },
15
21
  { title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
16
22
  { title: t.shared, content: <AgentsTab key="shared" visibility="SHARED" /> },
17
23
  { title: t.account, content: <AgentsTab key="account" visibility="ACCOUNT" /> },
18
- ]} />
24
+ ], [t, isTrial])
25
+
26
+ return <RightPanelTabs key={chat.id} tabs={tabs} />
19
27
  }
@@ -9,7 +9,6 @@ import { DescribedRadioGroup } from '../../components/form/DescribedRadioGroup'
9
9
  import { IconInput } from '../../components/IconInput'
10
10
  import { useCurrentChat } from '../../context/hooks'
11
11
  import { useRightPanel } from '../../right-panel/hooks'
12
- import { isAgentDefault } from '../../utils/agent'
13
12
  import { AgentDescription } from './AgentDescription'
14
13
  import { useAgentsDictionary } from './dictionary'
15
14
  import { AgentLabel } from './styled'
@@ -19,25 +18,27 @@ export const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT
19
18
  const { close } = useRightPanel()
20
19
  const chat = useCurrentChat()
21
20
  const [filter, setFilter] = useState('')
22
- const agentsBuiltIn = agentClient.publicAgents.useQuery({})
23
- const defaultAgent = agentsBuiltIn.find((agent) => isAgentDefault(agent.slug))
24
- const agents = visibility === 'BUILT-IN' ? agentsBuiltIn : agentClient.agents.useQuery({ visibility })
21
+ const defaultAgent = useMemo(() => ({
22
+ id: '',
23
+ name: 'StackSpot AI',
24
+ description: t.defaultAgentDescription,
25
+ llm_config: { model_slug: 'gpt4o' },
26
+ } as AgentResponse), [])
27
+ const agents = visibility === 'BUILT-IN' ? agentClient.publicAgents.useQuery({}) : agentClient.agents.useQuery({ visibility })
25
28
  const [value, setValue] = useState<AgentResponse | undefined>(
26
29
  chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : defaultAgent,
27
30
  )
28
31
  const filtered = useMemo(
29
- () => {
30
- return filter ? agents.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : agents
32
+ () => {
33
+ const ags = visibility === 'BUILT-IN' ? [defaultAgent as AgentResponse, ...agents] : agents
34
+ return filter ? ags.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : ags
31
35
  },
32
36
  [agents, filter, value],
33
37
  )
34
38
 
35
39
  function submit() {
36
40
  if (value) {
37
- chat.set('agent', !isAgentDefault(value.slug)
38
- ? { id: value.id, label: value.name, image: value.avatar, builtIn: visibility === 'BUILT-IN', slug: value.slug }
39
- : undefined
40
- )
41
+ chat.set('agent', value.id ? { id: value.id, label: value.name, image: value.avatar, builtIn: visibility === 'BUILT-IN' } : undefined)
41
42
  }
42
43
  close()
43
44
  }
@@ -15,6 +15,7 @@ const dictionary = {
15
15
  noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new agents.',
16
16
  defaultAgentDescription: 'The StackSpot CodeGen is an advanced artificial intelligence agent designed to optimize and accelerate software development. Integrated directly into your integrated development environment, StackSpot CodeGen offers real-time code suggestions, helping developers write high-quality code more efficiently. With robust features such as creating Stacks AI, customized knowledge sources, and quick commands, StackSpot CodeGen contextualizes your development needs to provide the best answers and code suggestions.',
17
17
  description: 'Description',
18
+ tools: 'Tools',
18
19
  },
19
20
  pt: {
20
21
  title: 'Agentes',
@@ -30,6 +31,7 @@ const dictionary = {
30
31
  noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos agentes.',
31
32
  defaultAgentDescription: 'O StackSpot CodeGen é um agente de inteligência artificial avançado projetado para otimizar e acelerar o desenvolvimento de software. Integrado diretamente ao seu ambiente de desenvolvimento, o StackSpot CodeGen oferece sugestões de código em tempo real, ajudando os desenvolvedores a escreverem código de alta qualidade de forma mais eficiente. Com recursos robustos, como a criação de Stacks AI, Knowledge Sources personalizadas e comandos rápidos, o StackSpot CodeGen contextualiza suas necessidades de desenvolvimento para fornecer as melhores respostas e sugestões de código.',
32
33
  description: 'Descrição',
34
+ tools: 'Ferramentas',
33
35
  },
34
36
  } satisfies Dictionary
35
37
 
@@ -30,7 +30,7 @@ export const AgentDescriptionBox = styled.div`
30
30
  border-bottom: none;
31
31
  }
32
32
 
33
- .title {
33
+ > .title {
34
34
  display: block;
35
35
  margin-bottom: 6px;
36
36
  }
@@ -1,5 +1,5 @@
1
- import { Text } from '@citric/core'
2
- import { MiniLogo } from '@stack-spot/portal-components/svg'
1
+ import { IconBox, Text } from '@citric/core'
2
+ import { Agent } from '@citric/icons'
3
3
  import { LabeledWithImage } from '../../state/types'
4
4
 
5
5
  interface Props {
@@ -13,8 +13,8 @@ export const AgentInfo = ({ agent }: Props) => (
13
13
  <>
14
14
  {agent?.image
15
15
  ? <img src={agent.image} className="custom-agent-image" />
16
- : <div className="default-image-wrapper"><MiniLogo className="agent-image" /></div>
16
+ : <IconBox className="default-image-wrapper" colorIcon="light.700"><Agent className="agent-image" /></IconBox>
17
17
  }
18
- <Text appearance="body2">{agent?.label || 'StackSpot AI CodeGen'}</Text>
18
+ <Text appearance="body2">{agent?.label || 'StackSpot AI'}</Text>
19
19
  </>
20
20
  )
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable semi */
2
2
  import { Box, Button, Checkbox, Flex, IconBox, Input, Label, Radio, Text } from '@citric/core'
3
- import { Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
3
+ import { Cog, Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
4
4
  import { Avatar, Badge, IconButton } from '@citric/ui'
5
+ import { agentClient } from '@stack-spot/portal-network'
5
6
  import { listToClass } from '@stack-spot/portal-theme'
6
7
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
8
  import { debounce } from 'lodash'
@@ -9,9 +10,11 @@ import { Dispatch, useCallback, useEffect, useMemo, useRef, useState } from 'rea
9
10
  import { PhoneInput } from 'react-international-phone'
10
11
  import 'react-international-phone/style.css'
11
12
  import { Markdown } from '../../components/Markdown'
13
+ import { StackedBadge } from '../../components/StackedBadge'
12
14
  import { useChatEntry, useCurrentChat, useWidget } from '../../context/hooks'
13
15
  import { ChatEntry, SerializableAction, TextChatEntry } from '../../state/ChatEntry'
14
16
  import { useDateFormatter } from '../../utils/date'
17
+ import { toolById } from '../../utils/tools'
15
18
  import { AgentInfo } from './AgentInfo'
16
19
  import { useChatScrollToBottomEffect } from './chat-scroll'
17
20
  import { onCopyAll, onCopyCode, onLikeOrDislike } from './events'
@@ -82,7 +85,7 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
82
85
  {option.hasInput && option.value && labels.findIndex((label) => label === option.value) !== -1 &&
83
86
  <Box w={4} ml={2}>
84
87
  <Input name={entry.name} onChange={(data) => setValue([data.target.value])}
85
- required={true} />
88
+ required={true} sx={{ height: '30px' }} />
86
89
  </Box>
87
90
  }
88
91
  </Flex>
@@ -120,7 +123,7 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
120
123
  {option.hasInput && option.value && labels.findIndex((label) => label === option.value)!== -1 &&
121
124
  <Box w={4} ml={2}>
122
125
  <Input name={entry.name} {...entry.validations} onChange={(data) => handleChange(data)}
123
- required={true} />
126
+ required={true} sx={{ height: '30px' }} />
124
127
  </Box>
125
128
  }
126
129
  </Flex>
@@ -163,6 +166,7 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
163
166
  const chatRef = useRef<HTMLDivElement>(null)
164
167
  const widget = useWidget()
165
168
  const chat = useCurrentChat()
169
+ const [agent] = agentClient.agent.useStatefulQuery({ agentId: entry.agent?.id ?? '' }, { enabled: !!entry.agent?.id })
166
170
  useChatScrollToBottomEffect(ref, [entry])
167
171
 
168
172
  const detailKS = useCallback(({ name, slug, documentScore, documentId }: Required<TextChatEntry>['knowledgeSources'][number]) => {
@@ -249,10 +253,15 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
249
253
  </form>
250
254
  }
251
255
 
256
+ function openToolsPanel() {
257
+ widget.set('currentMessageInPanel', { chatId: chat.id, messageId: message.id })
258
+ widget.set('panel', 'tools')
259
+ }
260
+
252
261
  return (entry.content || entry.error || !!entry.steps?.length) && (
253
262
  <li className={entry.agentType} ref={ref}>
254
263
  <div className="chat-message" ref={chatRef} onKeyDown={handleKeyDown} tabIndex={0}>
255
- <div className="user-info">{userInfo}</div>
264
+ <div className={`user-info ${entry.agentType}`}>{userInfo}</div>
256
265
  {(entry.content || entry.steps) && <div className={listToClass(['message-content', entry.card && 'card'])}>
257
266
  {!!entry.badges?.length && <div className="badges">
258
267
  {entry.badges.map((b, index) => <Badge key={index} palette={b.color ?? 'cyan'} appearance="square">{b.label}</Badge>)}
@@ -268,6 +277,19 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
268
277
  <Text appearance="microtext1">{entry.error}</Text>
269
278
  </div>
270
279
  )}
280
+ {!!entry.tools?.length && <StackedBadge
281
+ aria-label={t.openToolsPanel}
282
+ title={t.openToolsPanel}
283
+ tabIndex={0}
284
+ role="button"
285
+ className="tools-badge"
286
+ label={t.tools}
287
+ images={entry.tools.slice(0, 3).map((id) => {
288
+ const tool = toolById(id, agent?.toolkits)
289
+ return { key: id, name: tool?.name || id, icon: <Cog />, url: tool?.image }
290
+ })}
291
+ onClick={openToolsPanel}
292
+ />}
271
293
  {!!entry.knowledgeSources?.length && <div className="ks-box">
272
294
  <Text appearance="microtext1" colorScheme="light.700">Knowledge Sources:</Text>
273
295
  <ul>{entry.knowledgeSources.map((ks, index) => (
@@ -307,10 +329,14 @@ const dictionary = {
307
329
  copy: 'Copy',
308
330
  like: 'Like',
309
331
  dislike: 'Dislike',
332
+ tools: 'Tools',
333
+ openToolsPanel: 'Open the tools panel to see more details.',
310
334
  },
311
335
  pt: {
312
336
  copy: 'Copiar',
313
337
  like: 'Gostei',
314
338
  dislike: 'Não gostei',
339
+ tools: 'Ferramentas',
340
+ openToolsPanel: 'Abrir o painel de ferramentas para ver mais detalhes.',
315
341
  },
316
342
  } satisfies Dictionary
@@ -2,27 +2,27 @@ import { Button, IconBox, Text } from '@citric/core'
2
2
  import { CheckCircleFill, Circle, PlayFill, TimesCircleFill } from '@citric/icons'
3
3
  import { LoadingCircular } from '@citric/ui'
4
4
  import { AnimatedHeight } from '@stack-spot/portal-components/AnimatedHeight'
5
+ import { ChatStep, StepChatStep } from '@stack-spot/portal-network'
5
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
6
7
  import { findLastIndex } from 'lodash'
7
8
  import { useState } from 'react'
8
9
  import { useWidget } from '../../context/hooks'
9
- import { ChatEntryStep } from '../../state/ChatEntry'
10
10
  import { PropsOf } from '../../types'
11
11
 
12
12
  interface Props {
13
- steps: ChatEntryStep[],
13
+ steps: ChatStep[],
14
14
  messageId: number,
15
15
  chatId: string,
16
16
  }
17
17
 
18
18
  interface StepProps {
19
- step: ChatEntryStep,
19
+ step: StepChatStep,
20
20
  index: number,
21
21
  total: number,
22
22
  onClick?: () => void,
23
23
  }
24
24
 
25
- function getStatusIcon(status: ChatEntryStep['status']) {
25
+ function getStatusIcon(status: ChatStep['status']) {
26
26
  const iconBoxProps: PropsOf<typeof IconBox> = { colorIcon: 'light.700', size: 'xs' }
27
27
  switch (status) {
28
28
  case 'error': return <IconBox {...iconBoxProps}><TimesCircleFill /></IconBox>
@@ -53,8 +53,8 @@ export const StepsList = ({ steps, chatId, messageId }: Props) => {
53
53
  const widget = useWidget()
54
54
 
55
55
  function openToolsPanel() {
56
- widget.set('currentMessageInToolsPanel', { chatId, messageId })
57
- widget.set('panel', 'tools')
56
+ widget.set('currentMessageInPanel', { chatId, messageId })
57
+ widget.set('panel', 'steps')
58
58
  }
59
59
 
60
60
  return (
@@ -38,6 +38,10 @@ export const ChatList: IStyledComponentBase<
38
38
  display: flex;
39
39
  flex-direction: row;
40
40
  gap: 10px;
41
+
42
+ &.bot {
43
+ align-items: center;
44
+ }
41
45
  }
42
46
 
43
47
  .chat-message {
@@ -82,23 +86,19 @@ export const ChatList: IStyledComponentBase<
82
86
  gap: 4px;
83
87
 
84
88
  .default-image-wrapper {
85
- width: 24px;
86
- height: 24px;
87
- border-radius: 4px;
89
+ width: 32px;
90
+ height: 32px;
91
+ border-radius: 50%;
88
92
  background-color: ${theme.color.light[300]};
89
93
  display: flex;
90
94
  align-items: center;
91
95
  justify-content: center;
92
-
93
- .agent-image {
94
- width: 18px;
95
- height: 18px;
96
- }
96
+ border: 1px solid ${theme.color.light[600]};
97
97
  }
98
98
 
99
99
  .custom-agent-image {
100
- width: 24px;
101
- height: 24px;
100
+ width: 32px;
101
+ height: 32px;
102
102
  border-radius: 50%;
103
103
  }
104
104
  }
@@ -149,11 +149,11 @@ export const ChatList: IStyledComponentBase<
149
149
  background-color: ${theme.color.light[500]};
150
150
  border-radius: 4px;
151
151
 
152
- > p:first-child {
152
+ .markdown > p:first-child {
153
153
  margin-top: 0;
154
154
  }
155
155
 
156
- > p:last-child {
156
+ .markdown > p:last-child {
157
157
  margin-bottom: 0;
158
158
  }
159
159
  }
@@ -244,4 +244,13 @@ export const ChatList: IStyledComponentBase<
244
244
  }
245
245
  }
246
246
  }
247
+
248
+ .markdown img {
249
+ max-width: 70%;
250
+ }
251
+
252
+ .tools-badge {
253
+ align-self: start;
254
+ cursor: pointer;
255
+ }
247
256
  `