@stack-spot/ai-chat-widget 1.0.0-dev.1769120820021 → 1.0.0-dev.1769797270860

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +2 -1
  3. package/dist/StackspotAIWidget.d.ts +1 -0
  4. package/dist/StackspotAIWidget.d.ts.map +1 -1
  5. package/dist/StackspotAIWidget.js +1 -0
  6. package/dist/StackspotAIWidget.js.map +1 -1
  7. package/dist/app-metadata.json +15 -3
  8. package/dist/chat-interceptors/quick-commands.d.ts +15 -0
  9. package/dist/chat-interceptors/quick-commands.d.ts.map +1 -1
  10. package/dist/chat-interceptors/quick-commands.js +29 -234
  11. package/dist/chat-interceptors/quick-commands.js.map +1 -1
  12. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  13. package/dist/chat-interceptors/send-message.js +39 -38
  14. package/dist/chat-interceptors/send-message.js.map +1 -1
  15. package/dist/components/FileDescription.js +1 -1
  16. package/dist/components/FileDescription.js.map +1 -1
  17. package/dist/components/Markdown.d.ts.map +1 -1
  18. package/dist/components/Markdown.js +22 -6
  19. package/dist/components/Markdown.js.map +1 -1
  20. package/dist/components/Selector/index.d.ts +1 -1
  21. package/dist/components/Selector/index.d.ts.map +1 -1
  22. package/dist/components/Selector/index.js +3 -3
  23. package/dist/components/Selector/index.js.map +1 -1
  24. package/dist/components/TabManager.d.ts.map +1 -1
  25. package/dist/components/TabManager.js +20 -4
  26. package/dist/components/TabManager.js.map +1 -1
  27. package/dist/state/ChatState.d.ts +5 -0
  28. package/dist/state/ChatState.d.ts.map +1 -1
  29. package/dist/state/ChatState.js.map +1 -1
  30. package/dist/utils/chat.d.ts +4 -12
  31. package/dist/utils/chat.d.ts.map +1 -1
  32. package/dist/utils/chat.js +20 -19
  33. package/dist/utils/chat.js.map +1 -1
  34. package/dist/utils/knowledge-source.js +1 -1
  35. package/dist/utils/knowledge-source.js.map +1 -1
  36. package/dist/views/ChatHistory/utils.d.ts.map +1 -1
  37. package/dist/views/ChatHistory/utils.js +9 -5
  38. package/dist/views/ChatHistory/utils.js.map +1 -1
  39. package/dist/views/Editor.d.ts.map +1 -1
  40. package/dist/views/Editor.js +16 -2
  41. package/dist/views/Editor.js.map +1 -1
  42. package/dist/views/KnowledgeSources.js +1 -1
  43. package/dist/views/KnowledgeSources.js.map +1 -1
  44. package/dist/views/MessageInput/ContextBar.js +1 -1
  45. package/dist/views/MessageInput/ContextBar.js.map +1 -1
  46. package/dist/views/Steps/dictionary.d.ts +1 -1
  47. package/package.json +5 -2
  48. package/src/StackspotAIWidget.tsx +1 -0
  49. package/src/app-metadata.json +15 -3
  50. package/src/chat-interceptors/quick-commands.ts +38 -278
  51. package/src/chat-interceptors/send-message.ts +40 -40
  52. package/src/components/FileDescription.tsx +1 -1
  53. package/src/components/Markdown.tsx +42 -23
  54. package/src/components/Selector/index.tsx +6 -6
  55. package/src/components/TabManager.tsx +31 -8
  56. package/src/state/ChatState.ts +6 -0
  57. package/src/utils/chat.ts +23 -22
  58. package/src/utils/knowledge-source.ts +1 -1
  59. package/src/views/ChatHistory/utils.ts +11 -6
  60. package/src/views/Editor.tsx +20 -3
  61. package/src/views/KnowledgeSources.tsx +1 -1
  62. package/src/views/MessageInput/ContextBar.tsx +1 -1
