@stack-spot/ai-chat-widget 1.24.4 → 1.25.1-beta.0

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 (164) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/app-metadata.json +3 -3
  3. package/dist/chat-interceptors/send-message.js +3 -3
  4. package/dist/chat-interceptors/send-message.js.map +1 -1
  5. package/dist/components/AgentCard/dictionary.d.ts +4 -2
  6. package/dist/components/AgentCard/dictionary.d.ts.map +1 -1
  7. package/dist/components/AgentCard/dictionary.js +4 -2
  8. package/dist/components/AgentCard/dictionary.js.map +1 -1
  9. package/dist/components/FileDescription.d.ts +10 -0
  10. package/dist/components/FileDescription.d.ts.map +1 -0
  11. package/dist/components/FileDescription.js +85 -0
  12. package/dist/components/FileDescription.js.map +1 -0
  13. package/dist/components/Selector/index.d.ts +2 -2
  14. package/dist/components/Selector/index.d.ts.map +1 -1
  15. package/dist/components/Selector/index.js +2 -2
  16. package/dist/components/Selector/index.js.map +1 -1
  17. package/dist/state/ChatEntry.d.ts +9 -0
  18. package/dist/state/ChatEntry.d.ts.map +1 -1
  19. package/dist/state/ChatEntry.js.map +1 -1
  20. package/dist/state/ChatState.d.ts +5 -0
  21. package/dist/state/ChatState.d.ts.map +1 -1
  22. package/dist/state/ChatState.js +6 -0
  23. package/dist/state/ChatState.js.map +1 -1
  24. package/dist/state/constants.d.ts +5 -0
  25. package/dist/state/constants.d.ts.map +1 -0
  26. package/dist/state/constants.js +9 -0
  27. package/dist/state/constants.js.map +1 -0
  28. package/dist/state/types.d.ts +5 -1
  29. package/dist/state/types.d.ts.map +1 -1
  30. package/dist/utils/chat.d.ts +2 -1
  31. package/dist/utils/chat.d.ts.map +1 -1
  32. package/dist/utils/chat.js +2 -1
  33. package/dist/utils/chat.js.map +1 -1
  34. package/dist/utils/tools.d.ts +2 -2
  35. package/dist/utils/tools.d.ts.map +1 -1
  36. package/dist/utils/tools.js +3 -6
  37. package/dist/utils/tools.js.map +1 -1
  38. package/dist/utils/upload/FileUpload.d.ts +21 -0
  39. package/dist/utils/upload/FileUpload.d.ts.map +1 -0
  40. package/dist/utils/upload/FileUpload.js +55 -0
  41. package/dist/utils/upload/FileUpload.js.map +1 -0
  42. package/dist/utils/upload/UploadManager.d.ts +40 -0
  43. package/dist/utils/upload/UploadManager.d.ts.map +1 -0
  44. package/dist/utils/upload/UploadManager.js +131 -0
  45. package/dist/utils/upload/UploadManager.js.map +1 -0
  46. package/dist/utils/upload/context.d.ts +15 -0
  47. package/dist/utils/upload/context.d.ts.map +1 -0
  48. package/dist/utils/upload/context.js +37 -0
  49. package/dist/utils/upload/context.js.map +1 -0
  50. package/dist/utils/upload/errors.d.ts +17 -0
  51. package/dist/utils/upload/errors.d.ts.map +1 -0
  52. package/dist/utils/upload/errors.js +27 -0
  53. package/dist/utils/upload/errors.js.map +1 -0
  54. package/dist/utils/upload/types.d.ts +7 -0
  55. package/dist/utils/upload/types.d.ts.map +1 -0
  56. package/dist/utils/upload/types.js +2 -0
  57. package/dist/utils/upload/types.js.map +1 -0
  58. package/dist/utils/upload/utils.d.ts +4 -0
  59. package/dist/utils/upload/utils.d.ts.map +1 -0
  60. package/dist/utils/upload/utils.js +10 -0
  61. package/dist/utils/upload/utils.js.map +1 -0
  62. package/dist/views/Agents/AgentDescription.d.ts +2 -9
  63. package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
  64. package/dist/views/Agents/AgentDescription.js +11 -9
  65. package/dist/views/Agents/AgentDescription.js.map +1 -1
  66. package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
  67. package/dist/views/Agents/AgentsPanel.js +11 -11
  68. package/dist/views/Agents/AgentsPanel.js.map +1 -1
  69. package/dist/views/Agents/AgentsTab.d.ts +2 -2
  70. package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
  71. package/dist/views/Agents/AgentsTab.js +4 -4
  72. package/dist/views/Agents/AgentsTab.js.map +1 -1
  73. package/dist/views/Agents/useAgentFavorites.d.ts +1 -1
  74. package/dist/views/Agents/useAgentFavorites.js +4 -4
  75. package/dist/views/Agents/useAgentFavorites.js.map +1 -1
  76. package/dist/views/Chat/AgentInfo.d.ts +2 -1
  77. package/dist/views/Chat/AgentInfo.d.ts.map +1 -1
  78. package/dist/views/Chat/AgentInfo.js +2 -2
  79. package/dist/views/Chat/AgentInfo.js.map +1 -1
  80. package/dist/views/Chat/ChatMessage.d.ts +1 -1
  81. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  82. package/dist/views/Chat/ChatMessage.js +27 -8
  83. package/dist/views/Chat/ChatMessage.js.map +1 -1
  84. package/dist/views/Chat/styled.d.ts.map +1 -1
  85. package/dist/views/Chat/styled.js +15 -1
  86. package/dist/views/Chat/styled.js.map +1 -1
  87. package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
  88. package/dist/views/ChatHistory/HistoryItem.js +8 -5
  89. package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
  90. package/dist/views/ChatHistory/utils.d.ts +0 -6
  91. package/dist/views/ChatHistory/utils.d.ts.map +1 -1
  92. package/dist/views/ChatHistory/utils.js +1 -16
  93. package/dist/views/ChatHistory/utils.js.map +1 -1
  94. package/dist/views/Home/CustomAgent.js +3 -3
  95. package/dist/views/Home/CustomAgent.js.map +1 -1
  96. package/dist/views/MessageInput/AgentSelector.js +4 -4
  97. package/dist/views/MessageInput/AgentSelector.js.map +1 -1
  98. package/dist/views/MessageInput/ButtonAgent.js +2 -2
  99. package/dist/views/MessageInput/ButtonAgent.js.map +1 -1
  100. package/dist/views/MessageInput/{InfoBar.d.ts → ContextBar.d.ts} +2 -2
  101. package/dist/views/MessageInput/ContextBar.d.ts.map +1 -0
  102. package/dist/views/MessageInput/{InfoBar.js → ContextBar.js} +5 -5
  103. package/dist/views/MessageInput/ContextBar.js.map +1 -0
  104. package/dist/views/MessageInput/SelectContent.d.ts.map +1 -1
  105. package/dist/views/MessageInput/SelectContent.js +14 -17
  106. package/dist/views/MessageInput/SelectContent.js.map +1 -1
  107. package/dist/views/MessageInput/UploadBar.d.ts +2 -0
  108. package/dist/views/MessageInput/UploadBar.d.ts.map +1 -0
  109. package/dist/views/MessageInput/UploadBar.js +47 -0
  110. package/dist/views/MessageInput/UploadBar.js.map +1 -0
  111. package/dist/views/MessageInput/dictionary.d.ts +1 -1
  112. package/dist/views/MessageInput/dictionary.d.ts.map +1 -1
  113. package/dist/views/MessageInput/dictionary.js +18 -4
  114. package/dist/views/MessageInput/dictionary.js.map +1 -1
  115. package/dist/views/MessageInput/index.d.ts.map +1 -1
  116. package/dist/views/MessageInput/index.js +46 -5
  117. package/dist/views/MessageInput/index.js.map +1 -1
  118. package/dist/views/MessageInput/styled.d.ts.map +1 -1
  119. package/dist/views/MessageInput/styled.js +56 -27
  120. package/dist/views/MessageInput/styled.js.map +1 -1
  121. package/dist/views/Steps/dictionary.d.ts +1 -1
  122. package/dist/views/Tools.js +3 -3
  123. package/dist/views/Tools.js.map +1 -1
  124. package/dist/views/Workspaces/WorkspacesTab.js +1 -1
  125. package/package.json +2 -2
  126. package/src/app-metadata.json +3 -3
  127. package/src/chat-interceptors/send-message.ts +3 -3
  128. package/src/components/AgentCard/dictionary.ts +4 -2
  129. package/src/components/FileDescription.tsx +114 -0
  130. package/src/components/Selector/index.tsx +4 -5
  131. package/src/state/ChatEntry.ts +10 -0
  132. package/src/state/ChatState.ts +6 -0
  133. package/src/state/constants.ts +12 -0
  134. package/src/state/types.ts +6 -1
  135. package/src/utils/chat.ts +3 -1
  136. package/src/utils/tools.ts +5 -7
  137. package/src/utils/upload/FileUpload.ts +64 -0
  138. package/src/utils/upload/UploadManager.ts +147 -0
  139. package/src/utils/upload/context.tsx +44 -0
  140. package/src/utils/upload/errors.ts +34 -0
  141. package/src/utils/upload/types.ts +7 -0
  142. package/src/utils/upload/utils.ts +12 -0
  143. package/src/views/Agents/AgentDescription.tsx +18 -25
  144. package/src/views/Agents/AgentsPanel.tsx +11 -12
  145. package/src/views/Agents/AgentsTab.tsx +8 -16
  146. package/src/views/Agents/useAgentFavorites.ts +4 -4
  147. package/src/views/Chat/AgentInfo.tsx +3 -2
  148. package/src/views/Chat/ChatMessage.tsx +51 -16
  149. package/src/views/Chat/styled.ts +15 -1
  150. package/src/views/ChatHistory/HistoryItem.tsx +10 -5
  151. package/src/views/ChatHistory/utils.ts +1 -18
  152. package/src/views/Home/CustomAgent.tsx +4 -4
  153. package/src/views/MessageInput/AgentSelector.tsx +4 -4
  154. package/src/views/MessageInput/ButtonAgent.tsx +2 -2
  155. package/src/views/MessageInput/{InfoBar.tsx → ContextBar.tsx} +9 -9
  156. package/src/views/MessageInput/SelectContent.tsx +17 -21
  157. package/src/views/MessageInput/UploadBar.tsx +69 -0
  158. package/src/views/MessageInput/dictionary.ts +18 -4
  159. package/src/views/MessageInput/index.tsx +77 -32
  160. package/src/views/MessageInput/styled.ts +56 -27
  161. package/src/views/Tools.tsx +4 -3
  162. package/src/views/Workspaces/WorkspacesTab.tsx +1 -1
  163. package/dist/views/MessageInput/InfoBar.d.ts.map +0 -1
  164. package/dist/views/MessageInput/InfoBar.js.map +0 -1
