@stack-spot/ai-chat-widget 3.5.0-beta.5 → 3.6.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 +77 -0
- package/dist/app-metadata.json +3 -3
- package/dist/chat-interceptors/quick-commands.js +2 -2
- 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 +8 -18
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/form/DescribedCheckboxGroup.d.ts +3 -1
- package/dist/components/form/DescribedCheckboxGroup.d.ts.map +1 -1
- package/dist/components/form/DescribedCheckboxGroup.js +31 -19
- package/dist/components/form/DescribedCheckboxGroup.js.map +1 -1
- package/dist/context/hooks.d.ts +1 -0
- package/dist/context/hooks.d.ts.map +1 -1
- package/dist/context/hooks.js +24 -0
- package/dist/context/hooks.js.map +1 -1
- package/dist/state/ChatEntry.d.ts +9 -10
- package/dist/state/ChatEntry.d.ts.map +1 -1
- package/dist/state/ChatEntry.js +2 -16
- package/dist/state/ChatEntry.js.map +1 -1
- package/dist/state/ChatState.d.ts +6 -0
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js +15 -0
- package/dist/state/ChatState.js.map +1 -1
- package/dist/utils/tools.d.ts +17 -8
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +20 -9
- package/dist/utils/tools.js.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.js +5 -14
- 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 +3 -2
- package/dist/views/Agents/AgentsTab.js.map +1 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +19 -21
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/StepsList.d.ts +2 -8
- package/dist/views/Chat/StepsList.d.ts.map +1 -1
- package/dist/views/Chat/StepsList.js +7 -6
- package/dist/views/Chat/StepsList.js.map +1 -1
- package/dist/views/ChatHistory/utils.js +2 -2
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/KnowledgeSources.d.ts +1 -1
- package/dist/views/KnowledgeSources.d.ts.map +1 -1
- package/dist/views/KnowledgeSources.js +31 -45
- package/dist/views/KnowledgeSources.js.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.js +29 -52
- package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
- package/dist/views/MessageInput/index.d.ts.map +1 -1
- package/dist/views/MessageInput/index.js +9 -1
- package/dist/views/MessageInput/index.js.map +1 -1
- package/dist/views/MessageInput/styled.d.ts.map +1 -1
- package/dist/views/MessageInput/styled.js +4 -0
- package/dist/views/MessageInput/styled.js.map +1 -1
- package/dist/views/Resources.js +8 -5
- package/dist/views/Resources.js.map +1 -1
- package/dist/views/Tools.js +1 -1
- package/dist/views/Tools.js.map +1 -1
- package/package.json +2 -2
- package/src/app-metadata.json +3 -3
- package/src/chat-interceptors/quick-commands.ts +2 -2
- package/src/chat-interceptors/send-message.ts +8 -20
- package/src/components/form/DescribedCheckboxGroup.tsx +61 -35
- package/src/context/hooks.ts +24 -0
- package/src/state/ChatEntry.ts +10 -18
- package/src/state/ChatState.ts +16 -0
- package/src/utils/tools.ts +28 -17
- package/src/views/Agents/AgentDescription.tsx +40 -36
- package/src/views/Agents/AgentsTab.tsx +8 -7
- package/src/views/Chat/ChatMessage.tsx +21 -23
- package/src/views/Chat/StepsList.tsx +8 -8
- package/src/views/ChatHistory/utils.ts +2 -2
- package/src/views/KnowledgeSources.tsx +57 -77
- package/src/views/MessageInput/QuickCommandSelector.tsx +39 -93
- package/src/views/MessageInput/index.tsx +10 -0
- package/src/views/MessageInput/styled.ts +4 -0
- package/src/views/Resources.tsx +11 -10
- package/src/views/Tools.tsx +1 -1
|
@@ -9,7 +9,7 @@ import { PhoneInput } from 'react-international-phone'
|
|
|
9
9
|
import 'react-international-phone/style.css'
|
|
10
10
|
import { FileDescription } from '../../components/FileDescription'
|
|
11
11
|
import { Markdown } from '../../components/Markdown'
|
|
12
|
-
import { useChatEntry,
|
|
12
|
+
import { useChatEntry, useCurrentChat, useCurrentChatState, useWidget } from '../../context/hooks'
|
|
13
13
|
import { useMidnightUpdateView } from '../../hooks/midnight-update-view'
|
|
14
14
|
import { ChatEntry, SerializableAction, TextChatEntry } from '../../state/ChatEntry'
|
|
15
15
|
import { useDateFormatter } from '../../utils/date'
|
|
@@ -213,26 +213,24 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
213
213
|
const widget = useWidget()
|
|
214
214
|
const chat = useCurrentChat()
|
|
215
215
|
const agentId = entry.agent?.id ?? ''
|
|
216
|
-
|
|
216
|
+
// enabled: we don't want to make any request if there is no agent
|
|
217
|
+
const [agent] = agentToolsClient.agent.useStatefulQuery({ agentId }, { enabled: !!agentId })
|
|
218
|
+
const toolkits = useMemo(() => [
|
|
219
|
+
...agent?.toolkits?.builtin_toolkits ?? [],
|
|
220
|
+
...agent?.toolkits?.custom_toolkits ?? [],
|
|
221
|
+
...agent?.toolkits?.mcp_toolkits ?? [],
|
|
222
|
+
], [agent])
|
|
217
223
|
const [agentsTools] = agentToolsClient.agentsByIds.useStatefulQuery(
|
|
218
|
-
{ searchAgentsRequest: { ids: entry.tools || [''] } }, { enabled: !!entry.tools })
|
|
224
|
+
{ searchAgentsRequest: { ids: entry.tools || [''] } }, { enabled: !!entry.tools?.length })
|
|
219
225
|
const [copied, setCopied] = useState(false)
|
|
220
226
|
const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
|
|
221
227
|
const isPlanning = useCurrentChatState('isPlaning') ?? false
|
|
222
228
|
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
const userHasAlreadyAnswered = useMemo(() => {
|
|
229
|
-
const messageIndex = messages.findIndex((messageItem) => messageItem.id === message.id)
|
|
230
|
-
if (messages.length - 1 === messageIndex) return false
|
|
231
|
-
const nextMessage = messages[messageIndex + 1].getValue()
|
|
232
|
-
return nextMessage.agentType === 'user'
|
|
233
|
-
}, [messages, messages.length])
|
|
234
|
-
const isMessageHidden = toolsStep && userHasAlreadyAnswered
|
|
235
|
-
|
|
229
|
+
// Dynamic tool steps are identified by the "dynamic" id.
|
|
230
|
+
// We're temporarily hiding the toolbox for these dynamic tools while we finalize their UI.
|
|
231
|
+
const shouldHideToolbox = entry?.steps?.some((step) => step?.id === 'dynamic')
|
|
232
|
+
const showToolBox = (!!agentsTools?.length || !!entry.tools?.length) && !shouldHideToolbox
|
|
233
|
+
|
|
236
234
|
useChatScrollToBottomEffect(ref, [entry])
|
|
237
235
|
useMidnightUpdateView()
|
|
238
236
|
|
|
@@ -357,8 +355,8 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
357
355
|
widget.set('panel', 'resources')
|
|
358
356
|
}
|
|
359
357
|
|
|
360
|
-
const
|
|
361
|
-
|
|
358
|
+
const shouldShowToolsOnlyMessage = (entry.done !== false || entry.hasPlanning) && !!entry.steps?.length
|
|
359
|
+
const shouldRender = entry.content || entry.error || shouldShowToolsOnlyMessage || !!entry.upload?.length
|
|
362
360
|
|
|
363
361
|
return shouldRender && (
|
|
364
362
|
<li key={entry.messageId} className={entry.agentType} ref={ref}>
|
|
@@ -374,8 +372,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
374
372
|
{entry.badges.map((b, index) => <Badge key={index} colorPalette={b.color ?? 'cyan'} appearance="square">{b.label}</Badge>)}
|
|
375
373
|
</div>}
|
|
376
374
|
|
|
377
|
-
{!!entry.steps?.length && <StepsList steps={entry.steps} chatId={chat.id} messageId={message.id}
|
|
378
|
-
userHasAlreadyAnswered={userHasAlreadyAnswered} />}
|
|
375
|
+
{!!entry.steps?.length && <StepsList steps={entry.steps} chatId={chat.id} messageId={message.id} />}
|
|
379
376
|
|
|
380
377
|
{renderContent()}
|
|
381
378
|
|
|
@@ -396,7 +393,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
396
393
|
))}</ul>
|
|
397
394
|
</div>}
|
|
398
395
|
|
|
399
|
-
{
|
|
396
|
+
{showToolBox &&
|
|
400
397
|
<div className="tools-box">
|
|
401
398
|
<Button appearance="none" onClick={openResourcesPanel} aria-label={t.openResourcesPanel}>
|
|
402
399
|
{agentsTools?.map((agent) => (
|
|
@@ -411,13 +408,14 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
|
|
|
411
408
|
</ImageBox>
|
|
412
409
|
))}
|
|
413
410
|
{entry?.tools?.map((id) => {
|
|
411
|
+
// In the multi-agents feature, an agent is returned as a tool, but we don't want to show agents here.
|
|
414
412
|
if (agentsTools?.some((agent) => agent.id === id)) return null
|
|
415
|
-
const tool = toolById(id,
|
|
413
|
+
const tool = toolById(id, toolkits)
|
|
416
414
|
return (
|
|
417
415
|
<ImageBox key={id} className="agent-info-avatar-resource">
|
|
418
416
|
<ImageWithFallback
|
|
419
417
|
src={tool?.image}
|
|
420
|
-
fallback={<Icon icon="Cog" />}
|
|
418
|
+
fallback={<Icon icon="Cog" title={tool?.name} aria-label={tool?.name} />}
|
|
421
419
|
alt={tool?.name}
|
|
422
420
|
aria-label={tool?.name}
|
|
423
421
|
title={tool?.name}
|
|
@@ -7,7 +7,7 @@ import { findLast, findLastIndex } from 'lodash'
|
|
|
7
7
|
import React, { useEffect, useMemo } from 'react'
|
|
8
8
|
import styled from 'styled-components'
|
|
9
9
|
import { Markdown } from '../../components/Markdown'
|
|
10
|
-
import { useChat, useChatMessages, useCurrentChat, useCurrentChatMessages, useWidget } from '../../context/hooks'
|
|
10
|
+
import { useChat, useChatMessages, useCurrentChat, useCurrentChatMessages, useIsLastEntryInCurrentChat, useWidget } from '../../context/hooks'
|
|
11
11
|
import { ChatEntry } from '../../state/ChatEntry'
|
|
12
12
|
import { planningToolDictionaryHelper } from '../../utils/planning-tool'
|
|
13
13
|
import { updateToolStep } from '../../utils/update-tool-step'
|
|
@@ -17,7 +17,6 @@ interface Props {
|
|
|
17
17
|
steps: ChatStep[],
|
|
18
18
|
messageId: number,
|
|
19
19
|
chatId: string,
|
|
20
|
-
userHasAlreadyAnswered?: boolean,
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
interface StepChatStepWithTarget extends Omit<StepChatStep, 'status' | 'id' | 'type'> {
|
|
@@ -174,10 +173,11 @@ const AwaitingApproval = ({ customApproveText, chatId }: { chatId: string, custo
|
|
|
174
173
|
</>
|
|
175
174
|
}
|
|
176
175
|
|
|
177
|
-
|
|
176
|
+
const ToolStepsList = ({ toolStep, messageId, chatId }: { toolStep: ToolChatStep, messageId: number, chatId: string }) => {
|
|
178
177
|
const t = useTranslate(dictionary)
|
|
179
178
|
const chat = useCurrentChat()
|
|
180
179
|
const messages = useCurrentChatMessages()
|
|
180
|
+
const isLastMessage = useIsLastEntryInCurrentChat(messageId)
|
|
181
181
|
const inputParsed = `\`\`\`json
|
|
182
182
|
${JSON.stringify(toolStep?.input, null, 2)}
|
|
183
183
|
\`\`\``
|
|
@@ -222,15 +222,16 @@ export const ToolStepsList = ({ toolStep, messageId, chatId }: { toolStep: ToolC
|
|
|
222
222
|
</Markdown>
|
|
223
223
|
</Accordion>
|
|
224
224
|
|
|
225
|
-
<AwaitingApproval customApproveText={t.approveTool} chatId={chatId} />
|
|
225
|
+
{isLastMessage && <AwaitingApproval customApproveText={t.approveTool} chatId={chatId} />}
|
|
226
226
|
</Card>
|
|
227
227
|
</div>
|
|
228
228
|
</AnimatedHeight> : null}
|
|
229
229
|
</>
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
export const StepsList = ({ steps, messageId, chatId
|
|
232
|
+
export const StepsList = ({ steps, messageId, chatId }: Props) => {
|
|
233
233
|
const t = useTranslate(dictionary)
|
|
234
|
+
const isLastMessage = useIsLastEntryInCurrentChat(messageId)
|
|
234
235
|
|
|
235
236
|
const filteredSteps = steps.filter(s => s.type === 'step')
|
|
236
237
|
const actualSteps = useMemo(() => filteredSteps.filter((item) => {
|
|
@@ -302,7 +303,7 @@ export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }:
|
|
|
302
303
|
<Column gap="8px" mt="8px">
|
|
303
304
|
<Divider colorScheme="light" />
|
|
304
305
|
<Text color="light.700">{planning?.[0]?.user_question}</Text>
|
|
305
|
-
{
|
|
306
|
+
{isLastMessage && planning?.[0]?.status === 'awaiting_approval' && <AwaitingApproval chatId={chatId} />}
|
|
306
307
|
</Column>
|
|
307
308
|
|
|
308
309
|
{planning?.[0]?.status === 'success' && <ViewToolsDetails chatId={chatId} messageId={messageId}/>}
|
|
@@ -310,8 +311,7 @@ export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }:
|
|
|
310
311
|
</div>
|
|
311
312
|
</AnimatedHeight> : null}
|
|
312
313
|
|
|
313
|
-
{toolsStep && toolsStep.status === 'awaiting_approval' &&
|
|
314
|
-
<ToolStepsList toolStep={toolsStep} messageId={messageId} chatId={chatId} />}
|
|
314
|
+
{toolsStep && toolsStep.status === 'awaiting_approval' && <ToolStepsList toolStep={toolsStep} messageId={messageId} chatId={chatId} />}
|
|
315
315
|
</>
|
|
316
316
|
)
|
|
317
317
|
}
|
|
@@ -64,7 +64,7 @@ function findTool(agent: AgentModel | undefined, id: string) {
|
|
|
64
64
|
]
|
|
65
65
|
for (const toolkit of allToolkits) {
|
|
66
66
|
for (const tool of toolkit.tools ?? []) {
|
|
67
|
-
if (tool.id === id) return { toolkit, tool }
|
|
67
|
+
if ('id' in tool && tool.id === id) return { toolkit, tool }
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
return { toolkit: undefined, tool: { name: id } }
|
|
@@ -106,7 +106,7 @@ async function stepsFromAgentInfo(
|
|
|
106
106
|
id: t.tool_id,
|
|
107
107
|
executionId: t.tool_execution_id,
|
|
108
108
|
name: tool?.name,
|
|
109
|
-
description: tool
|
|
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,
|
|
112
112
|
duration: end?.duration,
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { Box } from '@citric/core'
|
|
2
1
|
import { Button, Tab } from '@stack-spot/citric-react'
|
|
3
2
|
import { Placeholder } from '@stack-spot/portal-components/Placeholder'
|
|
4
3
|
import { aiClient, dataIntegrationClient, workspaceAiClient } from '@stack-spot/portal-network'
|
|
5
4
|
import { VisibilityLevelEnum } from '@stack-spot/portal-network/api/ai'
|
|
5
|
+
import { KnowledgeSourceItemResponse } from '@stack-spot/portal-network/api/dataIntegration'
|
|
6
6
|
import { WorkspaceResponse } from '@stack-spot/portal-network/api/workspace-ai'
|
|
7
7
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
8
8
|
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
9
|
-
import InfiniteScroll from 'react-infinite-scroll-component'
|
|
10
9
|
import { NavigationComponent } from '../components/ComponentNavigator'
|
|
11
10
|
import { DescribedCheckboxGroup } from '../components/form/DescribedCheckboxGroup'
|
|
12
11
|
import { RightPanelContentList } from '../components/RightPanelContentList'
|
|
@@ -66,13 +65,19 @@ const KnowledgeSourcesPanel = () => {
|
|
|
66
65
|
}, [chat])
|
|
67
66
|
|
|
68
67
|
const allTabsMap: Partial<Record<Scope, Omit<Tab, 'key'>>> = {
|
|
69
|
-
favorite: {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
favorite: {
|
|
69
|
+
label: t.favorites,
|
|
70
|
+
content: <KnowledgeSourcesTab key="favorite" visibility="favorite" allKS={allKS} onSubmit={onSubmit} />,
|
|
71
|
+
},
|
|
72
|
+
personal: {
|
|
73
|
+
label: t.personal,
|
|
74
|
+
content: <KnowledgeSourcesTab key="personal" visibility="personal" allKS={allKS} onSubmit={onSubmit} />,
|
|
75
|
+
},
|
|
73
76
|
shared: { label: t.shared, content: <KnowledgeSourcesTab key="shared" visibility="shared" allKS={allKS} onSubmit={onSubmit} /> },
|
|
74
|
-
workspace: {
|
|
75
|
-
|
|
77
|
+
workspace: {
|
|
78
|
+
label: t.spots,
|
|
79
|
+
content: <KnowledgeSourcesTabWorkspace key="workspace" visibility="workspace" allKS={allKS} onSubmit={onSubmit} />,
|
|
80
|
+
},
|
|
76
81
|
account: { label: t.account, content: <KnowledgeSourcesTab key="account" visibility="account" allKS={allKS} onSubmit={onSubmit} /> },
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -97,47 +102,29 @@ const KnowledgeSourcesPanel = () => {
|
|
|
97
102
|
|
|
98
103
|
export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId, showSubmitButton = true }: TabProps) => {
|
|
99
104
|
const t = useTranslate(dictionary)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
ksListPages,
|
|
116
|
-
_isLoadingKnowledgeSources,
|
|
117
|
-
_ksListError,
|
|
118
|
-
{ fetchNextPage: _fetchNextPage, hasNextPage: _hasNextPage },
|
|
119
|
-
] = dataIntegrationClient.knowledgeSourcesV2.useStatefulInfiniteQuery(
|
|
120
|
-
{
|
|
121
|
-
order: 'a-to-z',
|
|
122
|
-
visibilityList: visibility ? [visibility] : [],
|
|
123
|
-
types: ['snippet', 'api', 'event', 'custom'],
|
|
124
|
-
size: 20,
|
|
125
|
-
},
|
|
126
|
-
{ enabled: true },
|
|
127
|
-
)
|
|
128
|
-
knowledgeSources = ksListPages?.flat() ?? []
|
|
129
|
-
fetchNextPage = _fetchNextPage
|
|
130
|
-
hasNextPage = _hasNextPage
|
|
131
|
-
isLoadingKnowledgeSources = _isLoadingKnowledgeSources
|
|
132
|
-
ksListError = _ksListError
|
|
133
|
-
}
|
|
105
|
+
const [data = []] = workspaceAiClient.getKSFromWorkspaceAi.useStatefulQuery(
|
|
106
|
+
{ workspaceId: workspaceId! }, { enabled: !!workspaceId })
|
|
107
|
+
const workspaceKs = data as KnowledgeSourceItemResponse[]
|
|
108
|
+
|
|
109
|
+
const [ksListPages = [], { fetchNextPage, hasNextPage }] = dataIntegrationClient.knowledgeSourcesV2.useInfiniteQuery(
|
|
110
|
+
{
|
|
111
|
+
order: 'a-to-z',
|
|
112
|
+
visibilityList: visibility ? [visibility] : [],
|
|
113
|
+
types: ['snippet', 'api', 'event', 'custom'],
|
|
114
|
+
size: 20,
|
|
115
|
+
page: 1,
|
|
116
|
+
},
|
|
117
|
+
{ enabled: !workspaceId },
|
|
118
|
+
)
|
|
119
|
+
const knowledgeSources = workspaceId ? workspaceKs : ksListPages
|
|
134
120
|
|
|
135
121
|
const initialValue = useMemo(() => {
|
|
136
122
|
const currentlySelected = allKS.current?.map(ks => ks.id)
|
|
137
|
-
return knowledgeSources
|
|
138
|
-
}, [knowledgeSources
|
|
123
|
+
return knowledgeSources?.filter((ks: KnowledgeSourceItemResponse) => currentlySelected?.includes(ks.slug))
|
|
124
|
+
}, [knowledgeSources])
|
|
125
|
+
|
|
126
|
+
const listFavorites = dataIntegrationClient.knowledgeSourcesV2.useQuery({ visibilityList: ['favorite'] })?.items
|
|
139
127
|
|
|
140
|
-
const listFavorites = dataIntegrationClient.knowledgeSources.useQuery({ visibility: 'favorite' })
|
|
141
128
|
const [addFavorite, pendingAddFav] = dataIntegrationClient.addFavoriteKnowledgeSource.useMutation()
|
|
142
129
|
const [removeFavorite, pendingRemoveFav] = dataIntegrationClient.removeFavoriteKnowledgeSource.useMutation()
|
|
143
130
|
|
|
@@ -145,7 +132,7 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
|
|
|
145
132
|
try {
|
|
146
133
|
await removeFavorite({ slug: idOrSlug })
|
|
147
134
|
await aiClient.knowledgeSources.invalidate()
|
|
148
|
-
await dataIntegrationClient.
|
|
135
|
+
await dataIntegrationClient.knowledgeSourcesV2.invalidate()
|
|
149
136
|
} catch (error) {
|
|
150
137
|
// eslint-disable-next-line no-console
|
|
151
138
|
console.error(error)
|
|
@@ -156,7 +143,7 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
|
|
|
156
143
|
const onRemoveFavorite = (idOrSlug: string) => new Promise<boolean>(async (resolve, reject) => {
|
|
157
144
|
try {
|
|
158
145
|
await removeFavoriteKs(idOrSlug)
|
|
159
|
-
if (!pendingRemoveFav){
|
|
146
|
+
if (!pendingRemoveFav) {
|
|
160
147
|
resolve(true)
|
|
161
148
|
}
|
|
162
149
|
|
|
@@ -164,17 +151,14 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
|
|
|
164
151
|
// eslint-disable-next-line no-console
|
|
165
152
|
console.error(error)
|
|
166
153
|
reject(error)
|
|
167
|
-
}
|
|
154
|
+
}
|
|
168
155
|
})
|
|
169
156
|
|
|
170
|
-
if (isLoadingKnowledgeSources && knowledgeSources.length === 0) {return null}
|
|
171
|
-
if (ksListError) {return null}
|
|
172
|
-
|
|
173
157
|
const addFavoriteKs = async (idOrSlug: string) => {
|
|
174
158
|
try {
|
|
175
159
|
await addFavorite({ slug: idOrSlug })
|
|
176
160
|
await aiClient.knowledgeSources.invalidate()
|
|
177
|
-
await dataIntegrationClient.
|
|
161
|
+
await dataIntegrationClient.knowledgeSourcesV2.invalidate()
|
|
178
162
|
} catch (error) {
|
|
179
163
|
// eslint-disable-next-line no-console
|
|
180
164
|
console.error(error)
|
|
@@ -185,7 +169,7 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
|
|
|
185
169
|
const onAddFavorite = (idOrSlug: string) => new Promise<boolean>(async (resolve, reject) => {
|
|
186
170
|
try {
|
|
187
171
|
await addFavoriteKs(idOrSlug)
|
|
188
|
-
if (!pendingAddFav){
|
|
172
|
+
if (!pendingAddFav) {
|
|
189
173
|
resolve(true)
|
|
190
174
|
}
|
|
191
175
|
|
|
@@ -193,31 +177,27 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
|
|
|
193
177
|
// eslint-disable-next-line no-console
|
|
194
178
|
console.error(error)
|
|
195
179
|
reject(error)
|
|
196
|
-
}
|
|
180
|
+
}
|
|
197
181
|
})
|
|
198
182
|
|
|
199
|
-
return (
|
|
200
|
-
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
</InfiniteScroll>
|
|
218
|
-
</Box>
|
|
219
|
-
{!!knowledgeSources.length && showSubmitButton && <Button onClick={onSubmit}>{t.apply}</Button>}
|
|
220
|
-
</>
|
|
183
|
+
return (<>
|
|
184
|
+
<div className="content">
|
|
185
|
+
<DescribedCheckboxGroup
|
|
186
|
+
options={knowledgeSources}
|
|
187
|
+
initialValue={initialValue}
|
|
188
|
+
globalSelection={allKS}
|
|
189
|
+
hasNextPage={hasNextPage && !workspaceId}
|
|
190
|
+
fetchNextPage={fetchNextPage}
|
|
191
|
+
data={(ks: KnowledgeSourceItemResponse) => ({
|
|
192
|
+
idOrSlug: ks.slug, description: ks.description, name: ks.name, listFavorites,
|
|
193
|
+
onAddFavorite, onRemoveFavorite,
|
|
194
|
+
})}
|
|
195
|
+
emptyResults={<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />}
|
|
196
|
+
emptyDataset={<Placeholder title={t.noData} description={t.noDataDescription} className="no-data-placeholder" />}
|
|
197
|
+
/>
|
|
198
|
+
</div>
|
|
199
|
+
{!!knowledgeSources?.length && showSubmitButton && <Button onClick={onSubmit}>{t.apply}</Button>}
|
|
200
|
+
</>
|
|
221
201
|
)
|
|
222
202
|
}
|
|
223
203
|
|
|
@@ -228,7 +208,7 @@ export function KnowledgeSourcesTabWorkspace({ allKS, onSubmit }: TabProps) {
|
|
|
228
208
|
component: 'ks',
|
|
229
209
|
props: { visibility: 'workspace', workspaceId: workspace.id, allKS, onSubmit },
|
|
230
210
|
})
|
|
231
|
-
|
|
211
|
+
|
|
232
212
|
return <WorkspaceTabNavigator components={workspaceTabComponents} getNavigateParam={buildNavigateParams} />
|
|
233
213
|
}
|
|
234
214
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { Box } from '@citric/core'
|
|
2
1
|
import { Icon } from '@stack-spot/citric-icons'
|
|
3
2
|
import { aiClient, workspaceAiClient } from '@stack-spot/portal-network'
|
|
4
3
|
import { QuickCommandResponse } from '@stack-spot/portal-network/api/ai'
|
|
5
|
-
import { useCallback
|
|
6
|
-
import InfiniteScroll from 'react-infinite-scroll-component'
|
|
4
|
+
import { useCallback } from 'react'
|
|
7
5
|
import { Selector } from '../../components/Selector'
|
|
8
6
|
import { useCurrentChat, useCurrentChatState, useWidgetState } from '../../context/hooks'
|
|
9
7
|
import { quickCommandRegex } from '../../regex'
|
|
@@ -79,103 +77,51 @@ export const QuickCommandSelector = ({ inputRef, isTrial }:
|
|
|
79
77
|
inputRef.current.focus()
|
|
80
78
|
}, [chat, inputRef])
|
|
81
79
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
const quickCommands = useMemo(() => quickCommandsPages, [quickCommandsPages])
|
|
93
|
-
|
|
94
|
-
const quickCommandsFiltered = useMemo(() => quickCommands.filter(
|
|
95
|
-
(qc) => qc.visibility_level.toLowerCase() !== 'workspace'), [quickCommands],
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
const workspaceQuickCommands = workspaceAiClient.workspacesContentsByType.useQuery({
|
|
99
|
-
contentType: 'quick_command',
|
|
100
|
-
}) || []
|
|
101
|
-
|
|
102
|
-
const workspaceQuickCommandsWithWorkspaceName: QuickCommandResponseWithSpaceName[] =
|
|
103
|
-
useMemo(
|
|
104
|
-
() =>
|
|
105
|
-
workspaceQuickCommands.flatMap(({ qcs, space_name }) =>
|
|
106
|
-
qcs?.map((qc) => ({ ...qc, spaceName: space_name })),
|
|
107
|
-
) as QuickCommandResponseWithSpaceName[],
|
|
108
|
-
[workspaceQuickCommands],
|
|
80
|
+
const getQuickCommands = () => {
|
|
81
|
+
if (spotId) {
|
|
82
|
+
return workspaceAiClient.getQCFromWorkspaceAi.useQuery({ workspaceId: spotId })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const quickCommands = aiClient.allQuickCommands.useQuery({ order: 'a-to-z' })
|
|
86
|
+
const quickCommandsFiltered = quickCommands.filter(
|
|
87
|
+
(qc) => qc.visibility_level.toLowerCase() !== 'workspace',
|
|
109
88
|
)
|
|
110
89
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
: [...quickCommandsFiltered, ...workspaceQuickCommandsWithWorkspaceName]
|
|
90
|
+
const workspaceQuickCommands = workspaceAiClient.workspacesContentsByType.useQuery({
|
|
91
|
+
contentType: 'quick_command',
|
|
92
|
+
})
|
|
115
93
|
|
|
116
|
-
|
|
117
|
-
|
|
94
|
+
const workspaceQuickCommandsWithWorkspaceName: QuickCommandResponseWithSpaceName[] =
|
|
95
|
+
workspaceQuickCommands.flatMap(({ qcs, space_name }) =>
|
|
96
|
+
qcs?.map((qc) => ({ ...qc, spaceName: space_name })),
|
|
97
|
+
) as QuickCommandResponseWithSpaceName[]
|
|
118
98
|
|
|
119
|
-
|
|
99
|
+
return [...quickCommandsFiltered, ...workspaceQuickCommandsWithWorkspaceName]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const QuickCommandItem = ({ slug, description, spaceName }: QuickCommandResponseWithSpaceName) => <>
|
|
120
103
|
<p className="selector-description">{spaceName}</p>
|
|
121
104
|
<p className="selector-title">/{slug?.toUpperCase()}</p>
|
|
122
105
|
<p className="selector-description">{description}</p>
|
|
123
106
|
</>
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
if (spotId) {
|
|
127
|
-
return (
|
|
128
|
-
<Selector
|
|
129
|
-
inputRef={inputRef}
|
|
130
|
-
favorite={{
|
|
131
|
-
useFavorites, onAddFavorite, onRemoveFavorite, favoriteIsSlug: true,
|
|
132
|
-
}}
|
|
133
|
-
selectorConfig={{
|
|
134
|
-
resourceName: 'Quick Command',
|
|
135
|
-
shortcut: '/',
|
|
136
|
-
icon: <Icon icon="QuickCommand" />,
|
|
137
|
-
searchProp: 'slug',
|
|
138
|
-
urlBuilder: (qc) => `/quick-command/${qc?.slug}`,
|
|
139
|
-
regex: quickCommandRegex,
|
|
140
|
-
sections: isTrial ? ['favorite', 'personal'] : ['favorite', 'personal', 'workspace', 'account', 'shared'],
|
|
141
|
-
isEnabled: isQuickCommandEnabled,
|
|
142
|
-
onSelect: onSelectItem,
|
|
143
|
-
renderComponentItem: QuickCommandItem,
|
|
144
|
-
useData: () => allQuickCommands,
|
|
145
|
-
}}
|
|
146
|
-
/>
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
107
|
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
urlBuilder: (qc) => `/quick-command/${qc?.slug}`,
|
|
170
|
-
regex: quickCommandRegex,
|
|
171
|
-
sections: isTrial ? ['favorite', 'personal'] : ['favorite', 'personal', 'workspace', 'account', 'shared'],
|
|
172
|
-
isEnabled: isQuickCommandEnabled,
|
|
173
|
-
onSelect: onSelectItem,
|
|
174
|
-
renderComponentItem: QuickCommandItem,
|
|
175
|
-
useData: () => allQuickCommands,
|
|
176
|
-
}}
|
|
177
|
-
/>
|
|
178
|
-
</InfiniteScroll>
|
|
179
|
-
</Box>
|
|
180
|
-
)
|
|
108
|
+
return <Selector
|
|
109
|
+
inputRef={inputRef}
|
|
110
|
+
favorite={{
|
|
111
|
+
useFavorites, onAddFavorite, onRemoveFavorite, favoriteIsSlug: true,
|
|
112
|
+
}}
|
|
113
|
+
selectorConfig={{
|
|
114
|
+
resourceName: 'Quick Command',
|
|
115
|
+
shortcut: '/',
|
|
116
|
+
icon: <Icon icon="QuickCommand" />,
|
|
117
|
+
searchProp: 'slug',
|
|
118
|
+
urlBuilder: (qc) => `/quick-command/${qc?.slug}`,
|
|
119
|
+
regex: quickCommandRegex,
|
|
120
|
+
sections: isTrial ? ['favorite', 'personal'] : ['favorite', 'personal', 'workspace', 'account', 'shared'],
|
|
121
|
+
isEnabled: isQuickCommandEnabled,
|
|
122
|
+
onSelect: onSelectItem,
|
|
123
|
+
renderComponentItem: QuickCommandItem,
|
|
124
|
+
useData: getQuickCommands,
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
181
127
|
}
|
|
@@ -39,6 +39,7 @@ export const MessageInput = ({ chatWindowRef, customInputMessage }:
|
|
|
39
39
|
const { handleKeyDown, handleKeyUp } = useUserEntryHistoryShortcut()
|
|
40
40
|
const isTrial = checkIsTrial()
|
|
41
41
|
const { isDragging, handleDrop, handleDragLeave } = useUploadDragDrop()
|
|
42
|
+
const [disabled, setDisabled] = useState(false)
|
|
42
43
|
|
|
43
44
|
usePasteUpload({
|
|
44
45
|
textAreaRef,
|
|
@@ -88,6 +89,14 @@ export const MessageInput = ({ chatWindowRef, customInputMessage }:
|
|
|
88
89
|
}, [chat, t])
|
|
89
90
|
|
|
90
91
|
const onSend = useCallback(async () => {
|
|
92
|
+
if (disabled) return
|
|
93
|
+
|
|
94
|
+
if (chat.get('isLoading')) {
|
|
95
|
+
setDisabled(true)
|
|
96
|
+
await chat.whenLoadingEnds()
|
|
97
|
+
setDisabled(false)
|
|
98
|
+
}
|
|
99
|
+
|
|
91
100
|
const message = chat.get('nextMessage')
|
|
92
101
|
const code = chat.get('codeSelection')
|
|
93
102
|
const language = chat.get('codeLanguage')
|
|
@@ -162,6 +171,7 @@ export const MessageInput = ({ chatWindowRef, customInputMessage }:
|
|
|
162
171
|
onKeyUp={handleKeyUp}
|
|
163
172
|
onIncreaseSize={() => setExpanded(false)}
|
|
164
173
|
onResetSize={() => !expansionLocked.current && setExpanded(true)}
|
|
174
|
+
disabled={disabled}
|
|
165
175
|
/>
|
|
166
176
|
</div>
|
|
167
177
|
</div>
|
package/src/views/Resources.tsx
CHANGED
|
@@ -46,29 +46,30 @@ const ResourcesPanel = () => {
|
|
|
46
46
|
const chat = widget.chatTabs.getAll().find(c => c.id === chatId)
|
|
47
47
|
return chat?.getMessages().find(m => m.id === messageId)?.getValue()
|
|
48
48
|
}, [messageId])
|
|
49
|
-
const [toolKits] = agentToolsClient.tools.useStatefulQuery({}, { enabled: !!message?.agent?.id })
|
|
50
49
|
const [agent] = agentToolsClient.agent.useStatefulQuery({ agentId: message?.agent?.id || '' },
|
|
51
50
|
{ enabled: !!message?.agent?.id })
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
const toolkits = useMemo(() => [
|
|
52
|
+
...agent?.toolkits?.builtin_toolkits ?? [],
|
|
53
|
+
...agent?.toolkits?.custom_toolkits ?? [],
|
|
54
|
+
...agent?.toolkits?.mcp_toolkits ?? [],
|
|
55
|
+
], [agent])
|
|
56
|
+
const tools = useMemo(() => message?.tools?.map(id => toolById(id, toolkits)), [messageId, toolkits])
|
|
56
57
|
const [agentsTools] = agentToolsClient.agentsByIds.useStatefulQuery({ searchAgentsRequest:{ ids: message?.tools || [] } })
|
|
57
|
-
const hasAgentTool = useMemo(() => message?.tools?.some(id => agentsTools?.find((agent) => agent.id === id)), [messageId
|
|
58
|
+
const hasAgentTool = useMemo(() => message?.tools?.some(id => agentsTools?.find((agent) => agent.id === id)), [messageId])
|
|
58
59
|
|
|
59
60
|
const header = (image?: string, label?: string) => (
|
|
60
61
|
<Row>
|
|
61
|
-
<ImageBox
|
|
62
|
+
<ImageBox>
|
|
62
63
|
<ImageWithFallback src={image} fallback={<Icon icon="Agent" />} aria-label={label} title={label} />
|
|
63
64
|
</ImageBox>
|
|
64
65
|
<Text style={{ marginLeft: '8px' }}>{label}</Text>
|
|
65
66
|
</Row>
|
|
66
67
|
)
|
|
67
|
-
|
|
68
|
-
return !!
|
|
68
|
+
|
|
69
|
+
return !!tools?.length && (
|
|
69
70
|
<>
|
|
70
71
|
<>
|
|
71
|
-
{
|
|
72
|
+
{tools.map(
|
|
72
73
|
(tool) =>
|
|
73
74
|
tool && (
|
|
74
75
|
<StyledAccordion key={tool.id} header={header(tool?.image, tool?.name)} appearance="card" maxHeight={120}>
|