@@ -1,24 +1,43 @@
1
- import { aiClient, CancelledError, FixedChatRequest, StackspotAPIError } from '@stack-spot/portal-network'
2
- import { QuickCommandStepResult, QuickCommandPromptResponse2, QuickCommandResponse, QuickCommandScriptExecutionResponse, QuickCommandStepFetchResponse, QuickCommandStepLlmResponse } from '@stack-spot/portal-network/api/ai'
1
+ import { aiClient, CancelledError, 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'
7
+ import appData from '../app-metadata.json'
8
8
  import { quickCommandRegex } from '../regex'
9
9
  import { ChatEntry } from '../state/ChatEntry'
10
10
  import { ChatState } from '../state/ChatState'
11
11
  import { LabeledWithImage } from '../state/types'
12
12
  import { WidgetState } from '../state/WidgetState'
13
- import { buildConversationContext } from '../utils/chat'
14
- import { getSizeOfString } from '../utils/string'
13
+ import { defaultLanguage } from '../utils/programming-languages'
15
14
  import { CustomInputs } from './CustomInputs'
16
15
 
16
+ export function buildQuickCommandContext(state: ChatState, message?: ChatEntry) {
17
+ return {
18
+ workspace: state.get('workspace')?.id,
19
+ conversation_id: state.id,
20
+ stack_id: state.get('stack')?.id,
21
+ language: state.get('codeLanguage') || (state.get('codeSelection') ? defaultLanguage : undefined),
22
+ knowledge_sources: state.get('knowledgeSources')?.map(ks => ks.id),
23
+ upload_ids: message?.getValue().upload?.map(f => f.id),
24
+ agent_id: state.get('agent')?.id,
25
+ agent_built_in: state.get('agent')?.builtIn,
26
+ os: navigator.userAgent,
27
+ platform: 'web-widget',
28
+ platform_version: navigator.userAgent,
29
+ stackspot_ai_version: appData.version,
30
+ selected_model_id: state.get('selected_model_id'),
31
+ }
32
+ }
33
+
34
+ type QuickCommandContext = ReturnType<typeof buildQuickCommandContext>
35
+
17
36
  type SlugExecution = Record<string, QuickCommandStepResult>
18
37
 
19
38
  interface QCContext {
20
39
  qc: QuickCommandResponse,
21
- context: Required<FixedChatRequest>['context'],
40
+ context: QuickCommandContext,
22
41
  resultMap: SlugExecution,
23
42
  customInputs: Record<string, string>,
24
43
  chat: ChatState,
@@ -73,132 +92,6 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
73
92
  )
74
93
  }
75
94
 
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
95
  function updateProgressMessageForStep(message: ChatEntry, qc: QuickCommandResponse, stepIndex: number) {
203
96
  const t = translate(dictionary)
204
97
  message.setValue({
@@ -230,115 +123,17 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
230
123
  }, showImmediately ? 0 : progressMessageDelayMS)
231
124
  return controller
232
125
  }
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
-
249
- if (response.status === 'failure') {
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
126
 
322
- async function runSteps(ctx: QCContext) {
127
+ async function runQC(ctx: QCContext) {
323
128
  const progress = showProgressMessage(ctx)
324
- try {
325
- await runStepsRecursively(0, progress, ctx, {})
326
- } finally {
327
- progress.remove()
328
- }
329
- }
330
129
 
331
- async function formatResult({ qc, code, executionId, context, resultMap, customInputs, signal }: QCContext) {
332
- const formatted = await aiClient.formatResultOfQuickCommand.mutate({
333
- slug: qc.slug,
334
- quickCommandsExecutionRequest: {
335
- input_data: code,
336
- context,
337
- qc_execution_id: executionId,
338
- slugs_executions: { ...resultMap, ...customInputs },
339
- },
340
- }, signal)
341
- return formatted.result
130
+ // await runStepsRecursively(0, progress, ctx, {}
131
+ const { qc, ...props } = ctx
132
+ const context: QCContextLib = { ...props, slug: qc.slug }
133
+ const finalResult = await aiClient.runQuickCommand(context, progress)
134
+
135
+ progress.remove()
136
+ return finalResult
342
137
  }
343
138
 
344
139
  // opens a new chat tab if the quick command doesn't preserve the conversation and the current conversation isn't clean.
@@ -368,7 +163,7 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
368
163
  } catch (error) {
369
164
  throw error instanceof StackspotAPIError && error.status === 404 ? new Error(t.notFound) : error
370
165
  }
371
-
166
+
372
167
  if (ctx.qc.use_selected_code && (!code && ctx.context.upload_ids?.length === 0)) {
373
168
  widget.set('panel', 'editor')
374
169
  ctx.chat.pushMessage(new ChatEntry({
@@ -377,44 +172,12 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
377
172
  agent: ctx.chat.get('agent'),
378
173
  content: t.requiresSelection,
379
174
  }))
380
- return
175
+ return
381
176
  }
382
177
  manageConversationContext(ctx)
383
178
  await computeCustomInputs(ctx)
384
- await runSteps(ctx)
385
- return formatResult(ctx)
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
- }
179
+ const result = await runQC(ctx)
180
+ return result
418
181
  }
