@stack-spot/ai-chat-widget 2.4.0 → 2.4.2

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 (45) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/app-metadata.json +5 -5
  3. package/dist/chat-interceptors/send-message.d.ts +16 -1
  4. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  5. package/dist/chat-interceptors/send-message.js +148 -138
  6. package/dist/chat-interceptors/send-message.js.map +1 -1
  7. package/dist/features.d.ts +4 -0
  8. package/dist/features.d.ts.map +1 -1
  9. package/dist/features.js +1 -0
  10. package/dist/features.js.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/state/ChatEntry.d.ts +13 -1
  16. package/dist/state/ChatEntry.d.ts.map +1 -1
  17. package/dist/state/ChatEntry.js.map +1 -1
  18. package/dist/views/Agents/styled.d.ts.map +1 -1
  19. package/dist/views/Agents/styled.js +1 -2
  20. package/dist/views/Agents/styled.js.map +1 -1
  21. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  22. package/dist/views/Chat/ChatMessage.js +7 -4
  23. package/dist/views/Chat/ChatMessage.js.map +1 -1
  24. package/dist/views/Chat/StepsList.js +5 -5
  25. package/dist/views/Chat/StepsList.js.map +1 -1
  26. package/dist/views/Chat/styled.d.ts.map +1 -1
  27. package/dist/views/Chat/styled.js +4 -8
  28. package/dist/views/Chat/styled.js.map +1 -1
  29. package/dist/views/MessageInput/ButtonBar.js +1 -1
  30. package/dist/views/MessageInput/ButtonBar.js.map +1 -1
  31. package/dist/views/Resources.js +12 -5
  32. package/dist/views/Resources.js.map +1 -1
  33. package/package.json +4 -4
  34. package/src/app-metadata.json +5 -5
  35. package/src/chat-interceptors/send-message.ts +163 -149
  36. package/src/features.ts +8 -3
  37. package/src/index.ts +2 -0
  38. package/src/state/ChatEntry.ts +19 -7
  39. package/src/views/Agents/styled.ts +1 -2
  40. package/src/views/Chat/ChatMessage.tsx +50 -60
  41. package/src/views/Chat/StepsList.tsx +8 -8
  42. package/src/views/Chat/styled.ts +4 -8
  43. package/src/views/MessageInput/ButtonBar.tsx +1 -1
  44. package/src/views/MessageInput/index.tsx +2 -2
  45. package/src/views/Resources.tsx +18 -9
@@ -69,12 +69,12 @@ interface Props extends CustomMessage {
69
69
  isLast: boolean,
70
70
  }
71
71
 
72
- interface RenderInputsEntryProps {
73
- isLast: boolean,
74
- entry: TextChatEntry,
75
- value: string[],
72
+ interface RenderInputsEntryProps {
73
+ isLast: boolean,
74
+ entry: TextChatEntry,
75
+ value: string[],
76
76
  setValue: Dispatch<React.SetStateAction<string[]>>,
77
- labels: string[],
77
+ labels: string[],
78
78
  setLabels: Dispatch<React.SetStateAction<string[]>>,
79
79
  }
80
80
 
@@ -113,7 +113,7 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
113
113
  <Input name={entry.name} onChange={v => setValue([v])} required style={{ height: '30px', width: '33%' }} />}
114
114
  </Row>
115
115
  )}
116
- />
116
+ />
117
117
  }
118
118
 
