@stack-spot/ai-chat-widget 3.3.2-beta.5 → 3.3.4-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 +53 -16
- 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 +40 -1
- package/dist/chat-interceptors/quick-commands.js.map +1 -1
- package/dist/chat-interceptors/send-message.d.ts +1 -1
- package/dist/chat-interceptors/send-message.d.ts.map +1 -1
- package/dist/chat-interceptors/send-message.js +25 -29
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/form/DescribedRadioGroup.d.ts +6 -1
- package/dist/components/form/DescribedRadioGroup.d.ts.map +1 -1
- package/dist/components/form/DescribedRadioGroup.js +43 -19
- package/dist/components/form/DescribedRadioGroup.js.map +1 -1
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
- package/dist/views/Agents/AgentsTab.js +30 -10
- package/dist/views/Agents/AgentsTab.js.map +1 -1
- package/dist/views/Agents/useAgentFavorites.d.ts +1 -1
- package/dist/views/Agents/useAgentFavorites.js +3 -3
- package/dist/views/Agents/useAgentFavorites.js.map +1 -1
- package/dist/views/Chat/ChatMessage.js +1 -1
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/StepsList.d.ts +2 -1
- package/dist/views/Chat/StepsList.d.ts.map +1 -1
- package/dist/views/Chat/StepsList.js +8 -6
- package/dist/views/Chat/StepsList.js.map +1 -1
- package/dist/views/MessageInput/ModelSwitcher/index.d.ts.map +1 -1
- package/dist/views/MessageInput/ModelSwitcher/index.js +1 -1
- package/dist/views/MessageInput/ModelSwitcher/index.js.map +1 -1
- package/dist/views/Steps/dictionary.d.ts +1 -1
- package/package.json +2 -2
- package/src/app-metadata.json +3 -3
- package/src/chat-interceptors/quick-commands.ts +60 -5
- package/src/chat-interceptors/send-message.ts +25 -30
- package/src/components/form/DescribedRadioGroup.tsx +94 -41
- package/src/index.ts +0 -1
- package/src/views/Agents/AgentsTab.tsx +69 -37
- package/src/views/Agents/useAgentFavorites.ts +3 -3
- package/src/views/Chat/ChatMessage.tsx +1 -1
- package/src/views/Chat/StepsList.tsx +9 -7
- package/src/views/MessageInput/ModelSwitcher/index.tsx +2 -1
|
@@ -47,22 +47,17 @@ function buildPrompt(content: string, data?: any) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
//Verify if the last planning in the messages has status awaiting_approval
|
|
50
|
-
const getLastPlanningAwaiting = (
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return false
|
|
60
|
-
})
|
|
61
|
-
}
|
|
50
|
+
const getLastPlanningAwaiting = (messages: ChatEntry[]) => findLast(messages, item => {
|
|
51
|
+
const steps = item.getValue().steps
|
|
52
|
+
if (steps) {
|
|
53
|
+
const hasPlanning = steps.find((step) => step.type === 'planning')
|
|
54
|
+
return hasPlanning ? hasPlanning.status === 'awaiting_approval' : false
|
|
55
|
+
}
|
|
56
|
+
return false
|
|
57
|
+
})
|
|
62
58
|
|
|
63
|
-
const updateToolStatus = (agentInfo: AgentInfo,
|
|
59
|
+
const updateToolStatus = (agentInfo: AgentInfo, messages: ChatEntry[]) => {
|
|
64
60
|
const executionId = agentInfo.id
|
|
65
|
-
const messages = chat.getMessages()
|
|
66
61
|
|
|
67
62
|
if (executionId) {
|
|
68
63
|
//Update message with type step which contains the planning steps
|
|
@@ -100,9 +95,8 @@ const updateToolStatus = (agentInfo: AgentInfo, chat: ChatState) => {
|
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
97
|
|
|
103
|
-
const updateStepMessage = (step: ChatStep,
|
|
104
|
-
const lastPlanningAwaiting = getLastPlanningAwaiting(
|
|
105
|
-
const messages = chat.getMessages()
|
|
98
|
+
const updateStepMessage = (step: ChatStep, messages: ChatEntry[]) => {
|
|
99
|
+
const lastPlanningAwaiting = getLastPlanningAwaiting(messages)
|
|
106
100
|
|
|
107
101
|
if (lastPlanningAwaiting) {
|
|
108
102
|
const originalItem = messages.find((message) => message.id === lastPlanningAwaiting.id)
|
|
@@ -117,9 +111,8 @@ const updateStepMessage = (step: ChatStep, chat: ChatState) => {
|
|
|
117
111
|
}
|
|
118
112
|
|
|
119
113
|
|
|
120
|
-
const updatePlanningMessage = (
|
|
121
|
-
const lastPlanningAwaiting = getLastPlanningAwaiting(
|
|
122
|
-
const messages = chat.getMessages()
|
|
114
|
+
const updatePlanningMessage = (messages: ChatEntry[]) => {
|
|
115
|
+
const lastPlanningAwaiting = getLastPlanningAwaiting(messages)
|
|
123
116
|
|
|
124
117
|
if (lastPlanningAwaiting) {
|
|
125
118
|
const originalItem = messages.find((message) => message.id === lastPlanningAwaiting.id)
|
|
@@ -133,9 +126,8 @@ const updatePlanningMessage = (chat: ChatState) => {
|
|
|
133
126
|
}
|
|
134
127
|
}
|
|
135
128
|
|
|
136
|
-
export function helperSendMessage(value: Partial<ChatResponseWithSteps> & { opportunities?: any },
|
|
129
|
+
export function helperSendMessage(messages: ChatEntry[], value: Partial<ChatResponseWithSteps> & { opportunities?: any },
|
|
137
130
|
chat: ChatState, botEntry: ChatEntry, knowledgeSources: KnowledgeSource[] | undefined) {
|
|
138
|
-
const messages = chat.getMessages()
|
|
139
131
|
if (value.agent_info?.type === 'planning') {
|
|
140
132
|
if (value.agent_info.action === 'start') {
|
|
141
133
|
chat.set('isPlaning', true)
|
|
@@ -156,19 +148,19 @@ export function helperSendMessage(value: Partial<ChatResponseWithSteps> & { oppo
|
|
|
156
148
|
knowledgeSources = genericSourcesToKnowledgeSources(value.sources)
|
|
157
149
|
}
|
|
158
150
|
|
|
159
|
-
const lastPlanningAwaiting = getLastPlanningAwaiting(
|
|
151
|
+
const lastPlanningAwaiting = getLastPlanningAwaiting(messages)
|
|
160
152
|
if (lastPlanningAwaiting && value.steps) {
|
|
161
153
|
value.steps?.map(step => {
|
|
162
154
|
if (step.type === 'planning') {
|
|
163
|
-
updatePlanningMessage(
|
|
155
|
+
updatePlanningMessage(messages)
|
|
164
156
|
} else if (step.type === 'step') {
|
|
165
|
-
updateStepMessage(step,
|
|
157
|
+
updateStepMessage(step, messages)
|
|
166
158
|
}
|
|
167
159
|
})
|
|
168
160
|
}
|
|
169
161
|
|
|
170
162
|
if (value.agent_info?.type === 'tool' && value.agent_info?.action !== 'awaiting_approval') {
|
|
171
|
-
updateToolStatus(value.agent_info,
|
|
163
|
+
updateToolStatus(value.agent_info, messages)
|
|
172
164
|
}
|
|
173
165
|
|
|
174
166
|
if (value.steps) {
|
|
@@ -219,7 +211,8 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
|
|
|
219
211
|
const context = buildConversationContext(chat, entry)
|
|
220
212
|
chat.set('isLoading', true)
|
|
221
213
|
const untitled = chat.untitled
|
|
222
|
-
const
|
|
214
|
+
const messages = chat.getMessages()
|
|
215
|
+
const isFirstMessage = messages.length === 1
|
|
223
216
|
if (untitled) {
|
|
224
217
|
chat.set('label', content || entry.getValue().upload?.[0]?.name || 'Chat')
|
|
225
218
|
chat.untitled = false
|
|
@@ -234,13 +227,15 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
|
|
|
234
227
|
}
|
|
235
228
|
let knowledgeSources: KnowledgeSource[] | undefined
|
|
236
229
|
|
|
237
|
-
stream.onChange(value =>
|
|
230
|
+
stream.onChange(value => {
|
|
231
|
+
helperSendMessage(messages, value, chat, botEntry, knowledgeSources)
|
|
232
|
+
})
|
|
238
233
|
|
|
239
234
|
let finalValue: Partial<ChatResponse3> | undefined
|
|
240
235
|
try {
|
|
241
236
|
finalValue = await stream.getValue()
|
|
242
237
|
|
|
243
|
-
const lastPlanningAwaiting = getLastPlanningAwaiting(
|
|
238
|
+
const lastPlanningAwaiting = getLastPlanningAwaiting(messages)
|
|
244
239
|
if (lastPlanningAwaiting) {
|
|
245
240
|
const value = lastPlanningAwaiting.getValue()
|
|
246
241
|
value.content = finalValue.answer || value.content
|
|
@@ -264,7 +259,7 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
|
|
|
264
259
|
}
|
|
265
260
|
}
|
|
266
261
|
if (finalValue?.answer) {
|
|
267
|
-
botEntry.setValue(createEntryValueFromChatResponse(finalValue, knowledgeSources, chat.get('agent'), true))
|
|
262
|
+
botEntry.setValue(createEntryValueFromChatResponse(finalValue, botEntry.getValue().knowledgeSources, chat.get('agent'), true))
|
|
268
263
|
aiClient.chat.invalidate({ conversationId: chat.id })
|
|
269
264
|
if (isFirstMessage) {
|
|
270
265
|
// if the chat has a title and this was its first message, we need to rename it according to the title, otherwise, the backend will
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { LoadingCircular } from '@citric/ui'
|
|
1
2
|
import { Icon } from '@stack-spot/citric-icons'
|
|
2
|
-
import { Accordion, FieldGroup, ImageBox, Input, RadioGroup, Row, Text, useRadioGroupControls } from '@stack-spot/citric-react'
|
|
3
|
+
import { Accordion, Column, FieldGroup, ImageBox, Input, RadioGroup, Row, Text, useRadioGroupControls } from '@stack-spot/citric-react'
|
|
4
|
+
import { InfiniteScroll } from '@stack-spot/portal-components/InfiniteScroll'
|
|
5
|
+
import { isEqual } from 'lodash'
|
|
3
6
|
import { useEffect, useState } from 'react'
|
|
7
|
+
import styled from 'styled-components'
|
|
4
8
|
import { ButtonFavorite, Favorite } from '../ButtonFavorite'
|
|
5
9
|
|
|
6
10
|
interface Props<T> {
|
|
@@ -15,15 +19,27 @@ interface Props<T> {
|
|
|
15
19
|
emptyResults: React.ReactNode,
|
|
16
20
|
emptyDataset: React.ReactNode,
|
|
17
21
|
onChange?: (value: T | undefined) => void,
|
|
22
|
+
fetchNextPage?: () => void,
|
|
23
|
+
hasNextPage?: boolean,
|
|
24
|
+
filter?: string,
|
|
25
|
+
setFilter?: (value?: string) => void,
|
|
26
|
+
isLoading?: boolean,
|
|
18
27
|
}
|
|
19
28
|
|
|
29
|
+
const StyledDiv = styled.div`
|
|
30
|
+
&#agents-content {
|
|
31
|
+
overflow: auto;
|
|
32
|
+
}
|
|
33
|
+
`
|
|
34
|
+
|
|
20
35
|
/**
|
|
21
36
|
* Renders a radio group where each option has a label and a description.
|
|
22
37
|
* The description in placed under the label and checkbox as an accordion.
|
|
23
38
|
*
|
|
24
39
|
* Also renders a search input.
|
|
25
40
|
*/
|
|
26
|
-
export function DescribedRadioGroup<T>({ initialValue, options: opt, data, emptyDataset, emptyResults, onChange
|
|
41
|
+
export function DescribedRadioGroup<T>({ initialValue, options: opt, data, emptyDataset, emptyResults, onChange,
|
|
42
|
+
fetchNextPage, hasNextPage, filter, setFilter, isLoading }: Props<T>) {
|
|
27
43
|
const [options, setOptions] = useState(opt)
|
|
28
44
|
const controls = useRadioGroupControls({
|
|
29
45
|
initialValue,
|
|
@@ -37,48 +53,85 @@ export function DescribedRadioGroup<T>({ initialValue, options: opt, data, empty
|
|
|
37
53
|
if (controls.value === undefined && initialValue !== undefined) controls.setValue(initialValue)
|
|
38
54
|
}, [initialValue])
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!isEqual(opt, options)) {
|
|
58
|
+
setOptions(opt)
|
|
59
|
+
}
|
|
60
|
+
}, [opt])
|
|
61
|
+
|
|
62
|
+
return (options?.length || isLoading || !!filter) ? <>
|
|
41
63
|
<FieldGroup fullWidth>
|
|
42
64
|
<Icon icon="Search" />
|
|
43
|
-
<Input type="search" value={controls.filter} onChange={controls.setFilter} />
|
|
65
|
+
<Input type="search" value={setFilter ? filter : controls.filter} onChange={setFilter ? setFilter : controls.setFilter} />
|
|
44
66
|
</FieldGroup>
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
{isLoading ?
|
|
68
|
+
<Column h="100%" alignItems="center" justifyContent="center">
|
|
69
|
+
<LoadingCircular/>
|
|
70
|
+
</Column> :
|
|
71
|
+
<StyledDiv id="agents-content">
|
|
72
|
+
{controls.options.length ?
|
|
73
|
+
<InfiniteScroll scrollableTarget="agents-content"
|
|
74
|
+
dataLength={controls.options.length}
|
|
75
|
+
next={fetchNextPage as any ?? undefined}
|
|
76
|
+
hasMore={hasNextPage ?? false}>
|
|
77
|
+
<RadioGroup
|
|
78
|
+
options={controls.options}
|
|
79
|
+
value={controls.value}
|
|
80
|
+
onChange={controls.setValue}
|
|
81
|
+
renderKey={controls.renderKey}
|
|
82
|
+
className="option-list"
|
|
83
|
+
renderItem={(radio, o) => {
|
|
84
|
+
const item = data(o)
|
|
85
|
+
const { idOrSlug, listFavorites, onAddFavorite, onRemoveFavorite } = item
|
|
86
|
+
return (
|
|
87
|
+
<Row className={controls.isUnfilteredButChecked(o) ? 'filtered-out' : ''}>
|
|
88
|
+
<ItemContent item={item} radio={radio} />
|
|
89
|
+
{onAddFavorite && <ButtonFavorite favorite={{
|
|
90
|
+
idOrSlug: idOrSlug,
|
|
91
|
+
listFavorites,
|
|
92
|
+
onAddFavorite: async (...args) => {
|
|
93
|
+
const res = await onAddFavorite(...args)
|
|
94
|
+
setOptions([...options]) // forces options re-rendering
|
|
95
|
+
return res
|
|
96
|
+
},
|
|
97
|
+
onRemoveFavorite: async (...args) => {
|
|
98
|
+
const res = await onRemoveFavorite?.(...args)
|
|
99
|
+
setOptions([...options]) // forces options re-rendering
|
|
100
|
+
return res ?? false
|
|
101
|
+
},
|
|
102
|
+
}} />}
|
|
103
|
+
</Row>
|
|
104
|
+
)}
|
|
60
105
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
idOrSlug: idOrSlug,
|
|
67
|
-
listFavorites,
|
|
68
|
-
onAddFavorite: async (...args) => {
|
|
69
|
-
const res = await onAddFavorite(...args)
|
|
70
|
-
setOptions([...options]) // forces options re-rendering
|
|
71
|
-
return res
|
|
72
|
-
},
|
|
73
|
-
onRemoveFavorite: async (...args) => {
|
|
74
|
-
const res = await onRemoveFavorite?.(...args)
|
|
75
|
-
setOptions([...options]) // forces options re-rendering
|
|
76
|
-
return res ?? false
|
|
77
|
-
},
|
|
78
|
-
}} />}
|
|
79
|
-
</Row>
|
|
80
|
-
)
|
|
81
|
-
}}
|
|
82
|
-
/> : emptyResults}
|
|
106
|
+
/>
|
|
107
|
+
</InfiniteScroll>
|
|
108
|
+
: emptyResults}
|
|
109
|
+
</StyledDiv> }
|
|
110
|
+
|
|
83
111
|
</> : emptyDataset
|
|
84
112
|
}
|
|
113
|
+
|
|
114
|
+
interface ItemContentProps {
|
|
115
|
+
radio: React.ReactElement,
|
|
116
|
+
item: { name: string, description: React.ReactNode, image?: React.ReactElement } & Favorite<any>,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const ItemContent = ({ item, radio }: ItemContentProps) => {
|
|
120
|
+
const [expanded, setExpanded] = useState(false)
|
|
121
|
+
const { description, name, image } = item
|
|
122
|
+
|
|
123
|
+
return <Accordion
|
|
124
|
+
header={btn => <>
|
|
125
|
+
{radio}
|
|
126
|
+
{image && <ImageBox size="xs" style={{ fontSize: '16px' }}>{image}</ImageBox>}
|
|
127
|
+
<Text>{name}</Text>{btn}</>
|
|
128
|
+
}
|
|
129
|
+
expanded={expanded}
|
|
130
|
+
onChange={setExpanded}
|
|
131
|
+
appearance="card"
|
|
132
|
+
>
|
|
133
|
+
{expanded && <>
|
|
134
|
+
{typeof description === 'string' ? <Text appearance="microtext1" color="light.700">{description}</Text> : description}
|
|
135
|
+
</>}
|
|
136
|
+
</Accordion>
|
|
137
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { Button } from '@stack-spot/citric-react'
|
|
|
3
3
|
import { Placeholder } from '@stack-spot/portal-components/Placeholder'
|
|
4
4
|
import { AgentResponseWithBuiltIn, agentToolsClient, AgentVisibilityLevel, workspaceAiClient } from '@stack-spot/portal-network'
|
|
5
5
|
import { WorkspaceResponse } from '@stack-spot/portal-network/api/workspace-ai'
|
|
6
|
-
import {
|
|
6
|
+
import { debounce } from 'lodash'
|
|
7
|
+
import { useCallback, useMemo, useState, useTransition } from 'react'
|
|
7
8
|
import { NavigationComponent } from '../../components/ComponentNavigator'
|
|
8
9
|
import { DescribedRadioGroup } from '../../components/form/DescribedRadioGroup'
|
|
9
10
|
import { WorkspaceTabNavigator } from '../../components/WorkspaceTabNavigator'
|
|
@@ -29,16 +30,41 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
29
30
|
const [submitEnabled, setSubmitEnabled] = useState(false)
|
|
30
31
|
const listFavorites = useFavorites()
|
|
31
32
|
const agentDefault = agentToolsClient.agentDefault.useQuery()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
|
|
34
|
+
const [filter, setFilter] = useState<string | undefined>()
|
|
35
|
+
const [apiFilter, setApiFilter] = useState<string | undefined>()
|
|
36
|
+
const [isPending, startTransition] = useTransition()
|
|
37
|
+
|
|
38
|
+
const runOnChange = useCallback(
|
|
39
|
+
debounce((value?: string) => {
|
|
40
|
+
startTransition(() => {
|
|
41
|
+
setApiFilter(value)
|
|
42
|
+
})
|
|
43
|
+
}, 500),
|
|
44
|
+
[startTransition],
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const data = workspaceAiClient.getAgentFromWorkspaceAi.useStatefulQuery({ workspaceId: workspaceId! },
|
|
48
|
+
{ enabled: !!workspaceId })
|
|
49
|
+
const workspaceAgents = data?.[0] as AgentResponseWithBuiltIn[]
|
|
50
|
+
|
|
51
|
+
const [agentsData=[], { fetchNextPage, hasNextPage }] = agentToolsClient.agentsMultipleFilters.useInfiniteQuery({
|
|
52
|
+
filters: {
|
|
53
|
+
page: 1,
|
|
54
|
+
size: 20,
|
|
55
|
+
name: apiFilter,
|
|
56
|
+
...(visibility?.length ? { visibility_list: [visibility] } : {}),
|
|
57
|
+
},
|
|
58
|
+
}, { enabled: !workspaceId })
|
|
59
|
+
|
|
60
|
+
const agents = workspaceId ? workspaceAgents : agentsData
|
|
35
61
|
|
|
36
62
|
const initialValue = useMemo<AgentResponseWithBuiltIn | undefined>(
|
|
37
63
|
() => {
|
|
38
64
|
const initial = agent.current
|
|
39
|
-
? agents
|
|
40
|
-
: chat.get('agent') ? agents
|
|
41
|
-
if (initial) setSubmitEnabled(true)
|
|
65
|
+
? agents?.find(a => a.id === agent.current?.id)
|
|
66
|
+
: chat.get('agent') ? agents?.find(a => a.id === chat.get('agent')?.id) : agentDefault as unknown as AgentResponseWithBuiltIn
|
|
67
|
+
if (initial && !submitEnabled) setSubmitEnabled(true)
|
|
42
68
|
return initial
|
|
43
69
|
},
|
|
44
70
|
[agents],
|
|
@@ -49,36 +75,42 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
49
75
|
close()
|
|
50
76
|
}
|
|
51
77
|
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
image: ag.avatar
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
return <>
|
|
79
|
+
<div className="content">
|
|
80
|
+
<DescribedRadioGroup
|
|
81
|
+
fetchNextPage={fetchNextPage}
|
|
82
|
+
hasNextPage={hasNextPage && !workspaceId}
|
|
83
|
+
options={agents}
|
|
84
|
+
initialValue={initialValue}
|
|
85
|
+
filter={workspaceId ? undefined : filter}
|
|
86
|
+
setFilter={workspaceId ? undefined : (value?: string) => {
|
|
87
|
+
setFilter(value)
|
|
88
|
+
runOnChange(value)
|
|
89
|
+
}}
|
|
90
|
+
onChange={(ag) => {
|
|
91
|
+
agent.current = ag
|
|
92
|
+
? { ...ag, label: ag.name, image: ag.avatar!, slug: ag.slug, builtIn: ag.visibility_level === 'built_in' }
|
|
93
|
+
: undefined
|
|
94
|
+
setSubmitEnabled(true)
|
|
95
|
+
}}
|
|
96
|
+
isLoading={isPending}
|
|
97
|
+
data={ag => ({
|
|
98
|
+
idOrSlug: ag.id,
|
|
99
|
+
image: ag.avatar ? <img src={ag.avatar} /> : <Icon icon="Agent" />,
|
|
100
|
+
description: <AgentDescription agentId={ag.id} />,
|
|
101
|
+
name: ag.name,
|
|
102
|
+
listFavorites,
|
|
103
|
+
onAddFavorite,
|
|
104
|
+
onRemoveFavorite,
|
|
105
|
+
})}
|
|
106
|
+
emptyResults={
|
|
107
|
+
<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} className="no-data-placeholder" />
|
|
108
|
+
}
|
|
109
|
+
emptyDataset={<Placeholder title={t.noData} description={t.noDataDescription} />}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
{!!agents?.length && showSubmitButton && <Button onClick={submit} disabled={!submitEnabled}>{t.apply}</Button>}
|
|
113
|
+
</>
|
|
82
114
|
}
|
|
83
115
|
|
|
84
116
|
export function AgentsTabWorkspace({ agent, visibility, showSubmitButton }: AgentTabProps) {
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
3
3
|
|
|
4
4
|
export function useAgentFavorites() {
|
|
5
|
-
const useFavorites = () => agentToolsClient.
|
|
5
|
+
const useFavorites = () => agentToolsClient.agentsMultipleFilters.useQuery({ filters: { visibility_list: ['favorite'] } })?.items
|
|
6
6
|
const [addFavorite, pendingAddFav] = agentToolsClient.addFavorite.useMutation()
|
|
7
7
|
const [removeFavorite, pendingRemoveFav] = agentToolsClient.removeFavorite.useMutation()
|
|
8
8
|
|
|
9
9
|
const removeFavoriteAgent = async (idOrSlug?: string) => {
|
|
10
10
|
try {
|
|
11
11
|
await removeFavorite({ agentId: idOrSlug || '' })
|
|
12
|
-
await agentToolsClient.
|
|
12
|
+
await agentToolsClient.agentsMultipleFilters.invalidate({ filters: { visibility_list: ['favorite'] } })
|
|
13
13
|
} catch (error) {
|
|
14
14
|
// eslint-disable-next-line no-console
|
|
15
15
|
console.error(error)
|
|
@@ -19,7 +19,7 @@ export function useAgentFavorites() {
|
|
|
19
19
|
const addFavoriteAgent = async (idOrSlug?: string) => {
|
|
20
20
|
try {
|
|
21
21
|
await addFavorite({ agentId: idOrSlug || '' })
|
|
22
|
-
await agentToolsClient.
|
|
22
|
+
await agentToolsClient.agentsMultipleFilters.invalidate({ filters: { visibility_list: ['favorite'] } })
|
|
23
23
|
} catch (error) {
|
|
24
24
|
// eslint-disable-next-line no-console
|
|
25
25
|
console.error(error)
|
|
@@ -50,14 +50,14 @@ const StepAccordionHeader = ({ step, index, expand }: Pick<StepProps, 'step' | '
|
|
|
50
50
|
return <Row gap="8px">
|
|
51
51
|
{expand}
|
|
52
52
|
{step.status === 'target' ? <Text className="step-title" appearance="body2" color="light.700">{t.planGoal}:</Text> :
|
|
53
|
-
<Badge colorScheme="inverse" appearance="square">
|
|
53
|
+
<Badge colorScheme="inverse" appearance="square" style={{ whiteSpace: 'nowrap' }}>
|
|
54
54
|
{t.step} {index}
|
|
55
55
|
</Badge>}
|
|
56
56
|
<Text className="step-title" appearance="body2">
|
|
57
57
|
{step.input}
|
|
58
58
|
</Text>
|
|
59
59
|
{step.status === 'awaiting_approval' &&
|
|
60
|
-
<Badge appearance="square" style={{ backgroundColor: theme.color.gray[800], color: theme.color.gray[50] }}>
|
|
60
|
+
<Badge appearance="square" style={{ backgroundColor: theme.color.gray[800], color: theme.color.gray[50], whiteSpace: 'nowrap' }}>
|
|
61
61
|
<Icon icon="Security" />
|
|
62
62
|
{t.pendingReview}
|
|
63
63
|
</Badge>}
|
|
@@ -199,7 +199,7 @@ export const ToolStepsList = ({ toolStep, messageId, chatId }: { toolStep: ToolC
|
|
|
199
199
|
return <>
|
|
200
200
|
{toolStep && tool ? <AnimatedHeight>
|
|
201
201
|
<div className="steps">
|
|
202
|
-
<Badge colorPalette="yellow" appearance="square">
|
|
202
|
+
<Badge colorPalette="yellow" appearance="square" style={{ whiteSpace: 'nowrap' }}>
|
|
203
203
|
<Icon icon="StopWatch" />
|
|
204
204
|
<Text>{tool.name} {t.keepWorking}</Text>
|
|
205
205
|
</Badge>
|
|
@@ -231,7 +231,7 @@ export const ToolStepsList = ({ toolStep, messageId, chatId }: { toolStep: ToolC
|
|
|
231
231
|
|
|
232
232
|
export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }: Props) => {
|
|
233
233
|
const t = useTranslate(dictionary)
|
|
234
|
-
|
|
234
|
+
|
|
235
235
|
const filteredSteps = steps.filter(s => s.type === 'step')
|
|
236
236
|
const actualSteps = useMemo(() => filteredSteps.filter((item) => {
|
|
237
237
|
const messageIdForStep = planningToolDictionaryHelper.getMessageIdFromStepId(item.id)
|
|
@@ -305,7 +305,7 @@ export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }:
|
|
|
305
305
|
{!userHasAlreadyAnswered && planning?.[0]?.status === 'awaiting_approval' && <AwaitingApproval chatId={chatId} />}
|
|
306
306
|
</Column>
|
|
307
307
|
|
|
308
|
-
{
|
|
308
|
+
{planning?.[0]?.status === 'success' && <ViewToolsDetails chatId={chatId} messageId={messageId}/>}
|
|
309
309
|
|
|
310
310
|
</div>
|
|
311
311
|
</AnimatedHeight> : null}
|
|
@@ -316,14 +316,16 @@ export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }:
|
|
|
316
316
|
)
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
export const ViewToolsDetails = ({ chatId }: { chatId: string }) => {
|
|
319
|
+
export const ViewToolsDetails = ({ chatId, messageId }: { chatId: string, messageId: number }) => {
|
|
320
320
|
const t = useTranslate(dictionary)
|
|
321
321
|
const messages = useChatMessages(chatId)
|
|
322
322
|
const widget = useWidget()
|
|
323
323
|
const getPlanningMessageId = () => {
|
|
324
|
+
const messageEntry = messages.find((message) => message.id === messageId)
|
|
325
|
+
const messageStep = messageEntry?.getValue().steps?.find((step) => step.type === 'step')
|
|
324
326
|
const messageWithPlanning = findLast(messages, (message) => {
|
|
325
327
|
const steps = message.getValue().steps
|
|
326
|
-
const planningStep = steps?.find((step) => step.
|
|
328
|
+
const planningStep = steps?.find((step) => step.id === messageStep?.id)
|
|
327
329
|
return planningStep ? true : false
|
|
328
330
|
})
|
|
329
331
|
return messageWithPlanning?.id
|
|
@@ -15,7 +15,8 @@ export const ModelSwitcher = () => {
|
|
|
15
15
|
const chat = useCurrentChat()
|
|
16
16
|
const [filter, setFilter] = useState('')
|
|
17
17
|
const [visibleMenu, setVisibleMenu] = useState(false)
|
|
18
|
-
const [agentData, isLoadingAgentData] = agentToolsClient.agent.useStatefulQuery({ agentId: agentCurrentChat?.id || '' }
|
|
18
|
+
const [agentData, isLoadingAgentData] = agentToolsClient.agent.useStatefulQuery({ agentId: agentCurrentChat?.id || '' },
|
|
19
|
+
{ enabled: !!agentCurrentChat?.id })
|
|
19
20
|
const [models, isLoadingModels] =
|
|
20
21
|
genAiInferenceClient.listModels.useStatefulQuery({ pageSize: 999, active: true })
|
|
21
22
|
|