@stack-spot/ai-chat-widget 1.0.0-dev.1769003016623 → 1.0.0-dev.1769537635610
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 +7 -0
- 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 +9 -232
- 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 +5 -1
- 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 +2 -0
- package/dist/components/Selector/index.d.ts.map +1 -1
- package/dist/components/Selector/index.js +5 -2
- 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 +45 -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/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 +6 -2
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.js +25 -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 +21 -4
- 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/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +6 -5
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/ChatHistory/utils.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.js +14 -7
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/Editor.d.ts.map +1 -1
- package/dist/views/Editor.js +16 -2
- package/dist/views/Editor.js.map +1 -1
- package/dist/views/Home/CustomAgent.js +3 -3
- package/dist/views/Home/CustomAgent.js.map +1 -1
- package/dist/views/MessageInput/AgentSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/AgentSelector.js +11 -1
- package/dist/views/MessageInput/AgentSelector.js.map +1 -1
- package/dist/views/MessageInput/ModelSwitcher/index.d.ts.map +1 -1
- package/dist/views/MessageInput/ModelSwitcher/index.js +3 -1
- package/dist/views/MessageInput/ModelSwitcher/index.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 +6 -6
- package/dist/views/MessageInput/ModelSwitcher/utils.js.map +1 -1
- package/dist/views/Resources.js +8 -5
- package/dist/views/Resources.js.map +1 -1
- package/dist/views/Steps/dictionary.d.ts +1 -1
- package/dist/views/Tools.js +3 -2
- 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 +14 -275
- package/src/chat-interceptors/send-message.ts +6 -2
- package/src/components/Selector/SelectVersion.tsx +55 -0
- package/src/components/Selector/index.tsx +11 -2
- package/src/components/Selector/styled.ts +47 -0
- package/src/hooks/enabled-feature-flags.ts +31 -0
- package/src/state/types.ts +1 -0
- package/src/utils/tools.ts +4 -4
- package/src/views/Agents/AgentDescription.tsx +48 -14
- package/src/views/Agents/AgentsTab.tsx +31 -13
- package/src/views/Agents/dictionary.ts +2 -1
- package/src/views/Chat/ChatMessage.tsx +8 -6
- package/src/views/ChatHistory/utils.ts +18 -10
- package/src/views/Editor.tsx +20 -3
- package/src/views/Home/CustomAgent.tsx +4 -4
- package/src/views/MessageInput/AgentSelector.tsx +15 -5
- package/src/views/MessageInput/ModelSwitcher/index.tsx +3 -2
- package/src/views/MessageInput/ModelSwitcher/utils.tsx +9 -8
- package/src/views/Resources.tsx +10 -6
- package/src/views/Tools.tsx +5 -4
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { aiClient, CancelledError, FixedChatRequest, StackspotAPIError } from '@stack-spot/portal-network'
|
|
2
|
-
import {
|
|
1
|
+
import { aiClient, CancelledError, FixedChatRequest, QCContext as QCContextLib, StackspotAPIError } from '@stack-spot/portal-network'
|
|
2
|
+
import { QuickCommandResponse, QuickCommandStepResult } from '@stack-spot/portal-network/api/ai'
|
|
3
3
|
import { Dictionary, interpolate, translate } from '@stack-spot/portal-translate'
|
|
4
|
-
import { isNil } from 'lodash'
|
|
5
4
|
import type { editor } from 'monaco-editor'
|
|
6
5
|
import { ulid } from 'ulid'
|
|
7
6
|
import { AbortedError } from '../AbortedError'
|
|
@@ -11,7 +10,6 @@ import { ChatState } from '../state/ChatState'
|
|
|
11
10
|
import { LabeledWithImage } from '../state/types'
|
|
12
11
|
import { WidgetState } from '../state/WidgetState'
|
|
13
12
|
import { buildConversationContext } from '../utils/chat'
|
|
14
|
-
import { getSizeOfString } from '../utils/string'
|
|
15
13
|
import { CustomInputs } from './CustomInputs'
|
|
16
14
|
|
|
17
15
|
type SlugExecution = Record<string, QuickCommandStepResult>
|
|
@@ -73,132 +71,6 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
|
|
|
73
71
|
)
|
|
74
72
|
}
|
|
75
73
|
|
|
76
|
-
/**
|
|
77
|
-
* Runs an Router step of a quick command.
|
|
78
|
-
*/
|
|
79
|
-
async function runRouterStep(
|
|
80
|
-
ctx: QCContext,
|
|
81
|
-
stepIndex: number, iteration: Record<string, number>,
|
|
82
|
-
progress: { update: (index: number) => void, remove: () => void },
|
|
83
|
-
) {
|
|
84
|
-
const { qc: { slug, steps }, code, resultMap, customInputs } = ctx
|
|
85
|
-
const step = steps![stepIndex]
|
|
86
|
-
const inputData = Object.keys(customInputs).length > 0 && code ? { ...customInputs, [code]: code } : code ?? customInputs
|
|
87
|
-
try {
|
|
88
|
-
if (step.slug in iteration) {
|
|
89
|
-
iteration[step.slug] = iteration[step.slug] + 1
|
|
90
|
-
} else {
|
|
91
|
-
iteration[step.slug] = 1
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const { next_step_slug } = await aiClient.calculateNextStep.mutate({
|
|
95
|
-
stepSlug: step.slug,
|
|
96
|
-
slug: slug,
|
|
97
|
-
quickCommandEvaluateStepRouterRequest: {
|
|
98
|
-
executions_count: iteration[step.slug],
|
|
99
|
-
input_data: inputData,
|
|
100
|
-
slugs_executions: resultMap,
|
|
101
|
-
},
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
if (next_step_slug === step.slug) {
|
|
105
|
-
return runStepsRecursively(stepIndex, progress, ctx, iteration)
|
|
106
|
-
}
|
|
107
|
-
const nextStepIndex = steps?.findIndex((step) => step.slug === next_step_slug)
|
|
108
|
-
|
|
109
|
-
if (isNil(nextStepIndex) || nextStepIndex === -1) return
|
|
110
|
-
|
|
111
|
-
return runStepsRecursively(nextStepIndex, progress, ctx, iteration)
|
|
112
|
-
}
|
|
113
|
-
catch (error: any) {
|
|
114
|
-
// eslint-disable-next-line no-console
|
|
115
|
-
console.error('Error executing QC step', error)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Runs a fetch step of a quick command and puts the result in the `resultMap` of the context passed as parameter.
|
|
121
|
-
*/
|
|
122
|
-
async function runFetchStep(ctx: QCContext, stepIndex: number) {
|
|
123
|
-
const { qc: { slug, steps }, code, context, resultMap, customInputs, executionId, signal } = ctx
|
|
124
|
-
const step = steps![stepIndex] as QuickCommandStepFetchResponse
|
|
125
|
-
if (step.is_remote) {
|
|
126
|
-
ctx.isRemote = true
|
|
127
|
-
|
|
128
|
-
const { data } = await aiClient.fetchStepOfQuickCommandRemotely.mutate({
|
|
129
|
-
slug, stepSlug: step.slug,
|
|
130
|
-
quickCommandsExecutionRequest: {
|
|
131
|
-
code_selection: code, context, qc_execution_id: executionId,
|
|
132
|
-
slugs_executions: { ...resultMap, ...customInputs },
|
|
133
|
-
},
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
//data is the return of the request in the QC so we do not have full control over the response
|
|
137
|
-
//We handle the usual format with body, status_code and headers, but we might also handle other formats
|
|
138
|
-
const responseData = data as any
|
|
139
|
-
resultMap[step.slug] = {
|
|
140
|
-
status: responseData.status_code || 200,
|
|
141
|
-
data: JSON.stringify(responseData.body) ?? JSON.stringify(responseData),
|
|
142
|
-
headers: responseData.headers ?? {},
|
|
143
|
-
}
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const { headers, data, method, url } = await aiClient.fetchStepOfQuickCommand.mutate({
|
|
148
|
-
slug,
|
|
149
|
-
stepSlug: step.slug,
|
|
150
|
-
quickCommandsExecutionRequest: {
|
|
151
|
-
input_data: code, context, qc_execution_id: executionId,
|
|
152
|
-
slugs_executions: { ...resultMap, ...customInputs },
|
|
153
|
-
},
|
|
154
|
-
}, signal)
|
|
155
|
-
const body = ['get', 'head'].includes(method.toLowerCase()) ? undefined : data
|
|
156
|
-
const response = await fetch(url, { headers: headers || undefined, body, method, signal })
|
|
157
|
-
if (!response.ok) throw new Error(`Failed to execute step "${step.slug}" of quick command "${slug}". Status ${response.status}.`)
|
|
158
|
-
resultMap[step.slug] = {
|
|
159
|
-
status: response.status,
|
|
160
|
-
data: await response.text(),
|
|
161
|
-
headers: Object.fromEntries(response.headers.entries()),
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Runs an LLM step of a quick command and puts the result in the `resultMap` of the context passed as parameter.
|
|
167
|
-
*/
|
|
168
|
-
async function runLLMStep(
|
|
169
|
-
{ qc: { slug, steps }, code, customInputs, context, executionId, resultMap, signal }: QCContext,
|
|
170
|
-
stepIndex: number,
|
|
171
|
-
) {
|
|
172
|
-
const step = steps![stepIndex] as QuickCommandStepLlmResponse
|
|
173
|
-
let stepContext = context
|
|
174
|
-
if (!step.use_uploaded_files) {
|
|
175
|
-
const { upload_ids: _upload_ids, ...contextDataProps } = context
|
|
176
|
-
stepContext = { ...contextDataProps }
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const stream = aiClient.streamLlmStepOfQuickCommand(
|
|
180
|
-
slug,
|
|
181
|
-
step.slug,
|
|
182
|
-
{
|
|
183
|
-
input_data: code,
|
|
184
|
-
context: stepContext,
|
|
185
|
-
qc_execution_id: executionId,
|
|
186
|
-
slugs_executions: { ...resultMap, ...customInputs },
|
|
187
|
-
},
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
signal.addEventListener('abort', () => stream.cancel())
|
|
191
|
-
|
|
192
|
-
let finalValue: QuickCommandPromptResponse2
|
|
193
|
-
try {
|
|
194
|
-
finalValue = await stream.getValue()
|
|
195
|
-
resultMap[step.slug] = finalValue
|
|
196
|
-
} catch (error: any) {
|
|
197
|
-
// eslint-disable-next-line no-console
|
|
198
|
-
console.error('Error executing QC step', error)
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
74
|
function updateProgressMessageForStep(message: ChatEntry, qc: QuickCommandResponse, stepIndex: number) {
|
|
203
75
|
const t = translate(dictionary)
|
|
204
76
|
message.setValue({
|
|
@@ -230,115 +102,17 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
|
|
|
230
102
|
}, showImmediately ? 0 : progressMessageDelayMS)
|
|
231
103
|
return controller
|
|
232
104
|
}
|
|
233
|
-
async function getScriptStepStatus(
|
|
234
|
-
scriptExecutionId: string,
|
|
235
|
-
interval = 5000,
|
|
236
|
-
maxAttempts = 500,
|
|
237
|
-
currentAttempt = 0,
|
|
238
|
-
): Promise<QuickCommandScriptExecutionResponse> {
|
|
239
|
-
if (currentAttempt >= maxAttempts) {
|
|
240
|
-
throw new Error('Max attempts reached in verify script status')
|
|
241
|
-
}
|
|
242
|
-
await aiClient.getStatusScriptStep.invalidate({ scriptExecutionId })
|
|
243
|
-
const response = await aiClient.getStatusScriptStep.query({ scriptExecutionId })
|
|
244
|
-
|
|
245
|
-
if (response.status === 'success') {
|
|
246
|
-
return response
|
|
247
|
-
}
|
|
248
105
|
|
|
249
|
-
|
|
250
|
-
throw response
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
await new Promise(resolve => {setTimeout(resolve, interval)})
|
|
254
|
-
|
|
255
|
-
return getScriptStepStatus(scriptExecutionId, interval, maxAttempts, currentAttempt + 1)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
async function runScriptStep(
|
|
259
|
-
{ qc: { slug, steps }, code, context, resultMap, customInputs, signal }: QCContext,
|
|
260
|
-
stepIndex: number,
|
|
261
|
-
) {
|
|
262
|
-
const step = steps![stepIndex] as QuickCommandStepLlmResponse
|
|
263
|
-
let stepContext = context
|
|
264
|
-
if (!step.use_uploaded_files) {
|
|
265
|
-
const { upload_ids: _upload_ids, ...contextDataProps } = context
|
|
266
|
-
stepContext = { ...contextDataProps }
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
const { script_execution_id } = await aiClient.startScriptStep.mutate({
|
|
271
|
-
stepSlug: step.slug,
|
|
272
|
-
slug: slug,
|
|
273
|
-
quickCommandStartScriptRequest: {
|
|
274
|
-
input_data: code,
|
|
275
|
-
custom_inputs: customInputs,
|
|
276
|
-
context: stepContext,
|
|
277
|
-
slugs_executions: resultMap,
|
|
278
|
-
},
|
|
279
|
-
}, signal)
|
|
280
|
-
const scriptResult = await getScriptStepStatus(script_execution_id)
|
|
281
|
-
resultMap[step.slug] = scriptResult
|
|
282
|
-
}
|
|
283
|
-
catch (error: any) {
|
|
284
|
-
throw new Error(`Failed to execute step "${step.slug}" of quick command "${slug}". Error ${error}.`)
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async function runStepsRecursively(currentIndex: number, progress: { update: (index: number) => void, remove: () => void },
|
|
289
|
-
ctx: QCContext, iteration: Record<string, number>) {
|
|
290
|
-
const { qc } = ctx
|
|
291
|
-
|
|
292
|
-
if (!qc.steps || currentIndex >= qc.steps?.length) return
|
|
293
|
-
progress.update(currentIndex)
|
|
294
|
-
|
|
295
|
-
if (qc.steps[currentIndex].type === 'ROUTER') {
|
|
296
|
-
await runRouterStep(ctx, currentIndex, iteration, progress)
|
|
297
|
-
return
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (qc.steps[currentIndex].type === 'SCRIPT') {
|
|
301
|
-
await runScriptStep(ctx, currentIndex)
|
|
302
|
-
} else {
|
|
303
|
-
await (qc.steps[currentIndex].type === 'FETCH' ? runFetchStep(ctx, currentIndex) : runLLMStep(ctx, currentIndex))
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const currentStep = qc.steps?.[currentIndex] as QuickCommandStepFetchResponse | QuickCommandStepLlmResponse
|
|
307
|
-
let nextIndex = currentIndex + 1
|
|
308
|
-
|
|
309
|
-
let nextStepSlug = currentStep.next_step_slug
|
|
310
|
-
const stepResult = ctx.resultMap[currentStep.slug]
|
|
311
|
-
if (stepResult && typeof stepResult !== 'string' && 'answer_status' in stepResult && !!stepResult.answer_status?.next_step_slug) {
|
|
312
|
-
nextStepSlug = stepResult.answer_status.next_step_slug
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (nextStepSlug) {
|
|
316
|
-
nextIndex = nextStepSlug === 'end' ?
|
|
317
|
-
qc.steps.length : qc.steps?.findIndex((step) => step.slug === nextStepSlug)
|
|
318
|
-
}
|
|
319
|
-
await runStepsRecursively(nextIndex, progress, ctx, iteration)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async function runSteps(ctx: QCContext) {
|
|
106
|
+
async function runQC(ctx: QCContext) {
|
|
323
107
|
const progress = showProgressMessage(ctx)
|
|
324
|
-
try {
|
|
325
|
-
await runStepsRecursively(0, progress, ctx, {})
|
|
326
|
-
} finally {
|
|
327
|
-
progress.remove()
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
108
|
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
slugs_executions: { ...resultMap, ...customInputs },
|
|
339
|
-
},
|
|
340
|
-
}, signal)
|
|
341
|
-
return formatted.result
|
|
109
|
+
// await runStepsRecursively(0, progress, ctx, {}
|
|
110
|
+
const { qc, ...props } = ctx
|
|
111
|
+
const context: QCContextLib = { ...props, slug: qc.slug }
|
|
112
|
+
const finalResult = await aiClient.runQuickCommand(context, progress)
|
|
113
|
+
|
|
114
|
+
progress.remove()
|
|
115
|
+
return finalResult
|
|
342
116
|
}
|
|
343
117
|
|
|
344
118
|
// opens a new chat tab if the quick command doesn't preserve the conversation and the current conversation isn't clean.
|
|
@@ -368,7 +142,7 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
|
|
|
368
142
|
} catch (error) {
|
|
369
143
|
throw error instanceof StackspotAPIError && error.status === 404 ? new Error(t.notFound) : error
|
|
370
144
|
}
|
|
371
|
-
|
|
145
|
+
|
|
372
146
|
if (ctx.qc.use_selected_code && (!code && ctx.context.upload_ids?.length === 0)) {
|
|
373
147
|
widget.set('panel', 'editor')
|
|
374
148
|
ctx.chat.pushMessage(new ChatEntry({
|
|
@@ -377,44 +151,12 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
|
|
|
377
151
|
agent: ctx.chat.get('agent'),
|
|
378
152
|
content: t.requiresSelection,
|
|
379
153
|
}))
|
|
380
|
-
return
|
|
154
|
+
return
|
|
381
155
|
}
|
|
382
156
|
manageConversationContext(ctx)
|
|
383
157
|
await computeCustomInputs(ctx)
|
|
384
|
-
await
|
|
385
|
-
return
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* This registers a quick command event in the backend (analytics).
|
|
390
|
-
*/
|
|
391
|
-
async function registerAnalyticsEvent({ qc, isRemote, executionId, code = '', context }: QCContext, status: string, start: number) {
|
|
392
|
-
const now = new Date().getTime()
|
|
393
|
-
try {
|
|
394
|
-
await aiClient.createEvent.mutate({
|
|
395
|
-
body: [{
|
|
396
|
-
type: 'custom_quick_command_execution',
|
|
397
|
-
quick_command_event: {
|
|
398
|
-
type: qc.type || '',
|
|
399
|
-
duration_execution: now - start,
|
|
400
|
-
status_execution: status,
|
|
401
|
-
slug: qc.slug,
|
|
402
|
-
qc_execution_id: executionId,
|
|
403
|
-
id: qc.id,
|
|
404
|
-
//@ts-ignore
|
|
405
|
-
is_remote: isRemote,
|
|
406
|
-
},
|
|
407
|
-
code,
|
|
408
|
-
context,
|
|
409
|
-
knowledge_sources: [],
|
|
410
|
-
size: getSizeOfString(code),
|
|
411
|
-
generated_at: now,
|
|
412
|
-
}],
|
|
413
|
-
})
|
|
414
|
-
} catch (error) {
|
|
415
|
-
// eslint-disable-next-line no-console
|
|
416
|
-
console.warn('Failed to register event: quick command.')
|
|
417
|
-
}
|
|
158
|
+
const result = await runQC(ctx)
|
|
159
|
+
return result
|
|
418
160
|
}
|
|
419
161
|
|
|
420
162
|
/**
|
|
@@ -462,12 +204,10 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
|
|
|
462
204
|
signal,
|
|
463
205
|
}
|
|
464
206
|
chat.set('isLoading', true)
|
|
465
|
-
const start = new Date().getTime()
|
|
466
207
|
try {
|
|
467
208
|
const result = await runQuickCommand(ctx)
|
|
468
209
|
if (result) {
|
|
469
210
|
outputResult(ctx, result)
|
|
470
|
-
registerAnalyticsEvent(ctx, '200', start)
|
|
471
211
|
}
|
|
472
212
|
} catch (error: any) {
|
|
473
213
|
let message = error.message || `${error}`
|
|
@@ -480,7 +220,6 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
|
|
|
480
220
|
agent: chat.get('agent'),
|
|
481
221
|
type: 'text',
|
|
482
222
|
}))
|
|
483
|
-
registerAnalyticsEvent(ctx, message, start)
|
|
484
223
|
}
|
|
485
224
|
ctx.chat.set('isLoading', false)
|
|
486
225
|
// prevents the next interceptors from running
|
|
@@ -202,8 +202,12 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
|
|
|
202
202
|
chat.set('label', content || entry.getValue().upload?.[0]?.name || 'Chat')
|
|
203
203
|
chat.untitled = false
|
|
204
204
|
}
|
|
205
|
-
|
|
206
|
-
const stream = aiClient.sendChatMessage({
|
|
205
|
+
|
|
206
|
+
const stream = aiClient.sendChatMessage({
|
|
207
|
+
context,
|
|
208
|
+
user_prompt: buildPrompt(content, data),
|
|
209
|
+
agent_version_number: chat.get('agent')?.agent_version_number,
|
|
210
|
+
})
|
|
207
211
|
signal.addEventListener('abort', () => stream.cancel())
|
|
208
212
|
const botEntry = ChatEntry.createStreamedBotEntry()
|
|
209
213
|
// we add the chat entry and show the streaming if the streaming feature is enabled
|
|
@@ -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
|
+
}
|
|
@@ -10,6 +10,7 @@ import { useCurrentChatState, useWidgetState } from '../../context/hooks'
|
|
|
10
10
|
import { getUrlToStackSpotAI } from '../../utils/url'
|
|
11
11
|
import { ButtonFavorite } from '../ButtonFavorite'
|
|
12
12
|
import { Fading } from '../Fading'
|
|
13
|
+
import { SelectVersion } from './SelectVersion'
|
|
13
14
|
import { SelectorBox } from './styled'
|
|
14
15
|
|
|
15
16
|
type SectionVisibility = AgentVisibilityLevel | VisibilityLevelEnum
|
|
@@ -20,6 +21,7 @@ interface Item {
|
|
|
20
21
|
slug: string,
|
|
21
22
|
description: string,
|
|
22
23
|
visibility_level: SectionVisibility,
|
|
24
|
+
version_number?: number,
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
interface Favorite<T> {
|
|
@@ -69,6 +71,8 @@ interface SelectorConfig<T> {
|
|
|
69
71
|
regex: RegExp,
|
|
70
72
|
urlBuilder: (item: T) => string,
|
|
71
73
|
searchProp: keyof T,
|
|
74
|
+
isEnabledVersionContent?: boolean,
|
|
75
|
+
useVersions?: (id: string, enabled: boolean) => number[] | undefined,
|
|
72
76
|
isEnabled: boolean,
|
|
73
77
|
sections: SectionVisibility[],
|
|
74
78
|
renderComponentItem: FC<T>,
|
|
@@ -91,10 +95,11 @@ interface SelectorProps<T> {
|
|
|
91
95
|
|
|
92
96
|
const ListItem = <T extends Item>({ item, selectorConfig, onSelect, favorite }: ListItemProps<T>) => {
|
|
93
97
|
const t = useTranslate(dictionary)
|
|
94
|
-
const { urlBuilder, renderComponentItem } = selectorConfig
|
|
98
|
+
const { urlBuilder, renderComponentItem, useVersions, isEnabledVersionContent } = selectorConfig
|
|
95
99
|
const linkTitle = interpolate(t.open, item.slug)
|
|
96
100
|
const listFavorites = favorite?.useFavorites?.()
|
|
97
|
-
|
|
101
|
+
const [selectedVersion, setSelectedVersion] = useState(item.version_number)
|
|
102
|
+
|
|
98
103
|
return (
|
|
99
104
|
<li>
|
|
100
105
|
<button
|
|
@@ -106,6 +111,10 @@ const ListItem = <T extends Item>({ item, selectorConfig, onSelect, favorite }:
|
|
|
106
111
|
>
|
|
107
112
|
{renderComponentItem(item)}
|
|
108
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
|
+
/> }
|
|
109
118
|
<IconLink
|
|
110
119
|
icon="ExternalLink"
|
|
111
120
|
title={linkTitle}
|
|
@@ -156,3 +156,50 @@ 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: 28px;
|
|
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
|
+
position: relative;
|
|
196
|
+
top: 0;
|
|
197
|
+
left: 0;
|
|
198
|
+
}
|
|
199
|
+
> div {
|
|
200
|
+
position: relative;
|
|
201
|
+
.options {
|
|
202
|
+
overflow: hidden;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
`
|
|
@@ -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/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)
|