@stack-spot/ai-chat-widget 1.28.1-beta.2 → 1.28.1
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 +8 -0
- package/dist/app-metadata.json +3 -3
- package/dist/components/AdaptiveTextArea.js +1 -1
- package/dist/components/AdaptiveTextArea.js.map +1 -1
- package/dist/components/AgentCard/dictionary.d.ts +2 -4
- package/dist/components/AgentCard/dictionary.d.ts.map +1 -1
- package/dist/components/AgentCard/dictionary.js +2 -4
- package/dist/components/AgentCard/dictionary.js.map +1 -1
- package/dist/components/Selector/index.d.ts +2 -2
- package/dist/components/Selector/index.d.ts.map +1 -1
- package/dist/components/Selector/index.js +3 -3
- package/dist/components/Selector/index.js.map +1 -1
- package/dist/components/Selector/styled.d.ts +1 -1
- package/dist/components/Selector/styled.d.ts.map +1 -1
- package/dist/components/Selector/styled.js +1 -1
- package/dist/components/Selector/styled.js.map +1 -1
- package/dist/state/types.d.ts +1 -1
- package/dist/state/types.d.ts.map +1 -1
- package/dist/utils/tools.d.ts +2 -2
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +6 -3
- package/dist/utils/tools.js.map +1 -1
- package/dist/views/Agents/AgentDescription.d.ts +9 -2
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.js +9 -11
- package/dist/views/Agents/AgentDescription.js.map +1 -1
- package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
- package/dist/views/Agents/AgentsPanel.js +11 -11
- package/dist/views/Agents/AgentsPanel.js.map +1 -1
- package/dist/views/Agents/AgentsTab.d.ts +2 -2
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
- package/dist/views/Agents/AgentsTab.js +6 -6
- package/dist/views/Agents/AgentsTab.js.map +1 -1
- package/dist/views/Agents/useAgentFavorites.d.ts +1 -1
- package/dist/views/Agents/useAgentFavorites.js +4 -4
- package/dist/views/Agents/useAgentFavorites.js.map +1 -1
- package/dist/views/Chat/ChatMessage.js +3 -3
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.js +5 -8
- package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
- package/dist/views/ChatHistory/utils.d.ts +6 -0
- package/dist/views/ChatHistory/utils.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.js +16 -1
- package/dist/views/ChatHistory/utils.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.js +4 -4
- package/dist/views/MessageInput/AgentSelector.js.map +1 -1
- package/dist/views/MessageInput/ButtonAgent.js +2 -2
- package/dist/views/MessageInput/ButtonAgent.js.map +1 -1
- package/dist/views/MessageInput/styled.d.ts +1 -1
- package/dist/views/MessageInput/styled.js +1 -1
- package/dist/views/Tools.js +3 -3
- package/dist/views/Tools.js.map +1 -1
- package/dist/views/Workspaces/WorkspacesTab.js +1 -1
- package/package.json +2 -2
- package/src/app-metadata.json +3 -3
- package/src/components/AdaptiveTextArea.tsx +1 -1
- package/src/components/AgentCard/dictionary.ts +2 -4
- package/src/components/Selector/index.tsx +6 -5
- package/src/components/Selector/styled.ts +2 -2
- package/src/layout.css +0 -17
- package/src/state/types.ts +1 -1
- package/src/utils/tools.ts +7 -5
- package/src/utils/upload/UploadManager.ts +23 -32
- package/src/views/Agents/AgentDescription.tsx +25 -19
- package/src/views/Agents/AgentsPanel.tsx +12 -11
- package/src/views/Agents/AgentsTab.tsx +17 -12
- package/src/views/Agents/useAgentFavorites.ts +4 -4
- package/src/views/Chat/ChatMessage.tsx +3 -3
- package/src/views/ChatHistory/HistoryItem.tsx +5 -10
- package/src/views/ChatHistory/utils.ts +18 -1
- package/src/views/Home/CustomAgent.tsx +4 -4
- package/src/views/MessageInput/AgentSelector.tsx +4 -4
- package/src/views/MessageInput/ButtonAgent.tsx +2 -2
- package/src/views/MessageInput/dictionary.ts +0 -9
- package/src/views/MessageInput/index.tsx +1 -15
- package/src/views/MessageInput/styled.ts +1 -1
- package/src/views/Tools.tsx +3 -4
- package/src/views/Workspaces/WorkspacesTab.tsx +1 -1
- package/src/utils/upload/use-paste-upload.tsx +0 -30
- package/src/views/MessageInput/UploadDragNDrop.tsx +0 -83
package/src/utils/tools.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { keyBy } from 'lodash'
|
|
1
|
+
import { BuiltinToolResponse, ToolkitsInAgentResponse } from '@stack-spot/portal-network/api/agent'
|
|
3
2
|
|
|
4
3
|
export type ToolWithImage = BuiltinToolResponse & { id: string, image?: string }
|
|
5
4
|
|
|
6
|
-
export function toolById(id: string, toolkits:
|
|
7
|
-
const
|
|
8
|
-
|
|
5
|
+
export function toolById(id: string, toolkits: ToolkitsInAgentResponse | undefined): ToolWithImage | undefined {
|
|
6
|
+
for (const toolkit of toolkits?.builtins ?? []) {
|
|
7
|
+
for (const tool of toolkit.tools ?? []) {
|
|
8
|
+
if (tool.id === id) return { ...tool, id, image: toolkit.image_url }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
9
11
|
}
|
|
@@ -43,35 +43,31 @@ export class UploadManager {
|
|
|
43
43
|
return this.value.some(f => id === f.id)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
private processFiles(files: FileList | File[]) {
|
|
47
|
-
const newFiles: FileUpload[] = []
|
|
48
|
-
const errors: UploadError[] = []
|
|
49
|
-
for (const f of files ?? []) {
|
|
50
|
-
if (this.maxSize && f.size > this.maxSize.value * Math.pow(1024, unitPower[this.maxSize?.unit])) {
|
|
51
|
-
errors.push(new FileIsTooLarge(f.name, this.maxSize))
|
|
52
|
-
} else if (this.maxItems && this.value.length + newFiles.length === this.maxItems) {
|
|
53
|
-
errors.push(new MaxFilesReached(f.name, this.maxItems))
|
|
54
|
-
} else if (this.hasEquivalentFile(f)) {
|
|
55
|
-
errors.push(new FileAlreadyExists(f.name))
|
|
56
|
-
} else {
|
|
57
|
-
const upload = new FileUpload(f)
|
|
58
|
-
newFiles.push(upload)
|
|
59
|
-
upload.onChange(() => this.runStatusListeners())
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (newFiles.length) {
|
|
63
|
-
this.value = [...this.value, ...newFiles]
|
|
64
|
-
this.runChangeListeners()
|
|
65
|
-
this.runStatusListeners()
|
|
66
|
-
}
|
|
67
|
-
if (errors.length) {
|
|
68
|
-
this.errorListeners.forEach(l => l(errors))
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
46
|
private attachEventListeners() {
|
|
73
47
|
const handleChange = () => {
|
|
74
|
-
|
|
48
|
+
const newFiles: FileUpload[] = []
|
|
49
|
+
const errors: UploadError[] = []
|
|
50
|
+
for (const f of this.input?.files ?? []) {
|
|
51
|
+
if (this.maxSize && f.size > this.maxSize.value * Math.pow(1024, unitPower[this.maxSize?.unit])) {
|
|
52
|
+
errors.push(new FileIsTooLarge(f.name, this.maxSize))
|
|
53
|
+
} else if (this.maxItems && this.value.length + newFiles.length === this.maxItems) {
|
|
54
|
+
errors.push(new MaxFilesReached(f.name, this.maxItems))
|
|
55
|
+
} else if (this.hasEquivalentFile(f)) {
|
|
56
|
+
errors.push(new FileAlreadyExists(f.name))
|
|
57
|
+
} else {
|
|
58
|
+
const upload = new FileUpload(f)
|
|
59
|
+
newFiles.push(upload)
|
|
60
|
+
upload.onChange(() => this.runStatusListeners())
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (newFiles.length) {
|
|
64
|
+
this.value = [...this.value, ...newFiles]
|
|
65
|
+
this.runChangeListeners()
|
|
66
|
+
this.runStatusListeners()
|
|
67
|
+
}
|
|
68
|
+
if (errors.length) {
|
|
69
|
+
this.errorListeners.forEach(l => l(errors))
|
|
70
|
+
}
|
|
75
71
|
this.destroy()
|
|
76
72
|
}
|
|
77
73
|
this.onDestroy = () => this.input?.removeEventListener('change', handleChange)
|
|
@@ -112,11 +108,6 @@ export class UploadManager {
|
|
|
112
108
|
this.runChangeListeners()
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
add(files: File | File[] = []) {
|
|
116
|
-
const fileList = Array.isArray(files) ? files : [files]
|
|
117
|
-
this.processFiles(fileList)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
111
|
remove(file: FileUpload) {
|
|
121
112
|
file.destroy()
|
|
122
113
|
this.value = this.value.filter(f => f !== file)
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import { Text } from '@citric/core'
|
|
2
2
|
import { Badge, Skeleton } from '@citric/ui'
|
|
3
|
-
import {
|
|
3
|
+
import { agentClient, AgentVisibilityLevel } from '@stack-spot/portal-network'
|
|
4
4
|
import { useMemo } from 'react'
|
|
5
5
|
import { ToolBadge } from '../../components/ToolBadge'
|
|
6
|
-
import { toolById } from '../../utils/tools'
|
|
7
6
|
import { useAgentsDictionary } from './dictionary'
|
|
8
7
|
import { AgentDescriptionBox } from './styled'
|
|
9
8
|
|
|
10
|
-
|
|
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) => {
|
|
11
18
|
const t = useAgentsDictionary()
|
|
12
|
-
const [agent, , , { isLoading }] =
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
const [agent, , , { isLoading }] = agentClient.agentById.useStatefulQuery(
|
|
20
|
+
{ agentId: agentId!, builtIn: visibility === 'BUILT-IN' },
|
|
21
|
+
{ enabled: !!agentId },
|
|
22
|
+
)
|
|
15
23
|
|
|
16
24
|
const knowledgeSources = useMemo(
|
|
17
|
-
() => agent?.
|
|
18
|
-
<li key={index}><Badge palette="teal" appearance="square">{ks}</Badge></li>
|
|
25
|
+
() => agent?.knowledge_sources_config?.knowledge_sources_details?.map((ks, index) => (
|
|
26
|
+
<li key={index}><Badge palette="teal" appearance="square">{ks.name}</Badge></li>
|
|
19
27
|
)),
|
|
20
28
|
[agent],
|
|
21
29
|
)
|
|
@@ -28,33 +36,31 @@ export const AgentDescription = ({ agentId }: { agentId?: string }) => {
|
|
|
28
36
|
}, [numberOfKnowledgeSources])
|
|
29
37
|
const tools = useMemo(() => {
|
|
30
38
|
const result: React.ReactElement[] = []
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<ToolBadge name={toolWithImage?.name || toolWithImage?.id || 'unknown'} image={toolWithImage?.image} />
|
|
36
|
-
</li>)
|
|
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
|
+
}
|
|
37
43
|
}
|
|
38
44
|
return result
|
|
39
45
|
}, [agent])
|
|
40
46
|
|
|
41
47
|
return (
|
|
42
48
|
<AgentDescriptionBox>
|
|
43
|
-
{
|
|
49
|
+
{description && <section>
|
|
44
50
|
<Text appearance="microtext1" className="title">{t.description}</Text>
|
|
45
|
-
<Text>{
|
|
51
|
+
<Text>{description}</Text>
|
|
46
52
|
</section>}
|
|
47
53
|
{(!!numberOfKnowledgeSources || !!knowledgeSources?.length) && <section>
|
|
48
54
|
<Text appearance="microtext1" className="title">Knowledge sources</Text>
|
|
49
|
-
<ul>{isLoading
|
|
55
|
+
<ul>{isLoading ? skeleton : knowledgeSources}</ul>
|
|
50
56
|
</section>}
|
|
51
57
|
{!!tools.length && <section>
|
|
52
58
|
<Text appearance="microtext1" className="title">{t.tools}</Text>
|
|
53
59
|
<ul>{tools}</ul>
|
|
54
60
|
</section>}
|
|
55
|
-
{
|
|
61
|
+
{llm && <section>
|
|
56
62
|
<Text appearance="microtext1" className="title">LLM</Text>
|
|
57
|
-
<Badge palette="orange" appearance="square">{
|
|
63
|
+
<Badge palette="orange" appearance="square">{llm}</Badge>
|
|
58
64
|
</section>}
|
|
59
65
|
</AgentDescriptionBox>
|
|
60
66
|
)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
2
1
|
import { useEffect, useMemo, useRef } from 'react'
|
|
3
2
|
import { RightPanelTabs } from '../../components/RightPanelTabs'
|
|
4
3
|
import { useCurrentChat } from '../../context/hooks'
|
|
@@ -6,6 +5,8 @@ import { checkIsTrial } from '../../utils/check-is-trial'
|
|
|
6
5
|
import { AgentsTab, AgentsTabWorkspace } from './AgentsTab'
|
|
7
6
|
import { useAgentsDictionary } from './dictionary'
|
|
8
7
|
|
|
8
|
+
const agentDefaultSlug = 'stk_code_buddy'
|
|
9
|
+
|
|
9
10
|
/**
|
|
10
11
|
* Renders the Agent selection form in the Right Panel if this is the panel that is currently opened.
|
|
11
12
|
*/
|
|
@@ -16,22 +17,22 @@ export const AgentsPanel = () => {
|
|
|
16
17
|
const agent = useRef(chat.get('agent'))
|
|
17
18
|
|
|
18
19
|
useEffect(() => {
|
|
19
|
-
if (
|
|
20
|
+
if (agentDefaultSlug !== chat.get('agent')?.slug) {
|
|
20
21
|
agent.current = chat.get('agent')
|
|
21
22
|
}
|
|
22
23
|
}, [chat])
|
|
23
24
|
|
|
24
25
|
const tabs= useMemo(() => isTrial ? [
|
|
25
|
-
{ title: t.favorites, content: <AgentsTab key="favorite" visibility="
|
|
26
|
-
{ title: t.builtin, content: <AgentsTab key="builtin" visibility="
|
|
27
|
-
{ title: t.personal, content: <AgentsTab key="personal" visibility="
|
|
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} /> },
|
|
28
29
|
]: [
|
|
29
|
-
{ title: t.favorites, content: <AgentsTab key="favorite" visibility="
|
|
30
|
-
{ title: t.builtin, content: <AgentsTab key="builtin" visibility="
|
|
31
|
-
{ title: t.personal, content: <AgentsTab key="personal" visibility="
|
|
32
|
-
{ title: t.shared, content: <AgentsTab key="shared" visibility="
|
|
33
|
-
{ title: t.spots, content: <AgentsTabWorkspace key="workspace" visibility="
|
|
34
|
-
{ title: t.account, content: <AgentsTab key="account" visibility="
|
|
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} /> },
|
|
35
36
|
|
|
36
37
|
], [t, isTrial, agent])
|
|
37
38
|
|
|
@@ -2,7 +2,8 @@ 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 {
|
|
5
|
+
import { agentClient, AgentResponseWithBuiltIn, workspaceAiClient } from '@stack-spot/portal-network'
|
|
6
|
+
import { VisibilityLevel } from '@stack-spot/portal-network/api/agent'
|
|
6
7
|
import { WorkspaceResponse } from '@stack-spot/portal-network/api/workspace-ai'
|
|
7
8
|
import { useCallback, useMemo, useState } from 'react'
|
|
8
9
|
import { ButtonFavorite } from '../../components/ButtonFavorite'
|
|
@@ -19,7 +20,7 @@ import { AgentLabel } from './styled'
|
|
|
19
20
|
import { useAgentFavorites } from './useAgentFavorites'
|
|
20
21
|
|
|
21
22
|
export interface AgentTabProps {
|
|
22
|
-
visibility:
|
|
23
|
+
visibility: VisibilityLevel | 'BUILT-IN',
|
|
23
24
|
workspaceId?: string,
|
|
24
25
|
agent: React.MutableRefObject<ChatProperties['agent']>,
|
|
25
26
|
showSubmitButton?: boolean,
|
|
@@ -32,15 +33,16 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
32
33
|
const [filter, setFilter] = useState('')
|
|
33
34
|
const { useFavorites, onAddFavorite, onRemoveFavorite } = useAgentFavorites()
|
|
34
35
|
const listFavorites = useFavorites()
|
|
35
|
-
const agentDefault =
|
|
36
|
+
const agentDefault = agentClient.agentDefault.useQuery({})
|
|
36
37
|
const agents = workspaceId
|
|
37
|
-
? workspaceAiClient.getAgentFromWorkspaceAi.useQuery({ workspaceId })
|
|
38
|
-
:
|
|
38
|
+
? workspaceAiClient.getAgentFromWorkspaceAi.useQuery({ workspaceId })
|
|
39
|
+
: agentClient.allAgents.useQuery({ visibilities: [visibility] })
|
|
40
|
+
|
|
39
41
|
|
|
40
42
|
const [value, setValue] = useState<AgentResponseWithBuiltIn | undefined>(
|
|
41
43
|
agent.current
|
|
42
44
|
? agents.find(a => a.id === agent.current?.id)
|
|
43
|
-
: chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : agentDefault
|
|
45
|
+
: chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : agentDefault,
|
|
44
46
|
)
|
|
45
47
|
|
|
46
48
|
const filtered = useMemo(
|
|
@@ -51,17 +53,14 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
51
53
|
|
|
52
54
|
function submit() {
|
|
53
55
|
if (value) {
|
|
54
|
-
chat.set(
|
|
55
|
-
'agent',
|
|
56
|
-
{ id: value.id, label: value.name, image: value.avatar!, builtIn: value.visibility_level === 'built_in', slug: value.slug },
|
|
57
|
-
)
|
|
56
|
+
chat.set('agent', { id: value.id, label: value.name, image: value.avatar, builtIn: !!value.builtIn, slug: value.slug })
|
|
58
57
|
}
|
|
59
58
|
close()
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
const onChange = useCallback((newValue: AgentResponseWithBuiltIn) => {
|
|
63
62
|
setValue(newValue)
|
|
64
|
-
agent.current = { ...newValue, label: newValue.name, builtIn: newValue.
|
|
63
|
+
agent.current = { ...newValue, label: newValue.name, builtIn: !!newValue.builtIn }
|
|
65
64
|
}, [])
|
|
66
65
|
|
|
67
66
|
return (
|
|
@@ -84,7 +83,13 @@ export const AgentsTab = ({ visibility, workspaceId, agent, showSubmitButton = t
|
|
|
84
83
|
<Text>{name}</Text>
|
|
85
84
|
</AgentLabel>
|
|
86
85
|
)}
|
|
87
|
-
renderDescription={a => <AgentDescription
|
|
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
|
+
/>}
|
|
88
93
|
optionClassName={a => (a === value && filter && !a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
|
|
89
94
|
? 'filtered-out'
|
|
90
95
|
: ''
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/* eslint-disable filenames/match-regex */
|
|
2
|
-
import { agentClient
|
|
2
|
+
import { agentClient } from '@stack-spot/portal-network'
|
|
3
3
|
|
|
4
4
|
export function useAgentFavorites() {
|
|
5
|
-
const useFavorites = () =>
|
|
5
|
+
const useFavorites = () => agentClient.allAgents.useQuery({ visibilities: ['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
|
|
12
|
+
await agentClient.allAgents.invalidate({ visibilities: ['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
|
|
22
|
+
await agentClient.allAgents.invalidate({ visibilities: ['FAVORITE'] })
|
|
23
23
|
} catch (error) {
|
|
24
24
|
// eslint-disable-next-line no-console
|
|
25
25
|
console.error(error)
|
|
@@ -1,7 +1,7 @@
|
|
|
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 {
|
|
4
|
+
import { agentClient } 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
7
|
import { groupBy } from 'lodash'
|
|
@@ -193,7 +193,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
|
|
|
193
193
|
const widget = useWidget()
|
|
194
194
|
const chat = useCurrentChat()
|
|
195
195
|
const agentId = entry.agent?.id ?? ''
|
|
196
|
-
const [
|
|
196
|
+
const [agent] = agentClient.agentById.useStatefulQuery({ agentId, builtIn: !!entry?.agent?.builtIn }, { enabled: !!agentId })
|
|
197
197
|
const [copied, setCopied] = useState(false)
|
|
198
198
|
const [showUserButtonCopy, setShowUserButtonCopy] = useState(false)
|
|
199
199
|
|
|
@@ -348,7 +348,7 @@ export const ChatMessage = ({ message, isLast, beforeMessage, afterMessage }: Pr
|
|
|
348
348
|
className="tools-badge"
|
|
349
349
|
label={t.tools}
|
|
350
350
|
images={entry.tools.slice(0, 3).map((id) => {
|
|
351
|
-
const tool = toolById(id,
|
|
351
|
+
const tool = toolById(id, agent?.toolkits)
|
|
352
352
|
return { key: id, name: tool?.name || id, icon: <Cog />, url: tool?.image }
|
|
353
353
|
})}
|
|
354
354
|
onClick={openToolsPanel}
|
|
@@ -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 {
|
|
5
|
+
import { 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,13 +11,12 @@ 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'
|
|
15
14
|
import { ButtonAction } from '../../types'
|
|
16
15
|
import { download } from '../../utils/download'
|
|
17
16
|
import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source'
|
|
18
17
|
import { useHistoryDictionary } from './dictionary'
|
|
19
18
|
import { HistoryItemBox } from './styled'
|
|
20
|
-
import { findStack, findWorkspace } from './utils'
|
|
19
|
+
import { findStack, findWorkspace, getAllAgents } from './utils'
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* Renders an item of the list of conversations (history).
|
|
@@ -89,16 +88,12 @@ export const HistoryItem = ({ item }: { item: ConversationResponse }) => {
|
|
|
89
88
|
setLoading(true)
|
|
90
89
|
try {
|
|
91
90
|
const chat = await aiClient.chat.query({ conversationId: item.id })
|
|
92
|
-
const historyAgentIds = chat.history?.map(item => item.custom_agent?.id).filter(Boolean) as string[] ?? []
|
|
93
91
|
const [stack, workspace, agents] = await Promise.all([
|
|
94
92
|
findStack(chat.ai_stack_id),
|
|
95
93
|
findWorkspace(chat.workspace_id),
|
|
96
|
-
|
|
94
|
+
getAllAgents(),
|
|
97
95
|
])
|
|
98
|
-
const
|
|
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)
|
|
96
|
+
const agent = agents.find(a => a.id === last(chat.history)?.custom_agent?.id)
|
|
102
97
|
const builtIn = !!last(chat.history ?? [])?.custom_agent?.built_in
|
|
103
98
|
widget.chatTabs.add(new ChatState({
|
|
104
99
|
id: chat.id,
|
|
@@ -108,7 +103,7 @@ export const HistoryItem = ({ item }: { item: ConversationResponse }) => {
|
|
|
108
103
|
agentType: item.agent === 'USER' ? 'user' : 'bot',
|
|
109
104
|
content: item.content,
|
|
110
105
|
type: 'md',
|
|
111
|
-
agent:
|
|
106
|
+
agent: agents.find(a => a.id === item.custom_agent?.id),
|
|
112
107
|
messageId: item.message_id,
|
|
113
108
|
knowledgeSources: item.agent === 'USER' ? undefined : genericSourcesToKnowledgeSources(item.sources),
|
|
114
109
|
updated: item.updated,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { aiClient, workspaceClient } from '@stack-spot/portal-network'
|
|
1
|
+
import { agentClient, aiClient, workspaceClient } from '@stack-spot/portal-network'
|
|
2
2
|
import { ChatProperties } from '../../state/ChatState'
|
|
3
|
+
import { LabeledWithImage } from '../../state/types'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Finds a stack by its id.
|
|
@@ -35,3 +36,19 @@ export async function findWorkspace(id: string | null): Promise<ChatProperties['
|
|
|
35
36
|
return { id, label: id }
|
|
36
37
|
}
|
|
37
38
|
}
|
|
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 {
|
|
3
|
+
import { agentClient } 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] =
|
|
15
|
+
const [agent] = agentClient.agent.useStatefulQuery({ agentId: id! })
|
|
16
16
|
const chat = useCurrentChat()
|
|
17
|
-
const suggestions = useMemo(() => agent?.
|
|
17
|
+
const suggestions = useMemo(() => agent?.suggested_prompts?.map((prompt, index) => (
|
|
18
18
|
<QuickStartButton
|
|
19
19
|
key={index}
|
|
20
20
|
label={prompt}
|
|
21
21
|
onClick={() => send(prompt)}
|
|
22
22
|
/>
|
|
23
|
-
)), [agent?.
|
|
23
|
+
)), [agent?.suggested_prompts])
|
|
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 {
|
|
3
|
+
import { agentClient, AgentResponseWithBuiltIn } 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
|
|
56
|
+
return agentClient.allAgents.useQuery({ visibilities: ['PERSONAL', 'BUILT-IN'] })
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return agentClient.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', '
|
|
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 {
|
|
5
|
+
import { agentClient } 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 =
|
|
12
|
+
const agentDefault = agentClient.agentDefault.useQuery({})
|
|
13
13
|
const widget = useWidget()
|
|
14
14
|
const chat = useCurrentChat()
|
|
15
15
|
const agent = useCurrentChatState('agent')
|
|
@@ -9,7 +9,6 @@ const dictionary = {
|
|
|
9
9
|
agent: 'Select agent',
|
|
10
10
|
send: 'Send message',
|
|
11
11
|
placeholder: 'Send a message to $0. For commands, use @ or /',
|
|
12
|
-
typing: 'Type your message...',
|
|
13
12
|
cancel: 'Cancel',
|
|
14
13
|
removeConfig: 'Remove all the configuration',
|
|
15
14
|
removeStack: 'Stop using the current stack',
|
|
@@ -23,10 +22,6 @@ const dictionary = {
|
|
|
23
22
|
collapse: 'Collapse options',
|
|
24
23
|
uploadSizeError: 'The following files were not added to the upload list because they\'re larger than $1:',
|
|
25
24
|
uploadItemLimitError: 'The following files were not added because no more than $1 items may be uploaded at a time:',
|
|
26
|
-
uploadError: 'An error occurred while uploading the file "$0".',
|
|
27
|
-
uploadDragging: 'Drop the file here to upload',
|
|
28
|
-
uploadDragDropTitle: 'Arraste e solte arquivos',
|
|
29
|
-
uploadDragDropDescription: 'Solte arquivos aqui para enviá-los na conversa',
|
|
30
25
|
cantSendBecauseOfUploadError: 'Can\'t send the message because one of the files in the upload list could not be uploaded. Please, retry it or remove it from the list.',
|
|
31
26
|
cantSendBecauseOfUploadProgress: 'Please wait until all files are uploaded before sending the message. You can also cancel the upload by removing it from the list of uploads.',
|
|
32
27
|
cantSendBecauseOfEmptyContent: 'You can\'t send empty messages. Please write some text or upload a file.',
|
|
@@ -40,7 +35,6 @@ const dictionary = {
|
|
|
40
35
|
agent: 'Selecionar agente',
|
|
41
36
|
send: 'Enviar mensagem',
|
|
42
37
|
placeholder: 'Envie uma mensagem para $0. Para comandos, use @ ou /',
|
|
43
|
-
typing: 'Digite sua mensagem...',
|
|
44
38
|
cancel: 'Cancelar',
|
|
45
39
|
removeConfig: 'Remover todas as configurações',
|
|
46
40
|
removeStack: 'Parar de usar a stack atual',
|
|
@@ -55,9 +49,6 @@ const dictionary = {
|
|
|
55
49
|
uploadSizeError: 'Os seguintes arquivos não foram adicionados à lista de upload porque eles são maiores que $0:',
|
|
56
50
|
uploadItemLimitError: 'Os seguintes arquivos não foram adicionados à lista de upload porque é permitido enviar no máximo $0 arquivos por vez:',
|
|
57
51
|
uploadError: 'Ocorreu um erro ao enviar o arquivo "$0".',
|
|
58
|
-
uploadDragging: 'Solte o arquivo aqui para fazer upload',
|
|
59
|
-
uploadDragDropTitle: 'Solte seus arquivos aqui',
|
|
60
|
-
uploadDragDropDescription: 'Arraste ou solte arquivos para compartilhar na conversa',
|
|
61
52
|
unknownUploadError: 'Ocorreu um erro ao enviar os arquivos.',
|
|
62
53
|
cantSendBecauseOfUploadError: 'Não é possível enviar a mensagem, pois um dos arquivos na lista de uploads não pôde ser enviado. Por favor, tente enviá-lo novamente ou remova-o da lista.',
|
|
63
54
|
cantSendBecauseOfUploadProgress: 'Por favor aguarde todos os uploads de arquivos antes de enviar a mensagem. Você pode cancelar o upload removendo o arquivo da lista de uploads.',
|
|
@@ -8,7 +8,6 @@ import { quickCommandRegex } from '../../regex'
|
|
|
8
8
|
import { ChatEntry } from '../../state/ChatEntry'
|
|
9
9
|
import { checkIsTrial } from '../../utils/check-is-trial'
|
|
10
10
|
import { UploadProvider } from '../../utils/upload/context'
|
|
11
|
-
import { usePasteUpload } from '../../utils/upload/use-paste-upload'
|
|
12
11
|
import { AgentSelector } from './AgentSelector'
|
|
13
12
|
import { ButtonAgent } from './ButtonAgent'
|
|
14
13
|
import { ButtonBar } from './ButtonBar'
|
|
@@ -18,7 +17,6 @@ import { useMessageInputDictionary } from './dictionary'
|
|
|
18
17
|
import { QuickCommandSelector } from './QuickCommandSelector'
|
|
19
18
|
import { MAX_INPUT_HEIGHT, MessageInputBox, MIN_INPUT_HEIGHT } from './styled'
|
|
20
19
|
import { UploadBar } from './UploadBar'
|
|
21
|
-
import { UploadDragNDrop, useUploadDragDrop } from './UploadDragNDrop'
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
22
|
* This renders the MessageInput part of the layout which includes the progress bar, the actual textarea, the badges telling what is
|
|
@@ -38,13 +36,6 @@ export const MessageInput = () => {
|
|
|
38
36
|
const agentLabel = useCurrentChatState('agent')?.label
|
|
39
37
|
const { handleKeyDown, handleKeyUp } = useUserEntryHistoryShortcut()
|
|
40
38
|
const isTrial = checkIsTrial()
|
|
41
|
-
const { isDragging, handleDrop, handleDragLeave } = useUploadDragDrop()
|
|
42
|
-
|
|
43
|
-
usePasteUpload({
|
|
44
|
-
textAreaRef,
|
|
45
|
-
onUploadFiles: files => chat.uploadManager.add(files),
|
|
46
|
-
enabled: !isLoading,
|
|
47
|
-
})
|
|
48
39
|
|
|
49
40
|
const checkSendRequirements = useCallback(() => {
|
|
50
41
|
if (chat.uploadManager.status === 'error') {
|
|
@@ -121,11 +112,7 @@ export const MessageInput = () => {
|
|
|
121
112
|
<ButtonAgent />
|
|
122
113
|
<AdaptiveTextArea
|
|
123
114
|
ref={textAreaRef}
|
|
124
|
-
placeholder={
|
|
125
|
-
agentLabel
|
|
126
|
-
? interpolate(t.placeholder, agentLabel)
|
|
127
|
-
: t.typing
|
|
128
|
-
}
|
|
115
|
+
placeholder={agentLabel && interpolate(t.placeholder, agentLabel)}
|
|
129
116
|
onChange={e => chat.set('nextMessage', e.target.value)}
|
|
130
117
|
value={value}
|
|
131
118
|
onFocus={() => setFocused(true)}
|
|
@@ -144,7 +131,6 @@ export const MessageInput = () => {
|
|
|
144
131
|
{chat.get('features').upload && <UploadBar />}
|
|
145
132
|
<ButtonBar onSend={onSend} isLoading={isLoading} />
|
|
146
133
|
</MessageInputBox>
|
|
147
|
-
<UploadDragNDrop isDragging={isDragging} onDrop={handleDrop} onDragLeave={handleDragLeave} />
|
|
148
134
|
</UploadProvider>
|
|
149
135
|
)
|
|
150
136
|
}
|
|
@@ -6,7 +6,7 @@ const CONTEXT_BAR_HEIGHT = 38
|
|
|
6
6
|
const CONTEXT_BAR_DISPLACEMENT = 4
|
|
7
7
|
const UPLOAD_BAR_HEIGHT = 60
|
|
8
8
|
const UPLOAD_BAR_DISPLACEMENT = 4
|
|
9
|
-
export const MAX_INPUT_HEIGHT =
|
|
9
|
+
export const MAX_INPUT_HEIGHT = 200
|
|
10
10
|
export const MIN_INPUT_HEIGHT = 24
|
|
11
11
|
|
|
12
12
|
export const SelectionBarWrapper = styled.div`
|
package/src/views/Tools.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { agentClient } from '@stack-spot/portal-network'
|
|
2
2
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
3
|
import { useEffect, useMemo } from 'react'
|
|
4
4
|
import styled from 'styled-components'
|
|
@@ -40,9 +40,8 @@ const ToolsPanel = () => {
|
|
|
40
40
|
const chat = widget.chatTabs.getAll().find(c => c.id === chatId)
|
|
41
41
|
return chat?.getMessages().find(m => m.id === messageId)?.getValue()
|
|
42
42
|
}, [messageId])
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
const tools = useMemo(() => message?.tools?.map(id => toolById(id, toolKits) ?? { id }), [messageId, toolKits])
|
|
43
|
+
const [agent] = agentClient.agent.useStatefulQuery({ agentId: message?.agent?.id ?? '' }, { enabled: !!message?.agent?.id })
|
|
44
|
+
const tools = useMemo(() => message?.tools?.map(id => toolById(id, agent?.toolkits) ?? { id }), [messageId, agent])
|
|
46
45
|
|
|
47
46
|
return !!tools?.length && (
|
|
48
47
|
<ToolList>
|
|
@@ -60,7 +60,7 @@ export const WorkspaceResources = ({ workspaceId, allKS, agent, stack }: Omit<Ta
|
|
|
60
60
|
const handleNavigate = (resource: WorkspaceResource) => {
|
|
61
61
|
startTransition(() => {
|
|
62
62
|
if (resource.resourceType === 'agent')
|
|
63
|
-
navigate({ component: 'agent', props: { visibility: '
|
|
63
|
+
navigate({ component: 'agent', props: { visibility: 'WORKSPACE', agent, workspaceId, showSubmitButton }, fullScreen: true })
|
|
64
64
|
|
|
65
65
|
if (resource.resourceType === 'ks')
|
|
66
66
|
navigate({ component: 'ks', props: { visibility: 'workspace', allKS, workspaceId, showSubmitButton }, fullScreen: true })
|