@stack-spot/ai-chat-widget 3.6.2-beta.5 → 3.7.0-beta.5
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 +23 -45
- package/dist/app-metadata.json +3 -3
- package/dist/chat-interceptors/quick-commands.d.ts.map +1 -1
- package/dist/chat-interceptors/quick-commands.js +14 -5
- package/dist/chat-interceptors/quick-commands.js.map +1 -1
- package/dist/chat-interceptors/send-message.d.ts.map +1 -1
- package/dist/chat-interceptors/send-message.js +6 -2
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/Selector/SelectVersion.d.ts +12 -0
- package/dist/components/Selector/SelectVersion.d.ts.map +1 -0
- package/dist/components/Selector/SelectVersion.js +33 -0
- package/dist/components/Selector/SelectVersion.js.map +1 -0
- package/dist/components/Selector/index.d.ts +13 -2
- package/dist/components/Selector/index.d.ts.map +1 -1
- package/dist/components/Selector/index.js +23 -19
- package/dist/components/Selector/index.js.map +1 -1
- package/dist/components/Selector/styled.d.ts +2 -0
- package/dist/components/Selector/styled.d.ts.map +1 -1
- package/dist/components/Selector/styled.js +46 -0
- package/dist/components/Selector/styled.js.map +1 -1
- package/dist/hooks/enabled-feature-flags.d.ts +5 -0
- package/dist/hooks/enabled-feature-flags.d.ts.map +1 -0
- package/dist/hooks/enabled-feature-flags.js +28 -0
- package/dist/hooks/enabled-feature-flags.js.map +1 -0
- package/dist/state/types.d.ts +1 -0
- package/dist/state/types.d.ts.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/tools.d.ts +5 -5
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +2 -2
- package/dist/utils/tools.js.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts +2 -1
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.js +21 -10
- package/dist/views/Agents/AgentDescription.js.map +1 -1
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
- package/dist/views/Agents/AgentsTab.js +6 -3
- package/dist/views/Agents/AgentsTab.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/Chat/ButtonExecutionDetail.js +6 -6
- package/dist/views/Chat/ButtonExecutionDetail.js.map +1 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +1 -2
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/ChatHistory/utils.js +1 -1
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/MessageInput/AgentSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/AgentSelector.js +19 -7
- package/dist/views/MessageInput/AgentSelector.js.map +1 -1
- package/dist/views/MessageInput/ModelSwitcher/utils.d.ts +2 -2
- package/dist/views/MessageInput/ModelSwitcher/utils.d.ts.map +1 -1
- package/dist/views/MessageInput/ModelSwitcher/utils.js.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.js +12 -8
- package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
- package/dist/views/Steps/FlowChart/{NodeTool.d.ts → NodeDynamic.d.ts} +2 -2
- package/dist/views/Steps/FlowChart/NodeDynamic.d.ts.map +1 -0
- package/dist/views/Steps/FlowChart/{NodeTool.js → NodeDynamic.js} +5 -5
- package/dist/views/Steps/FlowChart/NodeDynamic.js.map +1 -0
- package/dist/views/Steps/FlowChart/NodeStep.js +1 -1
- package/dist/views/Steps/FlowChart/NodeStep.js.map +1 -1
- package/dist/views/Steps/FlowChart/hooks.d.ts +1 -1
- package/dist/views/Steps/FlowChart/hooks.d.ts.map +1 -1
- package/dist/views/Steps/FlowChart/hooks.js +4 -2
- package/dist/views/Steps/FlowChart/hooks.js.map +1 -1
- package/dist/views/Steps/FlowChart/index.js +3 -3
- package/dist/views/Steps/FlowChart/index.js.map +1 -1
- package/dist/views/Steps/utils.d.ts.map +1 -1
- package/dist/views/Steps/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/app-metadata.json +3 -3
- package/src/chat-interceptors/quick-commands.ts +14 -5
- package/src/chat-interceptors/send-message.ts +6 -2
- package/src/components/Selector/SelectVersion.tsx +55 -0
- package/src/components/Selector/index.tsx +57 -35
- package/src/components/Selector/styled.ts +48 -0
- package/src/hooks/enabled-feature-flags.ts +31 -0
- package/src/state/types.ts +1 -0
- package/src/types.ts +0 -1
- package/src/utils/tools.ts +4 -4
- package/src/views/Agents/AgentDescription.tsx +36 -14
- package/src/views/Agents/AgentsTab.tsx +13 -11
- package/src/views/Agents/dictionary.ts +2 -1
- package/src/views/Chat/ButtonExecutionDetail.tsx +6 -6
- package/src/views/Chat/ChatMessage.tsx +1 -2
- package/src/views/ChatHistory/utils.ts +4 -4
- package/src/views/MessageInput/AgentSelector.tsx +24 -12
- package/src/views/MessageInput/ModelSwitcher/utils.tsx +2 -2
- package/src/views/MessageInput/QuickCommandSelector.tsx +33 -23
- package/src/views/Steps/FlowChart/{NodeTool.tsx → NodeDynamic.tsx} +6 -6
- package/src/views/Steps/FlowChart/NodeStep.tsx +1 -1
- package/src/views/Steps/FlowChart/hooks.ts +3 -2
- package/src/views/Steps/FlowChart/index.tsx +3 -3
- package/src/views/Steps/utils.tsx +1 -1
- package/dist/views/Steps/FlowChart/NodeTool.d.ts.map +0 -1
- package/dist/views/Steps/FlowChart/NodeTool.js.map +0 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
import { Select } from '@stack-spot/citric-react'
|
|
3
|
+
import { useTranslate } from '@stack-spot/portal-translate'
|
|
4
|
+
import { Dispatch, MouseEvent, SetStateAction, useEffect, useState } from 'react'
|
|
5
|
+
import { isEqual } from 'lodash'
|
|
6
|
+
import { VersionSelector } from './styled'
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
value?: number,
|
|
10
|
+
onChange: Dispatch<SetStateAction<number | undefined>>,
|
|
11
|
+
options?: number[],
|
|
12
|
+
lazyLoadOptions?: boolean,
|
|
13
|
+
useVersions?: (id: string, enabled: boolean) => number[] | undefined,
|
|
14
|
+
id: string,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const SelectVersion = ({ useVersions, value, onChange, options: initialOptions, id, lazyLoadOptions=false }: Props) => {
|
|
18
|
+
const t = useTranslate(dictionary)
|
|
19
|
+
const [options, setOptions] = useState(initialOptions)
|
|
20
|
+
const [enabled, setEnabled] = useState(!lazyLoadOptions)
|
|
21
|
+
const versions = useVersions?.(id, enabled)
|
|
22
|
+
|
|
23
|
+
const onClick = (e?: MouseEvent<HTMLDivElement>) => {
|
|
24
|
+
e?.stopPropagation()
|
|
25
|
+
if (lazyLoadOptions) {
|
|
26
|
+
setEnabled(true)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (lazyLoadOptions && !isEqual(versions, options)){
|
|
32
|
+
setOptions(versions)
|
|
33
|
+
}
|
|
34
|
+
}, [versions])
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<VersionSelector>
|
|
38
|
+
<Select className="version-selector"
|
|
39
|
+
options={options || []}
|
|
40
|
+
value={value}
|
|
41
|
+
onChange={onChange}
|
|
42
|
+
onClick={onClick}
|
|
43
|
+
renderLabel={(item) =>`${t.version} ${item}`} />
|
|
44
|
+
</VersionSelector>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const dictionary = {
|
|
49
|
+
en: {
|
|
50
|
+
version: 'Version',
|
|
51
|
+
},
|
|
52
|
+
pt: {
|
|
53
|
+
version: 'Versão',
|
|
54
|
+
},
|
|
55
|
+
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { FallbackBoundary, IconLink, Text } from '@stack-spot/citric-react'
|
|
2
2
|
import { useKeyboardControls } from '@stack-spot/portal-components'
|
|
3
|
+
import { InfiniteScroll } from '@stack-spot/portal-components/InfiniteScroll'
|
|
3
4
|
import { AgentVisibilityLevel } from '@stack-spot/portal-network'
|
|
5
|
+
import { VisibilityLevelEnum } from '@stack-spot/portal-network/api/ai'
|
|
4
6
|
import { Dictionary, interpolate, useTranslate } from '@stack-spot/portal-translate'
|
|
5
7
|
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
8
|
+
import { debounce } from 'lodash'
|
|
6
9
|
import { useCurrentChatState, useWidgetState } from '../../context/hooks'
|
|
7
10
|
import { getUrlToStackSpotAI } from '../../utils/url'
|
|
8
11
|
import { ButtonFavorite } from '../ButtonFavorite'
|
|
9
12
|
import { Fading } from '../Fading'
|
|
13
|
+
import { SelectVersion } from './SelectVersion'
|
|
10
14
|
import { SelectorBox } from './styled'
|
|
11
15
|
|
|
12
|
-
type SectionVisibility = AgentVisibilityLevel
|
|
16
|
+
type SectionVisibility = AgentVisibilityLevel | VisibilityLevelEnum
|
|
13
17
|
type SelectorShortcut = '/' | '@'
|
|
14
18
|
|
|
15
19
|
interface Item {
|
|
@@ -17,6 +21,7 @@ interface Item {
|
|
|
17
21
|
slug: string,
|
|
18
22
|
description: string,
|
|
19
23
|
visibility_level: SectionVisibility,
|
|
24
|
+
version_number?: number,
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
interface Favorite<T> {
|
|
@@ -53,6 +58,12 @@ interface ContentProps<T> {
|
|
|
53
58
|
favorite?: Favorite<T>,
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
interface PropsUseData<T> {
|
|
62
|
+
data: T[],
|
|
63
|
+
fetchNextPage?: () => void,
|
|
64
|
+
hasNextPage?: boolean,
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
interface SelectorConfig<T> {
|
|
57
68
|
resourceName: string,
|
|
58
69
|
shortcut: SelectorShortcut,
|
|
@@ -60,6 +71,8 @@ interface SelectorConfig<T> {
|
|
|
60
71
|
regex: RegExp,
|
|
61
72
|
urlBuilder: (item: T) => string,
|
|
62
73
|
searchProp: keyof T,
|
|
74
|
+
isEnabledVersionContent?: boolean,
|
|
75
|
+
useVersions?: (id: string, enabled: boolean) => number[] | undefined,
|
|
63
76
|
isEnabled: boolean,
|
|
64
77
|
sections: SectionVisibility[],
|
|
65
78
|
renderComponentItem: FC<T>,
|
|
@@ -71,7 +84,7 @@ interface SelectorConfig<T> {
|
|
|
71
84
|
*
|
|
72
85
|
* @returns the data to render in the selector.
|
|
73
86
|
*/
|
|
74
|
-
useData: () => T
|
|
87
|
+
useData: (props: {filter?: string, visibility?: SectionVisibility[]}) => PropsUseData<T>,
|
|
75
88
|
}
|
|
76
89
|
|
|
77
90
|
interface SelectorProps<T> {
|
|
@@ -82,10 +95,11 @@ interface SelectorProps<T> {
|
|
|
82
95
|
|
|
83
96
|
const ListItem = <T extends Item>({ item, selectorConfig, onSelect, favorite }: ListItemProps<T>) => {
|
|
84
97
|
const t = useTranslate(dictionary)
|
|
85
|
-
const { urlBuilder, renderComponentItem } = selectorConfig
|
|
98
|
+
const { urlBuilder, renderComponentItem, useVersions, isEnabledVersionContent } = selectorConfig
|
|
86
99
|
const linkTitle = interpolate(t.open, item.slug)
|
|
87
100
|
const listFavorites = favorite?.useFavorites?.()
|
|
88
|
-
|
|
101
|
+
const [selectedVersion, setSelectedVersion] = useState(item.version_number)
|
|
102
|
+
|
|
89
103
|
return (
|
|
90
104
|
<li>
|
|
91
105
|
<button
|
|
@@ -97,6 +111,10 @@ const ListItem = <T extends Item>({ item, selectorConfig, onSelect, favorite }:
|
|
|
97
111
|
>
|
|
98
112
|
{renderComponentItem(item)}
|
|
99
113
|
</button>
|
|
114
|
+
{isEnabledVersionContent &&
|
|
115
|
+
<SelectVersion options={item.version_number ? [item.version_number] : []} id={item.id}
|
|
116
|
+
value={selectedVersion} onChange={setSelectedVersion} lazyLoadOptions={true} useVersions={useVersions}
|
|
117
|
+
/> }
|
|
100
118
|
<IconLink
|
|
101
119
|
icon="ExternalLink"
|
|
102
120
|
title={linkTitle}
|
|
@@ -114,39 +132,27 @@ const ListItem = <T extends Item>({ item, selectorConfig, onSelect, favorite }:
|
|
|
114
132
|
|
|
115
133
|
const List = <T extends Item>({ selectorConfig, filter, visibility, onSelect, favorite }: ListProps<T>) => {
|
|
116
134
|
const t = useTranslate(dictionary)
|
|
117
|
-
const items = selectorConfig.useData()
|
|
118
|
-
const favorites = favorite?.useFavorites?.()
|
|
119
|
-
const filtered = useMemo(() => {
|
|
120
|
-
if (!filter && !visibility) return [...items]
|
|
121
|
-
const lowerFilter = filter?.toLowerCase() ?? ''
|
|
122
|
-
|
|
123
|
-
if (visibility?.toLowerCase() === 'favorite'){
|
|
124
|
-
return favorites?.filter(item =>
|
|
125
|
-
(item[selectorConfig.searchProp] as string).toLocaleLowerCase().includes(lowerFilter))
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return items.filter(item =>
|
|
129
|
-
(item[selectorConfig.searchProp] as string).toLocaleLowerCase().includes(lowerFilter) &&
|
|
130
|
-
(!visibility || item?.visibility_level?.toLowerCase() === visibility.toLowerCase()),
|
|
131
|
-
)
|
|
132
|
-
}, [filter, items, visibility, favorites])
|
|
135
|
+
const { data: items, fetchNextPage, hasNextPage } = selectorConfig.useData({ filter, visibility: visibility ? [visibility] : undefined })
|
|
133
136
|
|
|
134
|
-
|
|
135
|
-
if (!items
|
|
136
|
-
if (!filtered?.length)
|
|
137
|
-
<Text className="empty" color="light.700">{interpolate(t.noResults, selectorConfig.resourceName)}</Text>
|
|
137
|
+
if (!items.length && !filter) return <Text className="empty" color="light.700">{interpolate(t.noData, selectorConfig.resourceName)}</Text>
|
|
138
|
+
if (!items?.length) return <Text className="empty" color="light.700">{interpolate(t.noResults, selectorConfig.resourceName)}</Text>
|
|
138
139
|
|
|
139
140
|
return (
|
|
140
|
-
<ul className="selector-list">
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
<ul className="selector-list" id="selector-list">
|
|
142
|
+
<InfiniteScroll scrollableTarget="selector-list"
|
|
143
|
+
dataLength={items?.length ?? 0}
|
|
144
|
+
next={fetchNextPage as any ?? undefined}
|
|
145
|
+
hasMore={hasNextPage ?? false}>
|
|
146
|
+
{items?.map((item, i) => (
|
|
147
|
+
<ListItem
|
|
148
|
+
key={`${item.id}${item.visibility_level}${i}`}
|
|
149
|
+
item={item}
|
|
150
|
+
selectorConfig={selectorConfig}
|
|
151
|
+
onSelect={onSelect}
|
|
152
|
+
favorite={favorite}
|
|
153
|
+
/>
|
|
154
|
+
))}
|
|
155
|
+
</InfiniteScroll>
|
|
150
156
|
</ul>
|
|
151
157
|
)
|
|
152
158
|
}
|
|
@@ -206,9 +212,25 @@ export const Selector = <T, >({ inputRef, selectorConfig, favorite }: SelectorPr
|
|
|
206
212
|
const { shortcut, regex, isEnabled } = selectorConfig
|
|
207
213
|
const [isClosed, setClosed] = useState(false)
|
|
208
214
|
const selectorRef = useRef<HTMLDivElement>(null)
|
|
215
|
+
const [debouncedFilter, setDebouncedFilter] = useState<string | undefined>(undefined)
|
|
209
216
|
const value = useCurrentChatState('nextMessage') ?? ''
|
|
210
217
|
const filter = useMemo(() => value === shortcut || regex.test(value) ? value.substring(1) : undefined, [value])
|
|
211
218
|
const shouldRender = isEnabled && filter !== undefined && !isClosed
|
|
219
|
+
|
|
220
|
+
const debouncedSetFilter = useCallback(
|
|
221
|
+
debounce((filterValue: string | undefined) => {
|
|
222
|
+
setDebouncedFilter(filterValue)
|
|
223
|
+
}, 300),
|
|
224
|
+
[],
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
debouncedSetFilter(filter)
|
|
229
|
+
return () => {
|
|
230
|
+
debouncedSetFilter.cancel()
|
|
231
|
+
}
|
|
232
|
+
}, [filter, debouncedSetFilter])
|
|
233
|
+
|
|
212
234
|
|
|
213
235
|
// Resets the closed state whenever the message input is cleared
|
|
214
236
|
useEffect(() => {
|
|
@@ -258,7 +280,7 @@ export const Selector = <T, >({ inputRef, selectorConfig, favorite }: SelectorPr
|
|
|
258
280
|
<SelectorContent
|
|
259
281
|
favorite={favorite}
|
|
260
282
|
selectorConfig={selectorConfig}
|
|
261
|
-
filter={
|
|
283
|
+
filter={debouncedFilter}
|
|
262
284
|
onClose={() => setClosed(true)}
|
|
263
285
|
inputRef={inputRef}
|
|
264
286
|
/>
|
|
@@ -156,3 +156,51 @@ export const SelectorBox = styled.div<{ $tabsCount: number }>`
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
`
|
|
159
|
+
|
|
160
|
+
export const VersionSelector = styled.div`
|
|
161
|
+
position: relative;
|
|
162
|
+
min-width: 100px;
|
|
163
|
+
|
|
164
|
+
.version-selector {
|
|
165
|
+
height: 32px;
|
|
166
|
+
position: absolute;
|
|
167
|
+
top: -18px;
|
|
168
|
+
left: -12px;
|
|
169
|
+
|
|
170
|
+
header {
|
|
171
|
+
height: 20px;
|
|
172
|
+
background-color: ${theme.color.light[300]};
|
|
173
|
+
border: none;
|
|
174
|
+
margin-bottom: 0;
|
|
175
|
+
gap: 0;
|
|
176
|
+
padding: 0;
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
.selection-panel .options{
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
li {
|
|
183
|
+
gap: 5px;
|
|
184
|
+
padding: 0 8px 0 !important;
|
|
185
|
+
padding-left: 5px;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
`
|
|
190
|
+
|
|
191
|
+
export const VersionSelectorBox = styled.div`
|
|
192
|
+
border: 1px solid ${theme.color.light[500]};
|
|
193
|
+
border-radius: 4px;
|
|
194
|
+
.version-selector {
|
|
195
|
+
/* top: -16px; */
|
|
196
|
+
position: relative;
|
|
197
|
+
top: 0;
|
|
198
|
+
left: 0;
|
|
199
|
+
}
|
|
200
|
+
> div {
|
|
201
|
+
position: relative;
|
|
202
|
+
.options {
|
|
203
|
+
overflow: hidden;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { accountClient } from '@stack-spot/portal-network'
|
|
2
|
+
|
|
3
|
+
const useEnabledFeatureFlags = (resourceType?: string, resourceSlug?: string) => {
|
|
4
|
+
const queryParams = resourceType && resourceSlug ? { [resourceType]: resourceSlug } : {}
|
|
5
|
+
const [featureFlags, , error, { isLoading }] = accountClient.getEnabledFeatureFlagsForAccount.useStatefulQuery({
|
|
6
|
+
queryParams,
|
|
7
|
+
})
|
|
8
|
+
return { featureFlags, isLoading, error }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useIsFeatureFlagEnabled = (
|
|
12
|
+
flagSlug: string,
|
|
13
|
+
resourceType?: string,
|
|
14
|
+
resourceSlug?: string,
|
|
15
|
+
) => {
|
|
16
|
+
const { featureFlags, isLoading } = useEnabledFeatureFlags(resourceType, resourceSlug)
|
|
17
|
+
if (resourceType && resourceSlug) {
|
|
18
|
+
const featureFlag = featureFlags?.find((flag) => flag.slug === flagSlug)
|
|
19
|
+
const resourcesByType = featureFlag?.resources?.[resourceType]
|
|
20
|
+
if (!!featureFlag && !!resourcesByType) {
|
|
21
|
+
if (resourcesByType.mode === 'ALL') {
|
|
22
|
+
return { flagEnabled: true, isLoading }
|
|
23
|
+
} else {
|
|
24
|
+
return { flagEnabled: !!featureFlag.resources?.[resourceType].slugs.some((slug) => slug === resourceSlug), isLoading }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { flagEnabled: false, isLoading }
|
|
28
|
+
} else {
|
|
29
|
+
return { flagEnabled: !!featureFlags?.some((flag) => flag.slug === flagSlug), isLoading }
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/state/types.ts
CHANGED
package/src/types.ts
CHANGED
package/src/utils/tools.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
export type ToolWithImage = { id?: string, image?: string, name
|
|
2
|
+
export type ToolWithImage = { id?: string | null, image?: string, name?: string, description?: string }
|
|
3
3
|
|
|
4
4
|
interface Toolkit {
|
|
5
5
|
id?: string | null,
|
|
6
|
-
tools?: { id?: string, name?: string, description?: string, function?: { name: string, description
|
|
6
|
+
tools?: { id?: string | null, name?: string, description?: string, function?: { name: string, description: string } | null }[] | null,
|
|
7
7
|
image_url?: string | null,
|
|
8
8
|
avatar?: string | null,
|
|
9
9
|
}
|
|
@@ -22,8 +22,8 @@ export function toolById(id: string, toolkits: Toolkit[] | undefined): ToolWithI
|
|
|
22
22
|
if (mcp) {
|
|
23
23
|
const [, toolkitId, toolName] = mcp
|
|
24
24
|
const toolkit = toolkits?.find(tk => tk.id === toolkitId)
|
|
25
|
-
const tool = toolkit?.tools?.find(t => t.name === toolName)
|
|
26
|
-
return { id, image: toolkit?.avatar ?? undefined, name:
|
|
25
|
+
const tool = toolkit?.tools?.find(t => t.name === toolName || t.function?.name === toolName)
|
|
26
|
+
return { id, image: toolkit?.avatar ?? undefined, name: tool?.function?.name, description: tool?.function?.description }
|
|
27
27
|
}
|
|
28
28
|
const { tool, toolkit } = findToolById(id, toolkits ?? []) ?? {}
|
|
29
29
|
return (tool && toolkit)
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { Icon } from '@stack-spot/citric-icons'
|
|
2
|
-
import { AsyncContent, Badge, Card, IconBox, ImageBox, ImageWithFallback, Text } from '@stack-spot/citric-react'
|
|
2
|
+
import { AsyncContent, Badge, Card, IconBox, ImageBox, ImageWithFallback, Row, Text } from '@stack-spot/citric-react'
|
|
3
3
|
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
4
|
-
import { useMemo } from 'react'
|
|
4
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
5
|
+
import { SelectVersion } from '../../components/Selector/SelectVersion'
|
|
6
|
+
import { VersionSelectorBox } from '../../components/Selector/styled'
|
|
5
7
|
import { useAgentsDictionary } from './dictionary'
|
|
6
8
|
import { AgentDescriptionBox } from './styled'
|
|
7
9
|
|
|
8
|
-
export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
10
|
+
export const AgentDescription = ({ agentId, enableVersionSelect }: { agentId?: string, enableVersionSelect?: boolean }) => {
|
|
9
11
|
const t = useAgentsDictionary()
|
|
10
|
-
const [
|
|
11
|
-
const
|
|
12
|
+
const [selectedVersion, setSelectedVersion] = useState<number | undefined>()
|
|
13
|
+
const [agent,, error, { isLoading }] = agentToolsClient.agentV2.useStatefulQuery({ agentCoreId: agentId!,
|
|
14
|
+
versionNumber: selectedVersion }, { enabled: !!agentId })
|
|
15
|
+
const [listVersions, , errorAgentWithVersion, { isLoading: isLoadingAgentWithVersions }] =
|
|
16
|
+
agentToolsClient.listAgentVersions.useStatefulQuery({ agentCoreId: agentId! },
|
|
17
|
+
{ enabled: enableVersionSelect && !!agentId })
|
|
18
|
+
const optionsVersions = listVersions?.map((version) => version.version_number)
|
|
19
|
+
const numberOfKnowledgeSources = agent?.version.knowledge_sources_config?.knowledge_sources.length ?? 0
|
|
12
20
|
|
|
13
21
|
const knowledgeSources = useMemo(
|
|
14
|
-
() => agent?.knowledge_sources_config?.knowledge_sources_details?.map((ks, index) => (
|
|
22
|
+
() => agent?.version.knowledge_sources_config?.knowledge_sources_details?.map((ks, index) => (
|
|
15
23
|
<li key={index}>
|
|
16
24
|
<Card gap="10px" direction="row" flex={1} size="xxs" bgLevel={500} justifyContent="space-between">
|
|
17
25
|
<Text color="light.contrastText">{ks.name}</Text>
|
|
@@ -21,12 +29,12 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
21
29
|
)),
|
|
22
30
|
[agent],
|
|
23
31
|
)
|
|
24
|
-
|
|
32
|
+
|
|
25
33
|
const { tools, multiAgents } = useMemo(() => {
|
|
26
34
|
const tools: React.ReactElement[] = []
|
|
27
35
|
const multiAgents: React.ReactElement[] = []
|
|
28
|
-
const builtInTools = agent?.toolkits?.builtin_toolkits ?? []
|
|
29
|
-
const customToolkits = agent?.toolkits?.custom_toolkits ?? []
|
|
36
|
+
const builtInTools = agent?.version.toolkits?.builtin_toolkits ?? []
|
|
37
|
+
const customToolkits = agent?.version.toolkits?.custom_toolkits ?? []
|
|
30
38
|
for (const toolkit of builtInTools) {
|
|
31
39
|
for (const tool of toolkit.tools ?? []) {
|
|
32
40
|
if (toolkit.id == 'UTILITIES'){
|
|
@@ -66,22 +74,36 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
66
74
|
return { tools, multiAgents }
|
|
67
75
|
}, [agent])
|
|
68
76
|
|
|
69
|
-
const mcpToolkits = useMemo(() => agent?.toolkits?.mcp_toolkits?.map(t => (
|
|
77
|
+
const mcpToolkits = useMemo(() => agent?.version.toolkits?.mcp_toolkits?.map(t => (
|
|
70
78
|
<li key={`mcp-${t.id}`}>
|
|
71
79
|
<Card gap="10px" direction="row" flex={1} size="xxs" bgLevel={500}>
|
|
72
80
|
<ImageBox><ImageWithFallback src={t.avatar ?? undefined} fallback={<Icon icon="Cog" />} /></ImageBox>
|
|
73
81
|
<Text color="light.contrastText">{t.name}</Text>
|
|
74
82
|
</Card>
|
|
75
83
|
</li>
|
|
76
|
-
)), [agent?.toolkits?.mcp_toolkits])
|
|
84
|
+
)), [agent?.version.toolkits?.mcp_toolkits])
|
|
77
85
|
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!selectedVersion && agent?.version.version_number !== selectedVersion){
|
|
88
|
+
setSelectedVersion(agent?.version.version_number)
|
|
89
|
+
}
|
|
90
|
+
}, [agent?.version.version_number])
|
|
91
|
+
|
|
78
92
|
return (
|
|
79
|
-
<AsyncContent loading={isLoading} error={error}>
|
|
93
|
+
<AsyncContent loading={isLoading || isLoadingAgentWithVersions} error={error || errorAgentWithVersion}>
|
|
80
94
|
<AgentDescriptionBox>
|
|
81
95
|
{agent?.description && <section>
|
|
82
96
|
<Text appearance="microtext1" className="title">{t.description}</Text>
|
|
83
97
|
<Text>{agent?.description}</Text>
|
|
84
98
|
</section>}
|
|
99
|
+
{enableVersionSelect && optionsVersions && optionsVersions?.length > 0 && <section>
|
|
100
|
+
<VersionSelectorBox>
|
|
101
|
+
<Row justifyContent="space-between" p={4}>
|
|
102
|
+
<Text>{t.version}</Text>
|
|
103
|
+
<SelectVersion options={optionsVersions} value={selectedVersion} onChange={setSelectedVersion} id={agent?.id ?? ''} />
|
|
104
|
+
</Row>
|
|
105
|
+
</VersionSelectorBox>
|
|
106
|
+
</section>}
|
|
85
107
|
{(!!numberOfKnowledgeSources || !!knowledgeSources?.length) && <section>
|
|
86
108
|
<Text appearance="microtext1" className="title">Knowledge sources</Text>
|
|
87
109
|
<ul>{knowledgeSources}</ul>
|
|
@@ -97,9 +119,9 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
97
119
|
<Text appearance="microtext1" className="title">{t.multiAgent}</Text>
|
|
98
120
|
<ul>{multiAgents}</ul>
|
|
99
121
|
</section>}
|
|
100
|
-
{agent?.model_name && <section>
|
|
122
|
+
{agent?.version.model_name && <section>
|
|
101
123
|
<Text appearance="microtext1" className="title">LLM</Text>
|
|
102
|
-
<Badge colorPalette="orange" appearance="square">{agent?.model_name}</Badge>
|
|
124
|
+
<Badge colorPalette="orange" appearance="square">{agent?.version.model_name}</Badge>
|
|
103
125
|
</section>}
|
|
104
126
|
</AgentDescriptionBox>
|
|
105
127
|
</AsyncContent>
|
|
@@ -9,6 +9,7 @@ import { NavigationComponent } from '../../components/ComponentNavigator'
|
|
|
9
9
|
import { DescribedRadioGroup } from '../../components/form/DescribedRadioGroup'
|
|
10
10
|
import { WorkspaceTabNavigator } from '../../components/WorkspaceTabNavigator'
|
|
11
11
|
import { useCurrentChat } from '../../context/hooks'
|
|
12
|
+
import { useIsFeatureFlagEnabled } from '../../hooks/enabled-feature-flags'
|
|
12
13
|
import { useRightPanel } from '../../right-panel/hooks'
|
|
13
14
|
import { ChatProperties } from '../../state/ChatState'
|
|
14
15
|
import { AgentDescription } from './AgentDescription'
|
|
@@ -28,6 +29,7 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
28
29
|
const chat = useCurrentChat()
|
|
29
30
|
const { useFavorites, onAddFavorite, onRemoveFavorite } = useAgentFavorites()
|
|
30
31
|
const [submitEnabled, setSubmitEnabled] = useState(false)
|
|
32
|
+
const featureFlag = useIsFeatureFlagEnabled('ENABLE_VERSION_CONTENT_AI')
|
|
31
33
|
const listFavorites = useFavorites()
|
|
32
34
|
const agentDefault = agentToolsClient.agentDefault.useQuery()
|
|
33
35
|
|
|
@@ -59,15 +61,15 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
59
61
|
|
|
60
62
|
const agents = workspaceId ? workspaceAgents : agentsData
|
|
61
63
|
|
|
62
|
-
const initialValue = useMemo<AgentResponseWithBuiltIn | undefined>(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
const initialValue = useMemo<AgentResponseWithBuiltIn | undefined>(() => {
|
|
65
|
+
const initial = agent.current
|
|
66
|
+
? agents?.find(a => a.id === agent.current?.id)
|
|
67
|
+
: chat.get('agent') ?
|
|
68
|
+
agents?.find(a => a.id === chat.get('agent')?.id) : { ...agentDefault, suggested_prompts: null } as AgentResponseWithBuiltIn
|
|
69
|
+
if (initial && !submitEnabled) setSubmitEnabled(true)
|
|
70
|
+
return initial
|
|
71
|
+
},
|
|
72
|
+
[agents],
|
|
71
73
|
)
|
|
72
74
|
|
|
73
75
|
function submit() {
|
|
@@ -80,7 +82,7 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
80
82
|
<DescribedRadioGroup
|
|
81
83
|
fetchNextPage={fetchNextPage}
|
|
82
84
|
hasNextPage={hasNextPage && !workspaceId}
|
|
83
|
-
options={agents}
|
|
85
|
+
options={agents || []}
|
|
84
86
|
initialValue={initialValue}
|
|
85
87
|
filter={workspaceId ? undefined : filter}
|
|
86
88
|
setFilter={workspaceId ? undefined : (value?: string) => {
|
|
@@ -98,7 +100,7 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
98
100
|
idOrSlug: ag.id,
|
|
99
101
|
// below, "key" is important. I don't know why exactly, but sometimes the avatar doesn't update if we don't do this.
|
|
100
102
|
image: <ImageWithFallback key={ag.id} src={ag.avatar ?? undefined} fallback={<Icon icon="Agent" />} />,
|
|
101
|
-
description: <AgentDescription agentId={ag.id} />,
|
|
103
|
+
description: <AgentDescription agentId={ag.id} enableVersionSelect={featureFlag.flagEnabled} />,
|
|
102
104
|
name: ag.name,
|
|
103
105
|
listFavorites,
|
|
104
106
|
onAddFavorite,
|
|
@@ -19,6 +19,7 @@ const dictionary = {
|
|
|
19
19
|
tools: 'Tools',
|
|
20
20
|
spots: 'Spots',
|
|
21
21
|
multiAgent: 'Multi-agents',
|
|
22
|
+
version: 'Version',
|
|
22
23
|
},
|
|
23
24
|
pt: {
|
|
24
25
|
title: 'Agentes',
|
|
@@ -38,7 +39,7 @@ const dictionary = {
|
|
|
38
39
|
tools: 'Ferramentas',
|
|
39
40
|
spots: 'Spots',
|
|
40
41
|
multiAgent: 'Multi-agents',
|
|
41
|
-
|
|
42
|
+
version: 'Versão',
|
|
42
43
|
},
|
|
43
44
|
} satisfies Dictionary
|
|
44
45
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LoadingCircular } from '@citric/ui'
|
|
2
2
|
import { Button, Icon, Row } from '@stack-spot/citric-react'
|
|
3
3
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
4
|
-
import { useEffect } from 'react'
|
|
4
|
+
import { useEffect, useMemo } from 'react'
|
|
5
5
|
import { useChatMessages, useWidget } from '../../context/hooks'
|
|
6
6
|
|
|
7
7
|
export const ButtonExecutionDetail = ({ chatId, messageId }: { chatId: string, messageId: number }) => {
|
|
@@ -9,10 +9,10 @@ export const ButtonExecutionDetail = ({ chatId, messageId }: { chatId: string, m
|
|
|
9
9
|
const messages = useChatMessages(chatId)
|
|
10
10
|
const widget = useWidget()
|
|
11
11
|
|
|
12
|
-
const isRunning = () => {
|
|
13
|
-
const messageEntry = messages
|
|
12
|
+
const isRunning = useMemo(() => {
|
|
13
|
+
const messageEntry = messages?.find((message) => message.id === messageId)
|
|
14
14
|
return !messageEntry?.getValue().steps?.find((step) => step.type === 'answer' && step.status !== 'running')
|
|
15
|
-
}
|
|
15
|
+
}, [messageId, messages])
|
|
16
16
|
|
|
17
17
|
function openToolsPanel() {
|
|
18
18
|
if (messageId) {
|
|
@@ -22,12 +22,12 @@ export const ButtonExecutionDetail = ({ chatId, messageId }: { chatId: string, m
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
useEffect(() => {
|
|
25
|
-
isRunning
|
|
25
|
+
isRunning && openToolsPanel()
|
|
26
26
|
}, [isRunning])
|
|
27
27
|
|
|
28
28
|
return <>
|
|
29
29
|
<Row className="step-actions">
|
|
30
|
-
{isRunning
|
|
30
|
+
{isRunning && <LoadingCircular colorScheme="inverse" size="xs" />}
|
|
31
31
|
<Button colorScheme="light" size="sm" appearance="none" className="icon-button details" onClick={openToolsPanel}>
|
|
32
32
|
<Icon group="outline" icon="Expand" size="xs" />
|
|
33
33
|
{t.detailed}
|
|
@@ -227,8 +227,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
227
227
|
const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
|
|
228
228
|
const isPlanning = useCurrentChatState('isPlaning') ?? false
|
|
229
229
|
|
|
230
|
-
// Dynamic
|
|
231
|
-
// We're temporarily hiding the toolbox for these dynamic tools while we finalize their UI.
|
|
230
|
+
// Dynamic steps are identified by the "dynamic" id.
|
|
232
231
|
const isDynamicSteps = !!entry?.steps?.some((s) => s.id?.toLowerCase() === 'dynamic')
|
|
233
232
|
const hasTools = ((agentsTools?.length ?? 0) > 0) || ((entry.tools?.length ?? 0) > 0)
|
|
234
233
|
const showToolBox = hasTools && !isDynamicSteps
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agentToolsClient, aiClient, AnswerChatStep, ChatAgentTool, ChatStep, PlanningChatStep, workspaceClient } from '@stack-spot/portal-network'
|
|
2
|
-
import {
|
|
2
|
+
import { FindByIdAgentResponse } from '@stack-spot/portal-network/api/agent-tools'
|
|
3
3
|
import { last } from 'lodash'
|
|
4
4
|
import { ChatEntry } from '../../state/ChatEntry'
|
|
5
5
|
import { ChatProperties, ChatState } from '../../state/ChatState'
|
|
@@ -56,7 +56,7 @@ function toJSONString(data: any) {
|
|
|
56
56
|
return `${data}`
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function findTool(agent:
|
|
59
|
+
function findTool(agent: FindByIdAgentResponse | undefined, id: string) {
|
|
60
60
|
const allToolkits = [
|
|
61
61
|
...agent?.toolkits?.builtin_toolkits ?? [],
|
|
62
62
|
...agent?.toolkits?.custom_toolkits ?? [],
|
|
@@ -75,7 +75,7 @@ async function stepsFromAgentInfo(
|
|
|
75
75
|
): Promise<ChatStep[] | undefined> {
|
|
76
76
|
const planningInfo = agentInfo?.find(i => i.type === 'planning' && !!i.data)
|
|
77
77
|
if (planningInfo) {
|
|
78
|
-
let agent:
|
|
78
|
+
let agent: FindByIdAgentResponse | undefined
|
|
79
79
|
try {
|
|
80
80
|
agent = agentId ? await agentToolsClient.agent.query({ agentId }) : undefined
|
|
81
81
|
} catch { /* empty */ }
|
|
@@ -105,7 +105,7 @@ async function stepsFromAgentInfo(
|
|
|
105
105
|
return {
|
|
106
106
|
id: t.tool_id,
|
|
107
107
|
executionId: t.tool_execution_id,
|
|
108
|
-
name: tool?.name,
|
|
108
|
+
name: ('name' in tool ? tool.name : ('function' in tool ? tool?.function?.name : undefined) ?? ''),
|
|
109
109
|
description: 'description' in tool ? tool.description : undefined,
|
|
110
110
|
image: toolkit ? (('image_url' in toolkit ? toolkit?.image_url : toolkit?.avatar) ?? undefined) : undefined,
|
|
111
111
|
goal: t.goal,
|