119
119
  if (entry.type === 'button-list') {
@@ -145,7 +145,7 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
145
145
  renderLabel={o => (
146
146
  <Row gap={3}>
147
147
  <Text>{o.label}</Text>
148
- {o.hasInput && o.value && labels.findIndex((label) => label === o.value)!== -1 &&
148
+ {o.hasInput && o.value && labels.findIndex((label) => label === o.value) !== -1 &&
149
149
  <div style={{ width: '33%' }}>
150
150
  <Input
151
151
  name={entry.name}
@@ -158,7 +158,7 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
158
158
  const newValue = [...value]
159
159
  newValue[customIndex] = v
160
160
  setValue(newValue)
161
- }
161
+ }
162
162
  }}
163
163
  required={true}
164
164
  style={{ height: '30px' }}
@@ -219,7 +219,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
219
219
  const [copied, setCopied] = useState(false)
220
220
  const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
221
221
  const isPlanning = useCurrentChatState('isPlaning') ?? false
222
-
222
+
223
223
  // when we have a steps but we are not showing any content of the step
224
224
  // (because it is a tool and the user has already answered the question)
225
225
  // we do not want to show an avatar with empty content, so we hide the entire message
@@ -227,12 +227,12 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
227
227
  const messages = useChatMessages(chat.id)
228
228
  const userHasAlreadyAnswered = useMemo(() => {
229
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()
230
+ if (messages.length - 1 === messageIndex) return false
231
+ const nextMessage = messages[messageIndex + 1].getValue()
232
232
  return nextMessage.agentType === 'user'
233
233
  }, [messages, messages.length])
234
234
  const isMessageHidden = toolsStep && userHasAlreadyAnswered
235
-
235
+
236
236
  useChatScrollToBottomEffect(ref, [entry])
237
237
  useMidnightUpdateView()
238
238
 
@@ -277,7 +277,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
277
277
  }
278
278
  }
279
279
 