419
182
 
420
183
  /**
@@ -455,19 +218,17 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
455
218
  qc: { slug } as QuickCommandResponse,
456
219
  chat,
457
220
  code: chat.get('codeSelection'),
458
- context: buildConversationContext(chat, entry) ?? {},
221
+ context: buildQuickCommandContext(chat, entry) ?? {},
459
222
  executionId: ulid(),
460
223
  resultMap: {},
461
224
  customInputs: {},
462
225
  signal,
463
226
  }
464
227
  chat.set('isLoading', true)
465
- const start = new Date().getTime()
466
228
  try {
467
229
  const result = await runQuickCommand(ctx)
468
230
  if (result) {
469
231
  outputResult(ctx, result)
470
- registerAnalyticsEvent(ctx, '200', start)
471
232
  }
472
233
  } catch (error: any) {
473
234
  let message = error.message || `${error}`
@@ -480,7 +241,6 @@ export function createQuickCommandInterceptor(widget: WidgetState, getEditor: ()
480
241
  agent: chat.get('agent'),
481
242
  type: 'text',
482
243
  }))
483
- registerAnalyticsEvent(ctx, message, start)
484
244
  }
485
245
  ctx.chat.set('isLoading', false)
486
246
  // prevents the next interceptors from running
@@ -1,5 +1,4 @@
1
- import { AgentInfo, aiClient, ChatResponseWithPMResources, ChatResponseWithSteps, ChatStep, StackspotAPIError, StreamCanceledError } from '@stack-spot/portal-network'
2
- import { ChatResponse3 } from '@stack-spot/portal-network/api/ai'
1
+ import { AgentInfo, aiClient, ChatResponse, ChatResponseWithPMResources, ChatResponseWithSteps, ChatStep, genAiInferenceClient, StackspotAPIError, StreamCanceledError } from '@stack-spot/portal-network'
3
2
  import { findLast } from 'lodash'
4
3
  import { ChatEntry, KnowledgeSource, TextChatEntry } from '../state/ChatEntry'
5
4
  import { ChatState } from '../state/ChatState'
@@ -27,13 +26,13 @@ export function createEntryValueFromChatResponse(
27
26
  const entry = {
28
27
  agentType: 'bot',
29
28
  type: 'md',
30
- content: response.answer ?? '',
29
+ content: response.message ?? '',
31
30
  messageId: response.message_id ?? undefined,
32
31
  knowledgeSources,
33
32
  agent: agent,
34
33
  updated: includeDate ? new Date().toISOString() : undefined,
35
34
  steps: response.steps,
36
- tools: response.tools,
35
+ tools: response.tools_id,
37
36
  opportunities: response.opportunities,
38
37
  hypothesis: response.hypothesis,
39
38
  prfaq: response.prfaq,
@@ -112,39 +111,44 @@ const updatePlanningMessage = (messages: ChatEntry[]) => {
112
111
 
113
112
  export function helperSendMessage(messages: ChatEntry[], value: Partial<ChatResponseWithSteps> & { opportunities?: any },
114
113
  chat: ChatState, botEntry: ChatEntry, knowledgeSources: KnowledgeSource[] | undefined) {
115
- if (value.agent_info?.type === 'planning') {
116
- if (value.agent_info.action === 'start') {
117
- chat.set('isPlaning', true)
118
- } else {
119
- chat.set('isPlaning', false)
114
+ for (const agent_info of value.agent_info ?? []) {
115
+ if (agent_info?.type === 'planning') {
116
+ if (agent_info.action === 'start') {
117
+ chat.set('isPlaning', true)
118
+ } else {
119
+ chat.set('isPlaning', false)
120
+ }
120
121
  }
121
- }
122
122
 
123
- if (value.agent_info?.type === 'chat' && value.agent_info?.action === 'end') {
124
- //When an error happens, the step can still be running, so we enforce the error
125
- const stepRunning = findLast(value.steps, (item) => item.status === 'running')
126
- if (stepRunning?.status) {
127
- stepRunning.status = 'error'
123
+ if (agent_info?.type === 'chat' && agent_info?.action === 'end') {
124
+ //When an error happens, the step can still be running, so we enforce the error
125
+ const stepRunning = findLast(value.steps, (item) => item.status === 'running')
126
+ if (stepRunning?.status) {
127
+ stepRunning.status = 'error'
128
+ }
128
129
  }
129
- }
130
130
 
131
- if (value.sources?.length !== knowledgeSources?.length && chat.get('features').showSourcesInResponse) {
132
- knowledgeSources = genericSourcesToKnowledgeSources(value.sources)
133
- }
131
+ const lastPlanningAwaiting = getLastPlanningAwaiting(messages)
132
+ if (lastPlanningAwaiting && value.steps) {
133
+ value.steps?.map(step => {
134
+ if (step.type === 'planning') {
135
+ updatePlanningMessage(messages)
136
+ } else if (step.type === 'step') {
137
+ updateStepMessage(step, messages)
138
+ }
139
+ })
140
+ }
134
141
 
135
- const lastPlanningAwaiting = getLastPlanningAwaiting(messages)
136
- if (lastPlanningAwaiting && value.steps) {
137
- value.steps?.map(step => {
138
- if (step.type === 'planning') {
139
- updatePlanningMessage(messages)
140
- } else if (step.type === 'step') {
141
- updateStepMessage(step, messages)
142
- }
143
- })
142
+ if (agent_info?.type === 'tool' && agent_info?.action !== 'awaiting_approval') {
143
+ updateToolStatus(agent_info, messages)
144
+ }
144
145
  }
145
146
 
146
- if (value.agent_info?.type === 'tool' && value.agent_info?.action !== 'awaiting_approval') {
147
- updateToolStatus(value.agent_info, messages)
147
+ if (chat.get('features').showSourcesInResponse) {
148
+ const mergedSources = [...(value.source ?? []), ...(value.cross_account_source ?? [])]
149
+ if (mergedSources.length !== (knowledgeSources?.length ?? 0)) {
150
+ knowledgeSources = genericSourcesToKnowledgeSources(mergedSources)
151
+ }
148
152
  }
149
153
 
150
154
  if (value.steps) {
@@ -193,7 +197,7 @@ export function helperSendMessage(messages: ChatEntry[], value: Partial<ChatResp
193
197
  export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState, signal: AbortSignal) {
194
198
  const { agentType, content, data } = entry.getValue()
195
199
  if (agentType !== 'user') return
196
- const context = buildConversationContext(chat, entry)
200
+ const context = buildConversationContext(chat, entry, buildPrompt(content, data))
197
201
  chat.set('isLoading', true)
198
202
  const untitled = chat.untitled
199
203
  const messages = chat.getMessages()
@@ -202,12 +206,8 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
202
206
  chat.set('label', content || entry.getValue().upload?.[0]?.name || 'Chat')
203
207
  chat.untitled = false
204
208
  }
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
- })
209
+
210
+ const stream = genAiInferenceClient.sendChatMessage(context)
211
211
  signal.addEventListener('abort', () => stream.cancel())
212
212
  const botEntry = ChatEntry.createStreamedBotEntry()
213
213
  // we add the chat entry and show the streaming if the streaming feature is enabled
@@ -220,14 +220,14 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
220
220
  helperSendMessage(messages, value, chat, botEntry, knowledgeSources)
221
221
  })
222
222
 
223
- let finalValue: Partial<ChatResponse3> | undefined
223
+ let finalValue: Partial<ChatResponse> | undefined
224
224
  try {
225
225
  finalValue = await stream.getValue()
226
226
 
227
227
  const lastPlanningAwaiting = getLastPlanningAwaiting(messages)
228
228
  if (lastPlanningAwaiting) {
229
229
  const value = lastPlanningAwaiting.getValue()
230
- value.content = finalValue.answer || value.content
230
+ value.content = finalValue.message || value.content
231
231
  lastPlanningAwaiting.setValue(value)
232
232
  }
233
233
  // if the streaming feature is not enabled, we only add the chat entry once the streaming has finished
@@ -247,7 +247,7 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState,
247
247
  })
248
248
  }
249
249
  }
250
- if (finalValue?.answer) {
250
+ if (finalValue?.message) {
251
251
  botEntry.setValue({
252
252
  ...createEntryValueFromChatResponse(finalValue, botEntry.getValue().knowledgeSources, chat.get('agent'), true),
253
253
  hasPlanning: botEntry.getValue().hasPlanning,
@@ -94,7 +94,7 @@ export const FileDescription = ({ fileName, icon, status, onRemove, onRetry }: F
94
94
  icon="TimesMini"
95
95
  onClick={onRemove}
96
96
  title={t.remove}
97
- arial-label={`${t.remove} ${name}`}
97
+ aria-label={name}
98
98
  style={{ alignSelf: 'start' }}
99
99
  size="xs"
100
100
  />}
@@ -4,7 +4,9 @@
4
4
 
5
5
  import { Children, Fragment } from 'react'
6
6
  import ReactMarkdown from 'react-markdown'
7
+ import rehypeKatex from 'rehype-katex'
7
8
  import remarkGfm from 'remark-gfm'
9
+ import remarkMath from 'remark-math'
8
10
  import { WithChildren } from '../types'
9
11
  import { Code, Props as CodeProps } from './Code'
10
12
 
@@ -29,26 +31,43 @@ export const Markdown = (
29
31
  onCopyCode,
30
32
  children,
31
33
  }: Props,
32
- ) => (
33
- <>
34
- <ReactMarkdown
35
- className="markdown apply-citric"
36
- remarkPlugins={[[remarkGfm]]}
37
- components={{
38
- a: props => <a target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'underline' }} {...props} />,
39
- code: props =>
40
- <Code
41
- {...props}
42
- onInsertCode={onInsertCode}
43
- onNewFile={onNewFile}
44
- onCopyCode={onCopyCode}
45
- showActionBar
46
- />,
47
- pre: ({ children }) => <>{children}</>,
48
- p: ({ children }) => <p>{Children.map(children, renderP)}</p>,
49
- }}
50
- >
51
- {children}
52
- </ReactMarkdown>
53
- </>
54
- )
34
+ ) => {
35
+
36
+ // fix parsing LaTex: https://github.com/remarkjs/react-markdown/issues/785
37
+ const content = typeof children === 'string'
38
+ ? children
39
+ .replace(/\\\\\[/g, '$$$$') // Replace '\\[' with '$$'
40
+ .replace(/\\\\\]/g, '$$$$') // Replace '\\]' with '$$'
41
+ .replace(/\\\\\(/g, '$$$$') // Replace '\\(' with '$$'
42
+ .replace(/\\\\\)/g, '$$$$') // Replace '\\)' with '$$'
43
+ .replace(/\\\[/g, '$$$$') // Replace '\[' with '$$'
44
+ .replace(/\\\]/g, '$$$$') // Replace '\]' with '$$'
45
+ .replace(/\\\(/g, '$$$$') // Replace '\(' with '$$'
46
+ .replace(/\\\)/g, '$$$$') // Replace '\)' with '$$';
47
+ : children
48
+
49
+ return (
50
+ <>
51
+ <ReactMarkdown
52
+ className="markdown apply-citric"
53
+ remarkPlugins={[[remarkMath, { singleDollarTextMath: true }], remarkGfm]}
54
+ rehypePlugins={[rehypeKatex]}
55
+ components={{
56
+ a: props => <a target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'underline' }} {...props} />,
57
+ code: props =>
58
+ <Code
59
+ {...props}
60
+ onInsertCode={onInsertCode}
61
+ onNewFile={onNewFile}
62
+ onCopyCode={onCopyCode}
63
+ showActionBar
64
+ />,
65
+ pre: ({ children }) => <>{children}</>,
66
+ p: ({ children }) => <p>{Children.map(children, renderP)}</p>,
67
+ }}
68
+ >
69
+ {content}
70
+ </ReactMarkdown>
71
+ </>
72
+ )
73
+ }
@@ -42,7 +42,7 @@ interface ListProps<T> {
42
42
  filter?: string,
43
43
  visibility?: SectionVisibility,
44
44
  selectorConfig: SelectorConfig<T>,
45
- onSelect: (item: T) => void,
45
+ onSelect: (item: T, selectedVersion?: number) => void,
46
46
  favorite?: Favorite<T>,
47
47
  }
48
48
 
@@ -76,7 +76,7 @@ interface SelectorConfig<T> {
76
76
  isEnabled: boolean,
77
77
  sections: SectionVisibility[],
78
78
  renderComponentItem: FC<T>,
79
- onSelect: (item: T) => void,
79
+ onSelect: (item: T, selectedVersion?: number) => void,
80
80
  /**
81
81
  * A react hook or a simple function that returns the selectable items.
82
82
  *
@@ -104,7 +104,7 @@ const ListItem = <T extends Item>({ item, selectorConfig, onSelect, favorite }:
104
104
  <li>
105
105
  <button
106
106
  className="selector"
107
- onClick={() => onSelect(item)}
107
+ onClick={() => onSelect(item, selectedVersion)}
108
108
  onKeyDown={(e) => e.key === 'Enter' && e.preventDefault()}
109
109
  onFocus={(e) => e.target.closest('li')?.classList.add('focus')}
110
110
  onBlur={(e) => e.target.closest('li')?.classList.remove('focus')}
@@ -157,14 +157,14 @@ const List = <T extends Item>({ selectorConfig, filter, visibility, onSelect, fa
157
157
  )
158
158
  }
159
159
 
160
- const SelectorContent = ({ filter, onClose, selectorConfig, favorite }: ContentProps<any>) => {
160
+ const SelectorContent = <T, > ({ filter, onClose, selectorConfig, favorite }: ContentProps<T>) => {
161
161
  const t = useTranslate(dictionary)
162
162
  const ref = useRef<HTMLDivElement>(null)
163
163
  const [visibility, setVisibility] = useState<SectionVisibility | undefined>()
164
164
  const isGroupResourcesByScope = useWidgetState('features')?.groupResourcesByScope
165
165
  const { resourceName, icon, onSelect, sections } = selectorConfig
166
- const onSelectItem = useCallback((slug: string) => {
167
- onSelect(slug)
166
+ const onSelectItem = useCallback((item: any, selectedVersion?: number) => {
167
+ onSelect(item, selectedVersion)
168
168
  onClose()
169
169
  }, [])
170
170