@@ -1,29 +1,21 @@
1
1
  import { Text } from '@citric/core'
2
2
  import { Badge, Skeleton } from '@citric/ui'
3
- import { agentClient, AgentVisibilityLevel } from '@stack-spot/portal-network'
3
+ import { agentToolsClient } from '@stack-spot/portal-network'
4
4
  import { useMemo } from 'react'
5
5
  import { ToolBadge } from '../../components/ToolBadge'
6
+ import { toolById } from '../../utils/tools'
6
7
  import { useAgentsDictionary } from './dictionary'
7
8
  import { AgentDescriptionBox } from './styled'
8
9
 
9
- interface Props {
10
- agentId?: string,
11
- llm?: string,
12
- description?: string,
13
- numberOfKnowledgeSources: number,
14
- visibility: AgentVisibilityLevel,
15
- }
16
-
17
- export const AgentDescription = ({ agentId, llm, description, numberOfKnowledgeSources, visibility }: Props) => {
10
+ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
18
11
  const t = useAgentsDictionary()
19
- const [agent, , , { isLoading }] = agentClient.agentById.useStatefulQuery(
20
- { agentId: agentId!, builtIn: visibility === 'BUILT-IN' },
21
- { enabled: !!agentId },
22
- )
12
+ const [agent, , , { isLoading }] = agentToolsClient.agent.useStatefulQuery({ agentId: agentId! }, { enabled: !!agentId })
13
+ const [toolKits, , , { isLoading: isLoadingToolKit }] = agentToolsClient.tools.useStatefulQuery()
14
+ const numberOfKnowledgeSources = agent?.knowledge_source_config?.knowledge_sources.length ?? 0
23
15
 
24
16
  const knowledgeSources = useMemo(
25
- () => agent?.knowledge_sources_config?.knowledge_sources_details?.map((ks, index) => (
26
- <li key={index}><Badge palette="teal" appearance="square">{ks.name}</Badge></li>
17
+ () => agent?.knowledge_source_config?.knowledge_sources?.map((ks, index) => (
18
+ <li key={index}><Badge palette="teal" appearance="square">{ks}</Badge></li>
27
19
  )),
28
20
  [agent],
29
21
  )
@@ -36,31 +28,32 @@ export const AgentDescription = ({ agentId, llm, description, numberOfKnowledgeS
36
28
  }, [numberOfKnowledgeSources])
37
29
  const tools = useMemo(() => {
38
30
  const result: React.ReactElement[] = []
39
- for (const kit of agent?.toolkits?.builtins ?? []) {
40
- for (const tool of kit.tools ?? []) {
41
- result.push(<li key={tool.id}><ToolBadge name={tool.name || tool.id || 'unknown'} image={kit.image_url} /></li>)
42
- }
31
+ for (const tool of agent?.tools ?? []) {
32
+ const toolWithImage = toolById(tool.builtin_tool_id, toolKits)
33
+ result.push(<li key={tool.builtin_tool_id}>
34
+ <ToolBadge name={toolWithImage?.name || toolWithImage?.id || 'unknown'} image={toolWithImage?.image} />
35
+ </li>)
43
36
  }
44
37
  return result
45
38
  }, [agent])
46
39
 
47
40
  return (
48
41
  <AgentDescriptionBox>
49
- {description && <section>
42
+ {agent?.description && <section>
50
43
  <Text appearance="microtext1" className="title">{t.description}</Text>
51
- <Text>{description}</Text>
44
+ <Text>{agent?.description}</Text>
52
45
  </section>}
53
46
  {(!!numberOfKnowledgeSources || !!knowledgeSources?.length) && <section>
54
47
  <Text appearance="microtext1" className="title">Knowledge sources</Text>
55
- <ul>{isLoading ? skeleton : knowledgeSources}</ul>
48
+ <ul>{isLoading || isLoadingToolKit ? skeleton : knowledgeSources}</ul>
56
49
  </section>}
57
50
  {!!tools.length && <section>
58
51
  <Text appearance="microtext1" className="title">{t.tools}</Text>
59
52
  <ul>{tools}</ul>
60
53
  </section>}
61
- {llm && <section>
54
+ {agent?.model_name && <section>
62
55
  <Text appearance="microtext1" className="title">LLM</Text>
63
- <Badge palette="orange" appearance="square">{llm}</Badge>
56
+ <Badge palette="orange" appearance="square">{agent?.model_name}</Badge>
64
57
  </section>}
65
58
  </AgentDescriptionBox>
66
59
  )
@@ -1,3 +1,4 @@
1
+ import { agentToolsClient } from '@stack-spot/portal-network'
1
2
  import { useEffect, useMemo, useRef } from 'react'
2
3
  import { RightPanelTabs } from '../../components/RightPanelTabs'
3
4
  import { useCurrentChat } from '../../context/hooks'
@@ -5,8 +6,6 @@ import { checkIsTrial } from '../../utils/check-is-trial'
5
6
  import { AgentsTab, AgentsTabWorkspace } from './AgentsTab'
6
7
  import { useAgentsDictionary } from './dictionary'
7
8
 
8
- const agentDefaultSlug = 'stk_code_buddy'
9
-
10
9
  /**
11
10
  * Renders the Agent selection form in the Right Panel if this is the panel that is currently opened.
12
11
  */
@@ -17,22 +16,22 @@ export const AgentsPanel = () => {
17
16
  const agent = useRef(chat.get('agent'))
18
17
 
19
18
  useEffect(() => {
20
- if (agentDefaultSlug !== chat.get('agent')?.slug) {
19
+ if (agentToolsClient.agentDefaultSlug !== chat.get('agent')?.slug) {
21
20
  agent.current = chat.get('agent')
22
21
  }
23
22
  }, [chat])
24
23
 
25
24
  const tabs= useMemo(() => isTrial ? [
26
- { title: t.favorites, content: <AgentsTab key="favorite" visibility="FAVORITE" agent={agent} /> },
27
- { title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" agent={agent} /> },
28
- { title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" agent={agent} /> },
25
+ { title: t.favorites, content: <AgentsTab key="favorite" visibility="favorite" agent={agent} /> },
26
+ { title: t.builtin, content: <AgentsTab key="builtin" visibility="built_in" agent={agent} /> },
27
+ { title: t.personal, content: <AgentsTab key="personal" visibility="personal" agent={agent} /> },
29
28
  ]: [
30
- { title: t.favorites, content: <AgentsTab key="favorite" visibility="FAVORITE" agent={agent} /> },
31
- { title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" agent={agent} /> },
32
- { title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" agent={agent} /> },
33
- { title: t.shared, content: <AgentsTab key="shared" visibility="SHARED" agent={agent} /> },
34
- { title: t.spots, content: <AgentsTabWorkspace key="workspace" visibility="WORKSPACE" agent={agent} /> },
35
- { title: t.account, content: <AgentsTab key="account" visibility="ACCOUNT" agent={agent} /> },
29
+ { title: t.favorites, content: <AgentsTab key="favorite" visibility="favorite" agent={agent} /> },
30
+ { title: t.builtin, content: <AgentsTab key="builtin" visibility="built_in" agent={agent} /> },
31
+ { title: t.personal, content: <AgentsTab key="personal" visibility="personal" agent={agent} /> },
32
+ { title: t.shared, content: <AgentsTab key="shared" visibility="shared" agent={agent} /> },
33
+ { title: t.spots, content: <AgentsTabWorkspace key="workspace" visibility="workspace" agent={agent} /> },
34
+ { title: t.account, content: <AgentsTab key="account" visibility="account" agent={agent} /> },
36
35
 
37
36
  ], [t, isTrial, agent])
38
37
 
@@ -2,8 +2,7 @@ import { Button, IconBox, Text } from '@citric/core'
2
2
  import { Agent, Search } from '@citric/icons'
3
3
  import { Placeholder } from '@stack-spot/portal-components/Placeholder'
4
4
  import { MiniLogo } from '@stack-spot/portal-components/svg'
5
- import { agentClient, AgentResponseWithBuiltIn, workspaceAiClient } from '@stack-spot/portal-network'
6
- import { VisibilityLevel } from '@stack-spot/portal-network/api/agent'
5
+ import { AgentResponseWithBuiltIn, agentToolsClient, AgentVisibilityLevel, workspaceAiClient } from '@stack-spot/portal-network'
7
6
  import { WorkspaceResponse } from '@stack-spot/portal-network/api/workspace-ai'
8
7
  import { useCallback, useMemo, useState } from 'react'
9
8
  import { ButtonFavorite } from '../../components/ButtonFavorite'
@@ -20,7 +19,7 @@ import { AgentLabel } from './styled'
20
19
  import { useAgentFavorites } from './useAgentFavorites'
21
20
 
22
21
  export interface AgentTabProps {
23
- visibility: VisibilityLevel | 'BUILT-IN',
22
+ visibility: AgentVisibilityLevel,
24
23
  workspaceId?: string,
25
24
  agent: React.MutableRefObject<ChatProperties['agent']>,
26
25
  showSubmitButton?: boolean,
@@ -33,16 +32,15 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
33
32
  const [filter, setFilter] = useState('')
34
33
  const { useFavorites, onAddFavorite, onRemoveFavorite } = useAgentFavorites()
35
34
  const listFavorites = useFavorites()
36
- const agentDefault = agentClient.agentDefault.useQuery({})
35
+ const agentDefault = agentToolsClient.agentDefault.useQuery()
37
36
  const agents = workspaceId
38
- ? workspaceAiClient.getAgentFromWorkspaceAi.useQuery({ workspaceId })
39
- : agentClient.allAgents.useQuery({ visibilities: [visibility] })
40
-
37
+ ? workspaceAiClient.getAgentFromWorkspaceAi.useQuery({ workspaceId }) as AgentResponseWithBuiltIn[]
38
+ : agentToolsClient.agents.useQuery({ visibility })
41
39
 
42
40
  const [value, setValue] = useState<AgentResponseWithBuiltIn | undefined>(
43
41
  agent.current
44
42
  ? agents.find(a => a.id === agent.current?.id)
45
- : chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : agentDefault,
43
+ : chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : agentDefault as AgentResponseWithBuiltIn,
46
44
  )
47
45
 
48
46
  const filtered = useMemo(
@@ -53,7 +51,7 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
53
51
 
54
52
  function submit() {
55
53
  if (value) {
56
- chat.set('agent', { id: value.id, label: value.name, image: value.avatar, builtIn: !!value.builtIn, slug: value.slug })
54
+ chat.set('agent', { id: value.id, label: value.name, image: value.avatar!, builtIn: !!value.builtIn, slug: value.slug })
57
55
  }
58
56
  close()
59
57
  }
@@ -83,13 +81,7 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
83
81
  <Text>{name}</Text>
84
82
  </AgentLabel>
85
83
  )}
86
- renderDescription={a => <AgentDescription
87
- agentId={a.id}
88
- visibility={visibility}
89
- description={a.description}
90
- llm={a.llm_config?.model_slug}
91
- numberOfKnowledgeSources={a.knowledge_sources_config?.knowledge_sources?.length ?? 0}
92
- />}
84
+ renderDescription={a => <AgentDescription agentId={a.id} />}
93
85
  optionClassName={a => (a === value && filter && !a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
94
86
  ? 'filtered-out'
95
87
  : ''
@@ -1,15 +1,15 @@
1
1
  /* eslint-disable filenames/match-regex */
2
- import { agentClient } from '@stack-spot/portal-network'
2
+ import { agentClient, agentToolsClient } from '@stack-spot/portal-network'
3
3
 
4
4
  export function useAgentFavorites() {
5
- const useFavorites = () => agentClient.allAgents.useQuery({ visibilities: ['FAVORITE'] })
5
+ const useFavorites = () => agentToolsClient.agents.useQuery({ visibility: 'favorite' })
6
6
  const [addFavorite, pendingAddFav] = agentClient.addFavoriteAgent.useMutation()
7
7
  const [removeFavorite, pendingRemoveFav] = agentClient.removeFavoriteAgent.useMutation()
8
8
 
9
9
  const removeFavoriteAgent = async (idOrSlug?: string) => {
10
10
  try {
11
11
  await removeFavorite({ agentId: idOrSlug || '' })
12
- await agentClient.allAgents.invalidate({ visibilities: ['FAVORITE'] })
12
+ await agentToolsClient.agents.invalidate({ visibility: 'favorite' })
13
13
  } catch (error) {
14
14
  // eslint-disable-next-line no-console
15
15
  console.error(error)
@@ -19,7 +19,7 @@ export function useAgentFavorites() {
19
19
  const addFavoriteAgent = async (idOrSlug?: string) => {
20
20
  try {
21
21
  await addFavorite({ agentId: idOrSlug || '' })
22
- await agentClient.allAgents.invalidate({ visibilities: ['FAVORITE'] })
22
+ await agentToolsClient.agents.invalidate({ visibility: 'favorite' })
23
23
  } catch (error) {
24
24
  // eslint-disable-next-line no-console
25
25
  console.error(error)
@@ -4,16 +4,17 @@ import { LabeledWithImage } from '../../state/types'
4
4
 
5
5
  interface Props {
6
6
  agent?: LabeledWithImage,
7
+ icon?: React.ReactElement,
7
8
  }
8
9
 
9
10
  /**
10
11
  * Renders the avatar of an agent in a message.
11
12
  */
12
- export const AgentInfo = ({ agent }: Props) => (
13
+ export const AgentInfo = ({ agent, icon }: Props) => (
13
14
  <>
14
15
  {agent?.image
15
16
  ? <img src={agent.image} className="custom-agent-image" />
16
- : <IconBox className="default-image-wrapper" colorIcon="light.700"><Agent className="agent-image" /></IconBox>
17
+ : <IconBox className="default-image-wrapper" colorIcon="light.700">{icon ?? <Agent className="agent-image" />}</IconBox>
17
18
  }
18
19
  <Text appearance="body2">{agent?.label}</Text>
19
20
  </>
@@ -1,12 +1,14 @@
1
1
  import { Box, Button, Checkbox, Flex, IconBox, Input, Label, Radio, Text } from '@citric/core'
2
2
  import { Check, Cog, Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
3
3
  import { Badge, IconButton, Tooltip } from '@citric/ui'
4
- import { agentClient } from '@stack-spot/portal-network'
4
+ import { agentToolsClient } from '@stack-spot/portal-network'
5
5
  import { listToClass } from '@stack-spot/portal-theme'
6
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
+ import { groupBy } from 'lodash'
7
8
  import { createElement, Dispatch, useCallback, useMemo, useRef, useState } from 'react'
8
9
  import { PhoneInput } from 'react-international-phone'
9
10
  import 'react-international-phone/style.css'
11
+ import { FileDescription } from '../../components/FileDescription'
10
12
  import { Markdown } from '../../components/Markdown'
11
13
  import { StackedBadge } from '../../components/StackedBadge'
12
14
  import { useChatEntry, useCurrentChat, useWidget } from '../../context/hooks'
@@ -166,6 +168,14 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
166
168
  </Flex>
167
169
  }
168
170
 
171
+ const UserInfo = ({ entry }: { entry: TextChatEntry }) => {
172
+ switch (entry.agentType) {
173
+ case 'user': return
174
+ case 'bot': return <AgentInfo agent={entry.agent} />
175
+ case 'system': return <AgentInfo agent={{ id: 'system', label: 'System' }} icon={<Cog />} />
176
+ }
177
+ }
178
+
169
179
  /**
170
180
  * Renders a message (ChatEntry) in the chat.
171
181
  */
@@ -176,7 +186,6 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
176
186
  const [labels, setLabels] = useState<string[]>(message.getValue()?.initialValue ?? [])
177
187
  const entry = useChatEntry(message)
178
188
  const dateFormatter = useDateFormatter()
179
- const userInfo = entry.agentType === 'user' ? <></> : <AgentInfo agent={entry.agent} />
180
189
  const date = new Date(entry.updated ?? '')
181
190
  const shouldShowFooter = entry.updated && !isNaN(date.getTime())
182
191
  const ref = useRef<HTMLLIElement>(null)
@@ -184,9 +193,10 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
184
193
  const widget = useWidget()
185
194
  const chat = useCurrentChat()
186
195
  const agentId = entry.agent?.id ?? ''
187
- const [agent] = agentClient.agentById.useStatefulQuery({ agentId, builtIn: !!entry?.agent?.builtIn }, { enabled: !!agentId })
196
+ const [toolKits] = agentToolsClient.tools.useStatefulQuery({ enabled: !!agentId })
188
197
  const [copied, setCopied] = useState(false)
189
198
  const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
199
+
190
200
 
191
201
  useChatScrollToBottomEffect(ref, [entry])
192
202
 
@@ -258,9 +268,26 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
258
268
  setTimeout(() => setCopied(false), 2000)
259
269
  }
260
270
 
271
+ const renderUploads = () => {
272
+ const groups = groupBy(entry.upload, f => f.image ? 'images' : 'documents')
273
+ const lists: React.ReactElement[] = []
274
+ if (groups.images?.length) {
275
+ lists.push(<ul className="image-uploads">{groups.images.map(f => <li key={f.id}><img src={f.image}></img></li>)}</ul>)
276
+ }
277
+ if (groups.documents?.length) {
278
+ lists.push(
279
+ <ul className="document-uploads">
280
+ {groups.documents.map(f => <li key={f.id}><FileDescription fileName={f.name} /></li>)}
281
+ </ul>,
282
+ )
283
+ }
284
+ return lists
285
+ }
286
+
261
287
  const renderContent = () => {
262
288
  if (entry.type === 'md') {
263
289
  return <>
290
+ {renderUploads()}
264
291
  <Markdown onCopyCode={(code) => onCopyCode(code, entry.messageId ?? '', chat)}>{entry.content}</Markdown>
265
292
  {renderActions()}
266
293
  </>
@@ -268,6 +295,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
268
295
  if (entry.type === 'text') {
269
296
  return <>
270
297
  <p className="plain-text">{entry.content}</p>
298
+ {renderUploads()}
271
299
  {renderActions()}
272
300
  </>
273
301
  }
@@ -285,21 +313,26 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
285
313
  widget.set('panel', 'tools')
286
314
  }
287
315
 
288
- return (entry.content || entry.error || !!entry.steps?.length) && (
316
+
317
+ return (entry.content || entry.error || !!entry.steps?.length || entry.upload?.length) && (
289
318
  <li key={entry.messageId} className={entry.agentType} ref={ref}>
290
319
  <div className="chat-message-container"
291
320
  onMouseEnter={entry.agentType === 'user' ? () => setShowUserButtonCopy(true) : undefined}
292
321
  onMouseLeave={entry.agentType === 'user' ? () => setShowUserButtonCopy(false) : undefined}>
293
322
  <div className="chat-message" ref={chatRef} onKeyDown={handleKeyDown} tabIndex={0}>
294
- <div className={`user-info ${entry.agentType}`}>{userInfo}</div>
323
+ <div className={`user-info ${entry.agentType}`}><UserInfo entry={entry} /></div>
295
324
  {beforeMessage && createElement(beforeMessage, { message })}
296
- {(entry.content || entry.steps) && <div className={listToClass(['message-content', entry.card && 'card', entry.type])}>
297
- {!!entry.badges?.length && <div className="badges">
298
- {entry.badges.map((b, index) => <Badge key={index} palette={b.color ?? 'cyan'} appearance="square">{b.label}</Badge>)}
299
- </div>}
300
- {renderContent()}
301
- {!!entry.steps?.length && <StepsList steps={entry.steps} chatId={chat.id} messageId={message.id} />}
302
- </div>}
325
+ {(entry.content || entry.steps || entry.upload?.length) && (
326
+ <div className={listToClass(['message-content', entry.card && 'card', entry.type])}>
327
+ {!!entry.badges?.length && <div className="badges">
328
+ {entry.badges.map((b, index) => <Badge key={index} palette={b.color ?? 'cyan'} appearance="square">{b.label}</Badge>)}
329
+ </div>}
330
+ {renderContent()}
331
+
332
+ {!!entry.steps?.length && <StepsList steps={entry.steps} chatId={chat.id} messageId={message.id} />}
333
+ </div>
334
+ )}
335
+
303
336
  {entry.error && (
304
337
  <div className="error">
305
338
  <IconBox size="xs"><TimesCircle /></IconBox>
@@ -316,7 +349,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
316
349
  className="tools-badge"
317
350
  label={t.tools}
318
351
  images={entry.tools.slice(0, 3).map((id) => {
319
- const tool = toolById(id, agent?.toolkits)
352
+ const tool = toolById(id, toolKits)
320
353
  return { key: id, name: tool?.name || id, icon: <Cog />, url: tool?.image }
321
354
  })}
322
355
  onClick={openToolsPanel}
@@ -329,7 +362,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
329
362
  </li>
330
363
  ))}</ul>
331
364
  </div>}
332
-
365
+
333
366
  {shouldShowFooter && <div className="message-footer">
334
367
  {entry.agentType === 'bot' && !entry.error && <div className="message-actions">
335
368
  {entry.type === 'md' && (
@@ -368,6 +401,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
368
401
  </>
369
402
  )}
370
403
  </div>}
404
+
371
405
  {entry.agentType === 'user' && (
372
406
  <div className="message-actions">
373
407
  {copied ? (
@@ -384,7 +418,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
384
418
  </Tooltip>
385
419
  ) : (
386
420
  showUserButtonCopy && (
387
- <div>
421
+ <div className="action-bar">
388
422
  <IconButton
389
423
  appearance="square"
390
424
  color="light"
@@ -393,13 +427,14 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
393
427
  onClick={handleCopy}
394
428
  size="sm"
395
429
  >
396
- <Copy/>
430
+ <Copy className="copy-btn"/>
397
431
  </IconButton>
398
432
  </div>
399
433
  )
400
434
  )}
401
435
  </div>
402
436
  )}
437
+
403
438
  <Text as="label" appearance="microtext1" className="chat-date">
404
439
  {dateFormatter.formatForChatMessage(date)}
405
440
  </Text>
@@ -39,7 +39,7 @@ export const ChatList: IStyledComponentBase<
39
39
  flex-direction: row;
40
40
  gap: 10px;
41
41
 
42
- &.bot {
42
+ &.bot, &.system {
43
43
  align-items: center;
44
44
  }
45
45
  }
@@ -139,6 +139,20 @@ export const ChatList: IStyledComponentBase<
139
139
  background: linear-gradient(180deg, ${theme.color.blue[500]} 0%, ${theme.color.indigo[500]} 100%);
140
140
  }
141
141
  }
142
+ .image-uploads, .document-uploads {
143
+ display: flex;
144
+ flex-direction: row;
145
+ flex-wrap: nowrap;
146
+ gap: 8px;
147
+ list-style: none;
148
+ margin: 0 0 4px 0;
149
+ padding: 0;
150
+
151
+ img {
152
+ max-width: 240px;
153
+ max-height: 240px;
154
+ }
155
+ }
142
156
  }
143
157
 
144
158
  &.user {
@@ -2,7 +2,7 @@ import { IconBox, Input } from '@citric/core'
2
2
  import { Check, Download, EllipsisHorizontal, Pencil, Trash } from '@citric/icons'
3
3
  import { IconButton, LoadingCircular } from '@citric/ui'
4
4
  import { focusNextIgnoringChildren } from '@stack-spot/portal-components'
5
- import { aiClient } from '@stack-spot/portal-network'
5
+ import { agentToolsClient, aiClient } from '@stack-spot/portal-network'
6
6
  import { ConversationResponse } from '@stack-spot/portal-network/api/ai'
7
7
  import { theme } from '@stack-spot/portal-theme'
8
8
  import { last } from 'lodash'
@@ -11,12 +11,13 @@ import { OverlayMenu } from '../../components/OverlayMenu'
11
11
  import { useWidget } from '../../context/hooks'
12
12
  import { ChatEntry } from '../../state/ChatEntry'
13
13
  import { ChatState } from '../../state/ChatState'
14
+ import { LabeledAgent } from '../../state/types'
14
15
  import { ButtonAction } from '../../types'
15
16
  import { download } from '../../utils/download'
16
17
  import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source'
17
18
  import { useHistoryDictionary } from './dictionary'
18
19
  import { HistoryItemBox } from './styled'
19
- import { findStack, findWorkspace, getAllAgents } from './utils'
20
+ import { findStack, findWorkspace } from './utils'
20
21
 
21
22
  /**
22
23
  * Renders an item of the list of conversations (history).
@@ -88,12 +89,16 @@ export const HistoryItem = ({ item }: { item: ConversationResponse }) => {
88
89
  setLoading(true)
89
90
  try {
90
91
  const chat = await aiClient.chat.query({ conversationId: item.id })
92
+ const historyAgentIds = chat.history?.map(item => item.custom_agent?.id).filter(Boolean) as string[] ?? []
91
93
  const [stack, workspace, agents] = await Promise.all([
92
94
  findStack(chat.ai_stack_id),
93
95
  findWorkspace(chat.workspace_id),
94
- getAllAgents(),
96
+ agentToolsClient.agentsByIds.query({ searchAgentsRequest: { ids: historyAgentIds } }),
95
97
  ])
96
- const agent = agents.find(a => a.id === last(chat.history)?.custom_agent?.id)
98
+ const agentsAsLabeledAgents: LabeledAgent[] = agents
99
+ .map((a) => ({ ...a, label: a.name, image: a.avatar ?? '', builtIn: a.visibility_level === 'built_in' }))
100
+
101
+ const agent = agentsAsLabeledAgents.find(a => a.id === last(chat.history)?.custom_agent?.id)
97
102
  const builtIn = !!last(chat.history ?? [])?.custom_agent?.built_in
98
103
  widget.chatTabs.add(new ChatState({
99
104
  id: chat.id,
@@ -103,7 +108,7 @@ export const HistoryItem = ({ item }: { item: ConversationResponse }) => {
103
108
  agentType: item.agent === 'USER' ? 'user' : 'bot',
104
109
  content: item.content,
105
110
  type: 'md',
106
- agent: agents.find(a => a.id === item.custom_agent?.id),
111
+ agent: agentsAsLabeledAgents.find(a => a.id === item.custom_agent?.id),
107
112
  messageId: item.message_id,
108
113
  knowledgeSources: item.agent === 'USER' ? undefined : genericSourcesToKnowledgeSources(item.sources),
109
114
  updated: item.updated,
@@ -1,6 +1,5 @@
1
- import { agentClient, aiClient, workspaceClient } from '@stack-spot/portal-network'
1
+ import { aiClient, workspaceClient } from '@stack-spot/portal-network'
2
2
  import { ChatProperties } from '../../state/ChatState'
3
- import { LabeledWithImage } from '../../state/types'
4
3
 
5
4
  /**
6
5
  * Finds a stack by its id.
@@ -36,19 +35,3 @@ export async function findWorkspace(id: string | null): Promise<ChatProperties['
36
35
  return { id, label: id }
37
36
  }
38
37
  }
39
-
40
-
41
- /**
42
- * Finds all the agents, including common agents and public agents.
43
- * @returns an array with every agent.
44
- */
45
- export async function getAllAgents(): Promise<LabeledWithImage[]> {
46
- try {
47
- const agents = await agentClient.allAgents.query({ visibilities: ['ALL'] })
48
- return agents.map(a => ({ id: a.id, label: a.name, image: a.avatar, slug: a.slug, builtIn: a.builtIn }))
49
- } catch (error) {
50
- // eslint-disable-next-line no-console
51
- console.error(error)
52
- return []
53
- }
54
- }
@@ -1,6 +1,6 @@
1
1
  import { IconBox, Text } from '@citric/core'
2
2
  import { Agent } from '@citric/icons'
3
- import { agentClient } from '@stack-spot/portal-network'
3
+ import { agentToolsClient } from '@stack-spot/portal-network'
4
4
  import { useMemo } from 'react'
5
5
  import { QuickStartButton } from '../../components/QuickStartButton'
6
6
  import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
@@ -12,15 +12,15 @@ import { HomeBox } from './styled'
12
12
  */
13
13
  export const CustomAgent = () => {
14
14
  const { id, label, image } = useCurrentChatState('agent') ?? {}
15
- const [agent] = agentClient.agent.useStatefulQuery({ agentId: id! })
15
+ const [agent] = agentToolsClient.agent.useStatefulQuery({ agentId: id! })
16
16
  const chat = useCurrentChat()
17
- const suggestions = useMemo(() => agent?.suggested_prompts?.map((prompt, index) => (
17
+ const suggestions = useMemo(() => agent?.conversation_starter?.map((prompt, index) => (
18
18
  <QuickStartButton
19
19
  key={index}
20
20
  label={prompt}
21
21
  onClick={() => send(prompt)}
22
22
  />
23
- )), [agent?.suggested_prompts])
23
+ )), [agent?.conversation_starter])
24
24
 
25
25
  function send(message: string) {
26
26
  chat.pushMessage(ChatEntry.createUserEntry(message))
@@ -1,6 +1,6 @@
1
1
  import { Flex, IconBox, Image } from '@citric/core'
2
2
  import { Agent } from '@citric/icons'
3
- import { agentClient, AgentResponseWithBuiltIn } from '@stack-spot/portal-network'
3
+ import { AgentResponseWithBuiltIn, agentToolsClient } from '@stack-spot/portal-network'
4
4
  import { useCallback } from 'react'
5
5
  import { Selector } from '../../components/Selector'
6
6
  import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
@@ -53,9 +53,9 @@ export const AgentSelector = ({ inputRef, isTrial }: {
53
53
 
54
54
  const getAgents = () => {
55
55
  if (isTrial) {
56
- return agentClient.allAgents.useQuery({ visibilities: ['PERSONAL', 'BUILT-IN'] })
56
+ return agentToolsClient.allAgents.useQuery({ visibilities: ['personal', 'built_in'] })
57
57
  }
58
- return agentClient.allAgents.useQuery({ visibilities: ['ACCOUNT', 'SHARED', 'PERSONAL', 'BUILT-IN', 'WORKSPACE'] })
58
+ return agentToolsClient.allAgents.useQuery({ visibilities: ['account', 'shared', 'personal', 'built_in', 'workspace'] })
59
59
  }
60
60
 
61
61
  return <Selector
@@ -68,7 +68,7 @@ export const AgentSelector = ({ inputRef, isTrial }: {
68
68
  regex: agentRegex,
69
69
  urlBuilder: (agent) => `/agents/${agent?.id}`,
70
70
  searchProp: 'name',
71
- sections: isTrial ? ['favorite', 'personal', 'built-in'] : ['favorite', 'personal', 'workspace', 'account', 'shared', 'built-in'],
71
+ sections: isTrial ? ['favorite', 'personal', 'built_in'] : ['favorite', 'personal', 'workspace', 'account', 'shared', 'built_in'],
72
72
  renderComponentItem: AgentItem,
73
73
  isEnabled: isAgentEnabled,
74
74
  onSelect: onSelectItem,
@@ -2,14 +2,14 @@ import { Flex, IconBox } from '@citric/core'
2
2
  import { Agent, TimesMini } from '@citric/icons'
3
3
  import { IconButton, Tooltip } from '@citric/ui'
4
4
  import { MiniLogo } from '@stack-spot/portal-components/svg'
5
- import { agentClient } from '@stack-spot/portal-network'
5
+ import { agentToolsClient } from '@stack-spot/portal-network'
6
6
  import { useEffect } from 'react'
7
7
  import { useCurrentChat, useCurrentChatState, useWidget } from '../../context/hooks'
8
8
  import { useMessageInputDictionary } from './dictionary'
9
9
 
10
10
  export const ButtonAgent = () => {
11
11
  const t = useMessageInputDictionary()
12
- const agentDefault = agentClient.agentDefault.useQuery({})
12
+ const agentDefault = agentToolsClient.agentDefault.useQuery()
13
13
  const widget = useWidget()
14
14
  const chat = useCurrentChat()
15
15
  const agent = useCurrentChatState('agent')