280
- const renderActions = useCallback(()=> <> {entry.actions?.length && (
280
+ const renderActions = useCallback(() => <> {entry.actions?.length && (
281
281
  <div className="actions">
282
282
  {entry.actions.map(
283
283
  (a, index) => (<>
@@ -357,7 +357,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
357
357
  widget.set('panel', 'resources')
358
358
  }
359
359
 
360
- return (entry.content || entry.error || !!entry.steps?.length ||
360
+ return (entry.content || entry.error || !!entry.steps?.length ||
361
361
  entry.upload?.length) && (!isMessageHidden || !toolsStep || isPlanning) && (
362
362
  <li key={entry.messageId} className={entry.agentType} ref={ref}>
363
363
  <div className="chat-message-container"
@@ -371,33 +371,19 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
371
371
  {!!entry.badges?.length && <div className="badges">
372
372
  {entry.badges.map((b, index) => <Badge key={index} colorPalette={b.color ?? 'cyan'} appearance="square">{b.label}</Badge>)}
373
373
  </div>}
374
-
375
- {!!entry.steps?.length && <StepsList steps={entry.steps} chatId={chat.id} messageId={message.id}
374
+
375
+ {!!entry.steps?.length && <StepsList steps={entry.steps} chatId={chat.id} messageId={message.id}
376
376
  userHasAlreadyAnswered={userHasAlreadyAnswered} />}
377
377
 
378
378
  {renderContent()}
379
-
379
+
380
380
  </div>
381
381
  )}
382
- {isPlanning && entry.agentType === 'bot' && isLast && <StepsPlaceholder /> }
383
-
382
+ {isPlanning && entry.agentType === 'bot' && isLast && <StepsPlaceholder />}
383
+
384
384
  {entry.error && <Alert type="error">{entry.error}</Alert>}
385
385
  </div>
386
386
  {afterMessage && createElement(afterMessage, { message })}
387
- {/* {!!entry.tools?.length && <StackedBadge
388
- aria-label={t.openToolsPanel}
389
- title={t.openToolsPanel}
390
- tabIndex={0}
391
- role="button"
392
- className="tools-badge"
393
- label={t.tools}
394
- images={entry.tools.slice(0, 3).map((id) => {
395
- const tool = toolById(id, toolKits)
396
- return { key: id, name: tool?.name || id, icon: <Icon icon="Cog" />, url: tool?.image }
397
- })}
398
- onClick={openToolsPanel}
399
- style={{ marginTop: '12px', width: 'fit-content' }}
400
- />} */}
401
387
 
402
388
  {!!entry.knowledgeSources?.length && <div className="ks-box">
403
389
  <Text appearance="microtext1" color="light.700">Knowledge Sources:</Text>
@@ -408,36 +394,40 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage, cust
408
394
  ))}</ul>
409
395
  </div>}
410
396
 
411
- {(!!agentsTools?.length || !!entry.tools?.length) && <div className="tools-box">
412
- <Button appearance="none" onClick={openResourcesPanel} aria-label={t.openResourcesPanel}>
413
- {agentsTools?.map((agent) => (
414
- <ImageBox key={agent.id} className="agent-info-avatar-resource">
415
- <ImageWithFallback
416
- src={agent.avatar ?? undefined}
417
- fallback={<Icon icon="Agent" />}
418
- alt={agent.name}
419
- aria-label={agent.name}
420
- title={agent.name}
421
- />
422
- </ImageBox>
423
- ))}
424
- {entry.tools?.map((id) => {
425
- const tool = toolById(id, toolKits)
426
- return (
427
- <ImageBox key={id} className="agent-info-avatar-resource">
397
+ {(!!agentsTools?.length || !!entry.tools?.length) &&
398
+ <div className="tools-box">
399
+ <Button appearance="none" onClick={openResourcesPanel} aria-label={t.openResourcesPanel}>
400
+ {agentsTools?.map((agent) => (
401
+ <ImageBox key={agent.id} className="agent-info-avatar-resource">
428
402
  <ImageWithFallback
429
- src={tool?.image}
430
- fallback={<Icon icon="Cog" />}
431
- alt={tool?.name}
432
- aria-label={tool?.name}
433
- title={tool?.name}
403
+ src={agent.avatar ?? undefined}
404
+ fallback={<Icon icon="Agent" />}
405
+ alt={agent.name}
406
+ aria-label={agent.name}
407
+ title={agent.name}
434
408
  />
435
409
  </ImageBox>
436
- )})}
437
- </Button>
438
- <ViewToolsDetails chatId={chat.id} />
439
- </div>}
440
-
410
+ ))}
411
+ {entry?.tools?.map((id) => {
412
+ if (agentsTools?.some((agent) => agent.id === id)) return null
413
+ const tool = toolById(id, toolKits)
414
+ return (
415
+ <ImageBox key={id} className="agent-info-avatar-resource">
416
+ <ImageWithFallback
417
+ src={tool?.image}
418
+ fallback={<Icon icon="Cog" />}
419
+ alt={tool?.name}
420
+ aria-label={tool?.name}
421
+ title={tool?.name}
422
+ />
423
+ </ImageBox>
424
+ )
425
+ })}
426
+ </Button>
427
+ <ViewToolsDetails chatId={chat.id} />
428
+ </div>
429
+ }
430
+
441
431
  {shouldShowFooter && <div className="message-footer">
442
432
  {entry.agentType === 'bot' && !entry.error && <div className="message-actions">
443
433
  {entry.type === 'md' && (
@@ -50,14 +50,14 @@ const StepAccordionHeader = ({ step, index, expand }: Pick<StepProps, 'step' | '
50
50
  return <Row gap="8px">
51
51
  {expand}
52
52
  {step.status === 'target' ? <Text className="step-title" appearance="body2" color="light.700">{t.planGoal}:</Text> :
53
- <Badge colorScheme="inverse" appearance="square">
53
+ <Badge colorScheme="inverse" appearance="square" style={{ whiteSpace: 'nowrap' }}>
54
54
  {t.step} {index}
55
55
  </Badge>}
56
56
  <Text className="step-title" appearance="body2">
57
57
  {step.input}
58
58
  </Text>
59
59
  {step.status === 'awaiting_approval' &&
60
- <Badge appearance="square" style={{ backgroundColor: theme.color.gray[800], color: theme.color.gray[50] }}>
60
+ <Badge appearance="square" style={{ backgroundColor: theme.color.gray[800], color: theme.color.gray[50], whiteSpace: 'nowrap' }}>
61
61
  <Icon icon="Security" />
62
62
  {t.pendingReview}
63
63
  </Badge>}
@@ -199,7 +199,7 @@ export const ToolStepsList = ({ toolStep, messageId, chatId }: { toolStep: ToolC
199
199
  return <>
200
200
  {toolStep && tool ? <AnimatedHeight>
201
201
  <div className="steps">
202
- <Badge colorPalette="yellow" appearance="square">
202
+ <Badge colorPalette="yellow" appearance="square" style={{ whiteSpace: 'nowrap' }}>
203
203
  <Icon icon="StopWatch" />
204
204
  <Text>{tool.name} {t.keepWorking}</Text>
205
205
  </Badge>
@@ -271,8 +271,8 @@ export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }:
271
271
  const isLastStepDone = actualSteps[actualSteps.length - 1]?.status !== 'running' &&
272
272
  actualSteps[actualSteps.length - 1]?.status !== 'pending'
273
273
  const totalTools = useMemo(() => actualSteps?.reduce((sum, step) => {
274
- const firstAttempt = step.attempts && step.attempts[0]
275
- const toolsCount = firstAttempt.tools?.length ?? 0
274
+ const firstAttempt = step?.attempts && step.attempts[0]
275
+ const toolsCount = firstAttempt?.tools?.length ?? 0
276
276
  return sum + toolsCount
277
277
  }, 0), [steps])
278
278
 
@@ -302,7 +302,7 @@ export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }:
302
302
  <Column gap="8px" mt="8px">
303
303
  <Divider colorScheme="light" />
304
304
  <Text color="light.700">{planning?.[0]?.user_question}</Text>
305
- {!userHasAlreadyAnswered && planning?.[0]?.status === 'awaiting_approval' && <AwaitingApproval chatId={chatId} />}
305
+ {!userHasAlreadyAnswered && planning?.[0]?.status === 'awaiting_approval' && <AwaitingApproval chatId={chatId} />}
306
306
  </Column>
307
307
 
308
308
  {userHasAlreadyAnswered && planning?.[0]?.status === 'success' && <ViewToolsDetails chatId={chatId} />}
@@ -310,7 +310,7 @@ export const StepsList = ({ steps, messageId, chatId, userHasAlreadyAnswered }:
310
310
  </div>
311
311
  </AnimatedHeight> : null}
312
312
 
313
- {toolsStep && toolsStep.status === 'awaiting_approval' && !userHasAlreadyAnswered &&
313
+ {toolsStep && toolsStep.status === 'awaiting_approval' && !userHasAlreadyAnswered &&
314
314
  <ToolStepsList toolStep={toolsStep} messageId={messageId} chatId={chatId} />}
315
315
  </>
316
316
  )
@@ -331,7 +331,7 @@ export const ViewToolsDetails = ({ chatId }: { chatId: string }) => {
331
331
 
332
332
  function openToolsPanel() {
333
333
  const messageId = getPlanningMessageId()
334
-
334
+
335
335
  if (messageId) {
336
336
  widget.set('currentMessageInPanel', { chatId, messageId })
337
337
  widget.set('panel', 'steps')
@@ -220,7 +220,7 @@ export const ChatList: IStyledComponentBase<
220
220
 
221
221
  .tools-box {
222
222
 
223
- > ul {
223
+ > button {
224
224
  display: flex;
225
225
  flex-direction: row;
226
226
  flex-wrap: wrap;
@@ -229,15 +229,11 @@ export const ChatList: IStyledComponentBase<
229
229
  margin-top: 8px;
230
230
  padding: 0;
231
231
  list-style: none;
232
- gap: 6px;
232
+ gap: 0px;
233
233
 
234
234
  &:hover{
235
- cursor: pointer;
236
-
237
- .agent-info-avatar-resource {
238
- transition: margin-left 0.3s ease-in;
239
- margin-left: 0px;
240
- }
235
+ transition: gap 0.3s ease-in;
236
+ gap: 6px;
241
237
  }
242
238
  }
243
239
  }
@@ -30,7 +30,7 @@ export const ButtonBar = ({ onSend, isLoading }: SelectionBarProps) => {
30
30
  <IconButton icon="Code" appearance="square" aria-label={t.code} title={t.code} onClick={() => widget.set('panel', 'editor')} />
31
31
  )}
32
32
  </Row>
33
- <ModelSwitcher />
33
+ {features.showLLMSelect && <ModelSwitcher />}
34
34
  {isLoading ? (
35
35
  <IconButton
36
36
  icon="Stop"
@@ -24,7 +24,7 @@ import { UploadDragNDrop, useUploadDragDrop } from './UploadDragNDrop'
24
24
  * going to be used for the question and the buttons to send, cancel, set the workspace, among others. This also includes the Quick
25
25
  * Commands panel for auto completing.
26
26
  */
27
- export const MessageInput = ({ chatWindowRef, customInputMessage }:
27
+ export const MessageInput = ({ chatWindowRef, customInputMessage }:
28
28
  { chatWindowRef?: React.RefObject<HTMLElement>, customInputMessage?: string }) => {
29
29
  const t = useMessageInputDictionary()
30
30
  const [focused, setFocused] = useState(false)
@@ -96,7 +96,7 @@ export const MessageInput = ({ chatWindowRef, customInputMessage }:
96
96
  if (!checkSendRequirements()) return
97
97
 
98
98
  // Compose prompt with code block if needed
99
- const prompt = code && !quickCommandRegex.test(message ?? '') ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message
99
+ const prompt = code && !quickCommandRegex.test(message ?? '') ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message
100
100
 
101
101
  // Validate prompt length
102
102
  if (!checkPromptMaxLength(prompt || '')) return
@@ -1,4 +1,4 @@
1
- import { Accordion, Icon, ImageBox, ImageWithFallback, Row, Text } from '@stack-spot/citric-react'
1
+ import { Accordion, Column, Icon, ImageBox, ImageWithFallback, Row, Text } from '@stack-spot/citric-react'
2
2
  import { agentToolsClient } from '@stack-spot/portal-network'
3
3
  import { theme } from '@stack-spot/portal-theme'
4
4
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
@@ -27,9 +27,16 @@ export const Resources = () => {
27
27
  }
28
28
 
29
29
  const StyledAccordion = styled(Accordion)`
30
+ padding: 8px;
31
+ margin: 8px;
30
32
  &[data-citric="accordion"] {
31
33
  background-color: ${theme.color.light[400]};
32
34
  }
35
+ label {
36
+ display: flex;
37
+ width: 100%;
38
+ justify-content: space-between;
39
+ }
33
40
  `
34
41
 
35
42
  const ResourcesPanel = () => {
@@ -50,11 +57,11 @@ const ResourcesPanel = () => {
50
57
  const hasAgentTool = useMemo(() => message?.tools?.some(id => agentsTools?.find((agent) => agent.id === id)), [messageId, toolKits])
51
58
 
52
59
  const header = (image?: string, label?: string) => (
53
- <Row gap="10px">
54
- <ImageBox>
60
+ <Row>
61
+ <ImageBox >
55
62
  <ImageWithFallback src={image} fallback={<Icon icon="Agent" />} aria-label={label} title={label} />
56
63
  </ImageBox>
57
- <Text>{label}</Text>
64
+ <Text style={{ marginLeft: '8px' }}>{label}</Text>
58
65
  </Row>
59
66
  )
60
67
 
@@ -64,23 +71,25 @@ const ResourcesPanel = () => {
64
71
  {[...(tools || []), ...(customTools || [])].map(
65
72
  (tool) =>
66
73
  tool && (
67
- <StyledAccordion key={tool.id} header={header(tool?.image, tool?.name)} appearance="card">
74
+ <StyledAccordion key={tool.id} header={header(tool?.image, tool?.name)} appearance="card" maxHeight={120}>
68
75
  {tool?.description}
69
76
  </StyledAccordion>
70
77
  ))}
71
78
  </>
72
79
  {
73
80
  hasAgentTool &&
74
- <>
81
+ <Column>
75
82
  {message?.tools?.map((id) => {
76
83
  const agentTool = agentsTools?.find((agent) => agent.id === id)
77
84
  return (
78
- <StyledAccordion key={id} header={header(agentTool?.avatar || undefined, agentTool?.name)}>
79
- <AgentDescription agentId={id} />
85
+ <StyledAccordion key={agentTool?.id} header={header(agentTool?.avatar || undefined, agentTool?.name)}>
86
+ <Row>
87
+ <AgentDescription agentId={id} />
88
+ </Row>
80
89
  </StyledAccordion>
81
90
  )},
82
91
  )}
83
- </>
92
+ </Column>
84
93
  }
85
94
  </>
86
95
  )