@stack-spot/ai-chat-widget 0.3.0 → 0.5.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.
- package/dist/StackspotAIWidget.js +1 -1
- package/dist/StackspotAIWidget.js.map +1 -1
- package/dist/chat-interceptors/quick-commands.js +2 -2
- package/dist/chat-interceptors/quick-commands.js.map +1 -1
- package/dist/chat-interceptors/send-message.d.ts.map +1 -1
- package/dist/chat-interceptors/send-message.js +4 -3
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/Tooltip/TooltipAPI.d.ts +2 -0
- package/dist/components/Tooltip/TooltipAPI.d.ts.map +1 -1
- package/dist/components/Tooltip/TooltipAPI.js +24 -7
- package/dist/components/Tooltip/TooltipAPI.js.map +1 -1
- package/dist/components/Tooltip/context.js +1 -1
- package/dist/components/Tooltip/context.js.map +1 -1
- package/dist/layout.css +6 -0
- package/dist/state/ChatEntry.d.ts +3 -2
- package/dist/state/ChatEntry.d.ts.map +1 -1
- package/dist/state/ChatEntry.js +2 -2
- package/dist/state/ChatEntry.js.map +1 -1
- package/dist/state/ChatState.d.ts +1 -7
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js.map +1 -1
- package/dist/state/types.d.ts +8 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +2 -0
- package/dist/state/types.js.map +1 -0
- package/dist/views/Agents.d.ts.map +1 -1
- package/dist/views/Agents.js +19 -6
- package/dist/views/Agents.js.map +1 -1
- package/dist/views/Chat/AgentInfo.d.ts +3 -2
- package/dist/views/Chat/AgentInfo.d.ts.map +1 -1
- package/dist/views/Chat/AgentInfo.js +3 -3
- package/dist/views/Chat/AgentInfo.js.map +1 -1
- package/dist/views/Chat/ChatMessage.js +2 -2
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/styled.d.ts.map +1 -1
- package/dist/views/Chat/styled.js +8 -1
- package/dist/views/Chat/styled.js.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.js +15 -8
- package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
- package/dist/views/ChatHistory/utils.d.ts +1 -0
- package/dist/views/ChatHistory/utils.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.js +12 -1
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/package.json +1 -1
- package/src/StackspotAIWidget.tsx +1 -1
- package/src/chat-interceptors/quick-commands.ts +2 -2
- package/src/chat-interceptors/send-message.ts +4 -3
- package/src/components/Tooltip/TooltipAPI.ts +25 -7
- package/src/components/Tooltip/context.tsx +1 -1
- package/src/layout.css +6 -0
- package/src/state/ChatEntry.ts +5 -4
- package/src/state/ChatState.ts +1 -9
- package/src/state/types.ts +8 -0
- package/src/views/Agents.tsx +20 -7
- package/src/views/Chat/AgentInfo.tsx +8 -8
- package/src/views/Chat/ChatMessage.tsx +3 -3
- package/src/views/Chat/styled.ts +8 -1
- package/src/views/ChatHistory/HistoryItem.tsx +20 -8
- package/src/views/ChatHistory/utils.ts +12 -1
|
@@ -4,6 +4,7 @@ import { Check, Download, EllipsisHorizontal, Pencil, Trash } from '@citric/icon
|
|
|
4
4
|
import { IconButton, LoadingCircular } from '@citric/ui';
|
|
5
5
|
import { aiClient } from '@stack-spot/portal-network';
|
|
6
6
|
import { theme } from '@stack-spot/portal-theme';
|
|
7
|
+
import { last } from 'lodash';
|
|
7
8
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
8
9
|
import { OverlayMenu } from '../../components/OverlayMenu.js';
|
|
9
10
|
import { useWidget } from '../../context/hooks.js';
|
|
@@ -13,7 +14,7 @@ import { download } from '../../utils/download.js';
|
|
|
13
14
|
import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source.js';
|
|
14
15
|
import { useHistoryDictionary } from './dictionary.js';
|
|
15
16
|
import { HistoryItemBox } from './styled.js';
|
|
16
|
-
import { findStack, findWorkspace } from './utils.js';
|
|
17
|
+
import { findStack, findWorkspace, getAllAgents } from './utils.js';
|
|
17
18
|
export const HistoryItem = ({ item, interceptors }) => {
|
|
18
19
|
const t = useHistoryDictionary();
|
|
19
20
|
const [isLoading, setLoading] = useState(false);
|
|
@@ -31,17 +32,19 @@ export const HistoryItem = ({ item, interceptors }) => {
|
|
|
31
32
|
setRenaming(true);
|
|
32
33
|
}, []);
|
|
33
34
|
async function onSubmitRename() {
|
|
35
|
+
const prev = title;
|
|
34
36
|
setRenaming(false);
|
|
37
|
+
setTitle(renamed);
|
|
35
38
|
if (!renamed || renamed === item.title)
|
|
36
39
|
return;
|
|
37
40
|
try {
|
|
38
41
|
await aiClient.renameChat.mutate({ conversationId: item.id, conversationUpdateTitleRequest: { title: renamed } });
|
|
39
|
-
setTitle(renamed);
|
|
40
42
|
aiClient.chats.invalidate();
|
|
41
43
|
}
|
|
42
44
|
catch (error) {
|
|
43
45
|
// eslint-disable-next-line no-console
|
|
44
46
|
console.error(error);
|
|
47
|
+
setTitle(prev);
|
|
45
48
|
setRenaming(true);
|
|
46
49
|
}
|
|
47
50
|
}
|
|
@@ -76,18 +79,22 @@ export const HistoryItem = ({ item, interceptors }) => {
|
|
|
76
79
|
setLoading(true);
|
|
77
80
|
try {
|
|
78
81
|
const chat = await aiClient.chat.query({ conversationId: item.id });
|
|
79
|
-
const [stack, workspace] = await Promise.all([
|
|
82
|
+
const [stack, workspace, agents] = await Promise.all([
|
|
83
|
+
findStack(chat.ai_stack_id),
|
|
84
|
+
findWorkspace(chat.workspace_id),
|
|
85
|
+
getAllAgents(),
|
|
86
|
+
]);
|
|
80
87
|
widget.chatTabs.add(new ChatState({
|
|
81
88
|
id: chat.id,
|
|
82
|
-
initial: { label: chat.title, stack, workspace },
|
|
89
|
+
initial: { label: chat.title, stack, workspace, agent: agents.find(a => a.id === last(chat.history)?.custom_agent?.id) },
|
|
83
90
|
interceptors,
|
|
84
91
|
entries: chat.history?.map(item => new ChatEntry({
|
|
85
|
-
|
|
92
|
+
agentType: item.agent === 'USER' ? 'user' : 'bot',
|
|
86
93
|
content: item.content,
|
|
87
94
|
type: item.agent === 'USER' ? 'text' : 'md',
|
|
88
|
-
|
|
95
|
+
agent: agents.find(a => a.id === item.custom_agent?.id),
|
|
89
96
|
messageId: item.message_id,
|
|
90
|
-
knowledgeSources: genericSourcesToKnowledgeSources(item.sources),
|
|
97
|
+
knowledgeSources: item.agent === 'USER' ? undefined : genericSourcesToKnowledgeSources(item.sources),
|
|
91
98
|
updated: item.updated,
|
|
92
99
|
})),
|
|
93
100
|
}));
|
|
@@ -104,6 +111,6 @@ export const HistoryItem = ({ item, interceptors }) => {
|
|
|
104
111
|
{ label: t.download, onClick: onDownload, icon: _jsx(Download, {}) },
|
|
105
112
|
{ label: t.delete, onClick: onDelete, icon: _jsx(Trash, {}), color: theme.color.danger[500] },
|
|
106
113
|
];
|
|
107
|
-
return isDeleted ? null : (_jsx(HistoryItemBox, { className: isLoading ? 'loading' : '', children: isRenaming ? (_jsxs(_Fragment, { children: [_jsx(Input, { ref: renameInput, value: renamed, onChange: e => setRenamed(e.target.value) }), _jsx(IconButton, { onClick: onSubmitRename, children: _jsx(Check, {}) })] })) : (_jsxs(_Fragment, { children: [_jsx("button", { className: "label", onClick: onSelect, disabled: isLoading, children: title }), isLoading ? _jsx(LoadingCircular, { size: "xs" }) : _jsx(OverlayMenu, { actions: actions, position: "left", children: _jsx(IconBox, { children: _jsx(EllipsisHorizontal, {}) }) })] })) }));
|
|
114
|
+
return isDeleted ? null : (_jsx(HistoryItemBox, { className: isLoading ? 'loading' : '', children: isRenaming ? (_jsxs(_Fragment, { children: [_jsx(Input, { ref: renameInput, value: renamed, onChange: e => setRenamed(e.target.value), onKeyDown: e => e.key === 'Enter' && onSubmitRename() }), _jsx(IconButton, { onClick: onSubmitRename, children: _jsx(Check, {}) })] })) : (_jsxs(_Fragment, { children: [_jsx("button", { className: "label", onClick: onSelect, disabled: isLoading, children: title }), isLoading ? _jsx(LoadingCircular, { size: "xs" }) : _jsx(OverlayMenu, { actions: actions, position: "left", children: _jsx(IconBox, { children: _jsx(EllipsisHorizontal, {}) }) })] })) }));
|
|
108
115
|
};
|
|
109
116
|
//# sourceMappingURL=HistoryItem.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HistoryItem.js","sourceRoot":"","sources":["../../../src/views/ChatHistory/HistoryItem.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAErD,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAsB,MAAM,uBAAuB,CAAA;AAErE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAA;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"HistoryItem.js","sourceRoot":"","sources":["../../../src/views/ChatHistory/HistoryItem.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChatProperties } from '../../state/ChatState.js';
|
|
2
2
|
export declare function findStack(id: string | null): Promise<ChatProperties['stack'] | undefined>;
|
|
3
3
|
export declare function findWorkspace(id: string | null): Promise<ChatProperties['workspace'] | undefined>;
|
|
4
|
+
export declare function getAllAgents(): Promise<Required<ChatProperties>['agent'][]>;
|
|
4
5
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/views/ChatHistory/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAU/F;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,CAUvG"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/views/ChatHistory/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAU/F;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,CAUvG;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CASjF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { aiClient, workspaceClient } from '@stack-spot/portal-network';
|
|
1
|
+
import { agentClient, aiClient, workspaceClient } from '@stack-spot/portal-network';
|
|
2
2
|
export async function findStack(id) {
|
|
3
3
|
if (!id)
|
|
4
4
|
return;
|
|
@@ -25,4 +25,15 @@ export async function findWorkspace(id) {
|
|
|
25
25
|
return { id, label: id };
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
export async function getAllAgents() {
|
|
29
|
+
try {
|
|
30
|
+
const [agents, publicAgents] = await Promise.all([agentClient.agents.query({}), agentClient.publicAgents.query({})]);
|
|
31
|
+
return [...agents, ...publicAgents].map(a => ({ id: a.id, label: a.name, image: a.avatar }));
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.error(error);
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
28
39
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/views/ChatHistory/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/views/ChatHistory/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAGnF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAiB;IAC/C,IAAI,CAAC,EAAE;QAAE,OAAM;IACf,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,CAAA;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sCAAsC;QACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAiB;IACnD,IAAI,CAAC,EAAE;QAAE,OAAM;IACf,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAA;QACrE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sCAAsC;QACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,CAAC;QACH,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QACpH,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC9F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sCAAsC;QACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const useMessageInputDictionary: () => Record<"
|
|
1
|
+
export declare const useMessageInputDictionary: () => Record<"stack" | "workspace" | "agent" | "cancel" | "placeholder" | "send" | "collapse" | "knowledgeSource" | "expand" | "removeConfig" | "removeStack" | "removeWorkspace" | "removeKS", string>;
|
|
2
2
|
//# sourceMappingURL=dictionary.d.ts.map
|
package/package.json
CHANGED
|
@@ -63,7 +63,7 @@ export const StackspotAIWidget = (
|
|
|
63
63
|
<KSDocument />
|
|
64
64
|
<Agents />
|
|
65
65
|
<ChatHistory interceptors={interceptors} />
|
|
66
|
-
|
|
66
|
+
<div className="chat-right-panel" ref={rightPanelRef}><RightPanel /></div>
|
|
67
67
|
</div>
|
|
68
68
|
</RightPanelProvider>
|
|
69
69
|
</TooltipProvider>
|
|
@@ -3,8 +3,8 @@ import { ChatState } from '../state/ChatState'
|
|
|
3
3
|
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
5
|
export function quickCommandInterceptor(entry: ChatEntry, _chat: ChatState) {
|
|
6
|
-
const {
|
|
7
|
-
if (
|
|
6
|
+
const { agentType, content } = entry.getValue()
|
|
7
|
+
if (agentType !== 'user') return
|
|
8
8
|
if (content.startsWith('/')) {
|
|
9
9
|
alert('Quick commands are not yet supported!') /* todo */
|
|
10
10
|
return false // by returning false, the next interceptor are not run. i.e. the message is not sent to the AI agent.
|
|
@@ -5,8 +5,8 @@ import { buildConversationContext } from '../utils/chat'
|
|
|
5
5
|
import { genericSourcesToKnowledgeSources } from '../utils/knowledge-source'
|
|
6
6
|
|
|
7
7
|
export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState) {
|
|
8
|
-
const {
|
|
9
|
-
if (
|
|
8
|
+
const { agentType, content } = entry.getValue()
|
|
9
|
+
if (agentType !== 'user') return
|
|
10
10
|
const context = buildConversationContext(chat)
|
|
11
11
|
chat.set('isLoading', true)
|
|
12
12
|
// if this is the first message, let's rename the chat.
|
|
@@ -22,12 +22,13 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState)
|
|
|
22
22
|
knowledgeSources = genericSourcesToKnowledgeSources(value.sources)
|
|
23
23
|
}
|
|
24
24
|
botEntry.setValue({
|
|
25
|
-
|
|
25
|
+
agentType: 'bot',
|
|
26
26
|
type: 'md',
|
|
27
27
|
content: value.answer ?? '',
|
|
28
28
|
messageId: value.message_id ?? undefined,
|
|
29
29
|
knowledgeSources,
|
|
30
30
|
updated: new Date().toISOString(),
|
|
31
|
+
agent: chat.get('agent'),
|
|
31
32
|
})
|
|
32
33
|
})
|
|
33
34
|
try {
|
|
@@ -3,17 +3,30 @@ import { ShowOptions } from './types'
|
|
|
3
3
|
|
|
4
4
|
const MARGIN_TO_CORNERS_PX = 10
|
|
5
5
|
|
|
6
|
+
function isRelative(element: HTMLElement) {
|
|
7
|
+
return ['relative', 'absolute', 'fixed'].includes(element.computedStyleMap().get('position')?.toString() ?? '')
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
export class TooltipAPI {
|
|
7
11
|
private tooltipRef: React.RefObject<HTMLDivElement>
|
|
8
12
|
private setContent: React.Dispatch<React.SetStateAction<React.ReactNode>>
|
|
9
13
|
private hideTimeoutId: number | undefined
|
|
10
14
|
private clickListener: ((e: MouseEvent) => void) | undefined
|
|
15
|
+
private relativeTo: HTMLElement | undefined
|
|
11
16
|
|
|
12
17
|
constructor(tooltipRef: React.RefObject<HTMLDivElement>, setContent: React.Dispatch<React.SetStateAction<React.ReactNode>>) {
|
|
13
18
|
this.tooltipRef = tooltipRef
|
|
14
19
|
this.setContent = setContent
|
|
15
20
|
}
|
|
16
21
|
|
|
22
|
+
private computeRelativeTo() {
|
|
23
|
+
if (this.relativeTo) return
|
|
24
|
+
this.relativeTo = this.tooltipRef.current?.parentElement as HTMLElement
|
|
25
|
+
while (this.relativeTo && this.relativeTo !== document.body && !isRelative(this.relativeTo)) {
|
|
26
|
+
this.relativeTo = this.relativeTo.parentElement as HTMLElement
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
show({ content, anchor, position = 'bottom', hideOnClickOutside }: ShowOptions): void {
|
|
18
31
|
window.clearTimeout(this.hideTimeoutId)
|
|
19
32
|
this.hideTimeoutId = undefined
|
|
@@ -21,21 +34,26 @@ export class TooltipAPI {
|
|
|
21
34
|
this.setContent(content)
|
|
22
35
|
setTimeout(() => {
|
|
23
36
|
if (!this.tooltipRef.current) return
|
|
24
|
-
const
|
|
37
|
+
const anchorRect = anchor.getClientRects()[0]
|
|
25
38
|
this.tooltipRef.current.classList.add('visible')
|
|
26
39
|
const tooltipWidth = this.tooltipRef.current.clientWidth
|
|
27
40
|
const tooltipHeight = this.tooltipRef.current.clientHeight
|
|
28
41
|
let top = 0
|
|
29
42
|
let left = 0
|
|
30
43
|
if (position === 'left' || position === 'right') {
|
|
31
|
-
top =
|
|
32
|
-
if (position === 'left') left =
|
|
33
|
-
else left =
|
|
44
|
+
top = anchorRect.top + anchorRect.height / 2 - tooltipHeight / 2
|
|
45
|
+
if (position === 'left') left = anchorRect.left - tooltipWidth
|
|
46
|
+
else left = anchorRect.left + anchorRect.width
|
|
34
47
|
} else {
|
|
35
|
-
left =
|
|
36
|
-
if (position === 'top') top =
|
|
37
|
-
else top =
|
|
48
|
+
left = anchorRect.left + anchorRect.width / 2 - tooltipWidth / 2
|
|
49
|
+
if (position === 'top') top = anchorRect.top - tooltipHeight
|
|
50
|
+
else top = anchorRect.top + anchorRect.height
|
|
38
51
|
}
|
|
52
|
+
// takes the parent the tooltip is positioned relative to into consideration
|
|
53
|
+
this.computeRelativeTo()
|
|
54
|
+
const relativeRect = this.relativeTo?.getClientRects()[0] ?? { top: 0, left: 0 }
|
|
55
|
+
top -= relativeRect.top
|
|
56
|
+
left -= relativeRect.left
|
|
39
57
|
// adjusts positions in order to avoid overflowing the window and leaving a margin to the corners
|
|
40
58
|
if (top <= 0) top += MARGIN_TO_CORNERS_PX
|
|
41
59
|
else if (top + tooltipHeight >= document.body.clientHeight - MARGIN_TO_CORNERS_PX) {
|
|
@@ -12,7 +12,7 @@ export const TooltipProvider = ({ children }: Required<WithChildren>) => {
|
|
|
12
12
|
return (
|
|
13
13
|
<Context.Provider value={api}>
|
|
14
14
|
{children}
|
|
15
|
-
<TooltipBox ref={ref}
|
|
15
|
+
<TooltipBox ref={ref}>{content}</TooltipBox>
|
|
16
16
|
</Context.Provider>
|
|
17
17
|
)
|
|
18
18
|
}
|
package/src/layout.css
CHANGED
package/src/state/ChatEntry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { pull } from 'lodash'
|
|
2
|
+
import { LabeledWithImage } from './types'
|
|
2
3
|
|
|
3
4
|
export interface SerializableAction {
|
|
4
5
|
title: string,
|
|
@@ -25,14 +26,14 @@ export interface KnowledgeSource {
|
|
|
25
26
|
|
|
26
27
|
export interface TextChatEntry {
|
|
27
28
|
type: 'text' | 'md',
|
|
28
|
-
|
|
29
|
+
agentType: 'bot' | 'user' | 'system',
|
|
29
30
|
// image?: string,
|
|
30
31
|
actions?: ChatAction[],
|
|
31
32
|
subtitle?: string,
|
|
32
33
|
content: string,
|
|
33
34
|
knowledgeSources?: KnowledgeSource[],
|
|
34
35
|
updated?: string,
|
|
35
|
-
|
|
36
|
+
agent?: LabeledWithImage,
|
|
36
37
|
messageId?: string,
|
|
37
38
|
error?: string,
|
|
38
39
|
// customInput?: CustomInputResponse,
|
|
@@ -63,7 +64,7 @@ export class ChatEntry {
|
|
|
63
64
|
|
|
64
65
|
static createUserEntry(content: string) {
|
|
65
66
|
return new ChatEntry({
|
|
66
|
-
|
|
67
|
+
agentType: 'user',
|
|
67
68
|
type: 'text',
|
|
68
69
|
content,
|
|
69
70
|
updated: new Date().toISOString(),
|
|
@@ -71,7 +72,7 @@ export class ChatEntry {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
static createStreamedBotEntry(abort: () => void) {
|
|
74
|
-
return new ChatEntry({
|
|
75
|
+
return new ChatEntry({ agentType: 'bot', type: 'md', content: '' }, true, abort)
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
setValue(value: TextChatEntry) {
|
package/src/state/ChatState.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { dropRight, last, pull } from 'lodash'
|
|
2
2
|
import { ChatEntry } from './ChatEntry'
|
|
3
3
|
import { ObservableState } from './ObservableState'
|
|
4
|
-
|
|
5
|
-
interface Labeled {
|
|
6
|
-
id: string,
|
|
7
|
-
label: string,
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface LabeledWithImage extends Labeled {
|
|
11
|
-
image?: string,
|
|
12
|
-
}
|
|
4
|
+
import { Labeled, LabeledWithImage } from './types'
|
|
13
5
|
|
|
14
6
|
export interface ChatProperties {
|
|
15
7
|
label: string,
|
package/src/views/Agents.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { Button, Text } from '@citric/core'
|
|
|
2
2
|
import { Search } from '@citric/icons'
|
|
3
3
|
import { Badge } from '@citric/ui'
|
|
4
4
|
import { Placeholder } from '@stack-spot/portal-components/Placeholder'
|
|
5
|
+
import { MiniLogo } from '@stack-spot/portal-components/svg'
|
|
5
6
|
import { agentClient } from '@stack-spot/portal-network'
|
|
6
7
|
import { AgentResponse, VisibilityLevel } from '@stack-spot/portal-network/api/agent'
|
|
7
8
|
import { theme } from '@stack-spot/portal-theme'
|
|
@@ -20,7 +21,7 @@ const AgentLabel = styled.div`
|
|
|
20
21
|
align-items: center;
|
|
21
22
|
gap: 6px;
|
|
22
23
|
|
|
23
|
-
img {
|
|
24
|
+
img, svg {
|
|
24
25
|
width: 20px;
|
|
25
26
|
height: 20px;
|
|
26
27
|
border-radius: 50%;
|
|
@@ -86,8 +87,8 @@ const AgentsPanel = () => {
|
|
|
86
87
|
const chat = useCurrentChat()
|
|
87
88
|
|
|
88
89
|
return <RightPanelTabs key={chat.id} tabs={[
|
|
89
|
-
{ title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
|
|
90
90
|
{ title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" /> },
|
|
91
|
+
{ title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
|
|
91
92
|
{ title: t.shared, content: <AgentsTab key="shared" visibility="SHARED" /> },
|
|
92
93
|
{ title: t.account, content: <AgentsTab key="account" visibility="ACCOUNT" /> },
|
|
93
94
|
]} />
|
|
@@ -98,15 +99,23 @@ const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT-IN' })
|
|
|
98
99
|
const { close } = useRightPanel()
|
|
99
100
|
const chat = useCurrentChat()
|
|
100
101
|
const [filter, setFilter] = useState('')
|
|
102
|
+
const defaultAgent = useMemo(() => ({
|
|
103
|
+
name: t.defaultAgentName,
|
|
104
|
+
description: t.defaultAgentDescription,
|
|
105
|
+
llm_config: { model_slug: 'gpt4o' },
|
|
106
|
+
} as AgentResponse), [])
|
|
101
107
|
const agents = visibility === 'BUILT-IN' ? agentClient.publicAgents.useQuery({}) : agentClient.agents.useQuery({ visibility })
|
|
102
|
-
const [value, setValue] = useState<AgentResponse | undefined>(agents.find(a => a.id === chat.get('agent')?.id))
|
|
108
|
+
const [value, setValue] = useState<AgentResponse | undefined>(agents.find(a => a.id === chat.get('agent')?.id) ?? defaultAgent)
|
|
103
109
|
const filtered = useMemo(
|
|
104
|
-
() =>
|
|
110
|
+
() => {
|
|
111
|
+
const ags = visibility === 'BUILT-IN' ? [defaultAgent as AgentResponse, ...agents] : agents
|
|
112
|
+
return filter ? ags.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : ags
|
|
113
|
+
},
|
|
105
114
|
[agents, filter, value],
|
|
106
115
|
)
|
|
107
116
|
|
|
108
117
|
function submit() {
|
|
109
|
-
if (value) chat.set('agent', { id: value.id, label: value.name, image: value.avatar })
|
|
118
|
+
if (value) chat.set('agent', value.id ? { id: value.id, label: value.name, image: value.avatar } : undefined)
|
|
110
119
|
close()
|
|
111
120
|
}
|
|
112
121
|
|
|
@@ -119,9 +128,9 @@ const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT-IN' })
|
|
|
119
128
|
keygen={a => a.id}
|
|
120
129
|
value={value}
|
|
121
130
|
onChange={setValue}
|
|
122
|
-
renderLabel={({ name, avatar }) => (
|
|
131
|
+
renderLabel={({ name, avatar, id }) => (
|
|
123
132
|
<AgentLabel>
|
|
124
|
-
{avatar && <img src={avatar} />}
|
|
133
|
+
{id ? (avatar && <img src={avatar} />) : <MiniLogo />}
|
|
125
134
|
<Text>{name}</Text>
|
|
126
135
|
</AgentLabel>
|
|
127
136
|
)}
|
|
@@ -170,6 +179,8 @@ const dictionary = {
|
|
|
170
179
|
noSearchResultsDescription: 'Please, try another search term.',
|
|
171
180
|
noData: 'There are no agents in this category yet.',
|
|
172
181
|
noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new agents.',
|
|
182
|
+
defaultAgentName: 'Stackspot AI (default)',
|
|
183
|
+
defaultAgentDescription: 'The StackSpot CodeGen is an advanced artificial intelligence agent designed to optimize and accelerate software development. Integrated directly into your integrated development environment, StackSpot CodeGen offers real-time code suggestions, helping developers write high-quality code more efficiently. With robust features such as creating Stacks AI, customized knowledge sources, and quick commands, StackSpot CodeGen contextualizes your development needs to provide the best answers and code suggestions.',
|
|
173
184
|
},
|
|
174
185
|
pt: {
|
|
175
186
|
title: 'Agentes',
|
|
@@ -183,5 +194,7 @@ const dictionary = {
|
|
|
183
194
|
noSearchResultsDescription: 'Por favor, tente outra busca.',
|
|
184
195
|
noData: 'Ainda não há agentes nesta categoria.',
|
|
185
196
|
noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos agentes.',
|
|
197
|
+
defaultAgentName: 'Stackspot AI (padrão)',
|
|
198
|
+
defaultAgentDescription: 'O StackSpot CodeGen é um agente de inteligência artificial avançado projetado para otimizar e acelerar o desenvolvimento de software. Integrado diretamente ao seu ambiente de desenvolvimento, o StackSpot CodeGen oferece sugestões de código em tempo real, ajudando os desenvolvedores a escreverem código de alta qualidade de forma mais eficiente. Com recursos robustos, como a criação de Stacks AI, Knowledge Sources personalizadas e comandos rápidos, o StackSpot CodeGen contextualiza suas necessidades de desenvolvimento para fornecer as melhores respostas e sugestões de código.',
|
|
186
199
|
},
|
|
187
200
|
} satisfies Dictionary
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { Text } from '@citric/core'
|
|
2
2
|
import { MiniLogo } from '@stack-spot/portal-components/svg'
|
|
3
|
+
import { LabeledWithImage } from '../../state/types'
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
|
-
|
|
6
|
+
agent?: LabeledWithImage,
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
10
|
-
export const AgentInfo = ({ agentId: _agentId }: Props) => (
|
|
9
|
+
export const AgentInfo = ({ agent }: Props) => (
|
|
11
10
|
<>
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
{agent?.image
|
|
12
|
+
? <img src={agent.image} className="custom-agent-image" />
|
|
13
|
+
: <div className="default-image-wrapper"><MiniLogo className="agent-image" /></div>
|
|
14
|
+
}
|
|
15
|
+
<Text appearance="body2">{agent?.label || 'Stackspot AI'}</Text>
|
|
16
16
|
</>
|
|
17
17
|
)
|
|
@@ -16,7 +16,7 @@ export const ChatMessage = ({ message, username }: { message: ChatEntry, usernam
|
|
|
16
16
|
const [liked, setLiked] = useState<boolean | undefined>()
|
|
17
17
|
const entry = useChatEntry(message)
|
|
18
18
|
const dateFormatter = useDateFormatter()
|
|
19
|
-
const userInfo = entry.
|
|
19
|
+
const userInfo = entry.agentType === 'user' ? <Avatar size="xs">{username}</Avatar> : <AgentInfo agent={entry.agent} />
|
|
20
20
|
const date = new Date(entry.updated ?? '')
|
|
21
21
|
const shouldShowDate = entry.updated && !isNaN(date.getTime())
|
|
22
22
|
const ref = useRef<HTMLLIElement>(null)
|
|
@@ -55,7 +55,7 @@ export const ChatMessage = ({ message, username }: { message: ChatEntry, usernam
|
|
|
55
55
|
}, [entry.messageId, liked])
|
|
56
56
|
|
|
57
57
|
return (entry.content || entry.error) && (
|
|
58
|
-
<li className={entry.
|
|
58
|
+
<li className={entry.agentType} ref={ref}>
|
|
59
59
|
<div className="chat-message">
|
|
60
60
|
<div className="user-info">{userInfo}</div>
|
|
61
61
|
{entry.content && <div className="message-content">
|
|
@@ -77,7 +77,7 @@ export const ChatMessage = ({ message, username }: { message: ChatEntry, usernam
|
|
|
77
77
|
))}</ul>
|
|
78
78
|
</div>}
|
|
79
79
|
<div className="message-footer">
|
|
80
|
-
{entry.
|
|
80
|
+
{entry.agentType === 'bot' && entry.messageId && !entry.error && <div className="message-actions">
|
|
81
81
|
<IconButton title={t.like} aria-label={t.like} onClick={like}>
|
|
82
82
|
{liked === true ? <LikeFill /> : <Like />}
|
|
83
83
|
</IconButton>
|
package/src/views/Chat/styled.ts
CHANGED
|
@@ -76,7 +76,7 @@ export const ChatList = styled.ul`
|
|
|
76
76
|
flex-direction: column;
|
|
77
77
|
gap: 4px;
|
|
78
78
|
|
|
79
|
-
.
|
|
79
|
+
.default-image-wrapper {
|
|
80
80
|
width: 24px;
|
|
81
81
|
height: 24px;
|
|
82
82
|
border-radius: 4px;
|
|
@@ -90,6 +90,12 @@ export const ChatList = styled.ul`
|
|
|
90
90
|
height: 18px;
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
.custom-agent-image {
|
|
95
|
+
width: 24px;
|
|
96
|
+
height: 24px;
|
|
97
|
+
border-radius: 50%;
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -100,6 +106,7 @@ export const ChatList = styled.ul`
|
|
|
100
106
|
display: flex;
|
|
101
107
|
flex-direction: row;
|
|
102
108
|
gap: 8px;
|
|
109
|
+
align-items: center;
|
|
103
110
|
|
|
104
111
|
.message-content {
|
|
105
112
|
padding: 10px;
|
|
@@ -4,6 +4,7 @@ import { IconButton, LoadingCircular } from '@citric/ui'
|
|
|
4
4
|
import { aiClient } from '@stack-spot/portal-network'
|
|
5
5
|
import { ConversationResponse } from '@stack-spot/portal-network/api/ai'
|
|
6
6
|
import { theme } from '@stack-spot/portal-theme'
|
|
7
|
+
import { last } from 'lodash'
|
|
7
8
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
8
9
|
import { OverlayMenu } from '../../components/OverlayMenu'
|
|
9
10
|
import { useWidget } from '../../context/hooks'
|
|
@@ -14,7 +15,7 @@ import { download } from '../../utils/download'
|
|
|
14
15
|
import { genericSourcesToKnowledgeSources } from '../../utils/knowledge-source'
|
|
15
16
|
import { useHistoryDictionary } from './dictionary'
|
|
16
17
|
import { HistoryItemBox } from './styled'
|
|
17
|
-
import { findStack, findWorkspace } from './utils'
|
|
18
|
+
import { findStack, findWorkspace, getAllAgents } from './utils'
|
|
18
19
|
|
|
19
20
|
export const HistoryItem = ({ item, interceptors }: { item: ConversationResponse, interceptors: MessageInterceptor[] }) => {
|
|
20
21
|
const t = useHistoryDictionary()
|
|
@@ -35,15 +36,17 @@ export const HistoryItem = ({ item, interceptors }: { item: ConversationResponse
|
|
|
35
36
|
}, [])
|
|
36
37
|
|
|
37
38
|
async function onSubmitRename() {
|
|
39
|
+
const prev = title
|
|
38
40
|
setRenaming(false)
|
|
41
|
+
setTitle(renamed)
|
|
39
42
|
if (!renamed || renamed === item.title) return
|
|
40
43
|
try {
|
|
41
44
|
await aiClient.renameChat.mutate({ conversationId: item.id, conversationUpdateTitleRequest: { title: renamed } })
|
|
42
|
-
setTitle(renamed)
|
|
43
45
|
aiClient.chats.invalidate()
|
|
44
46
|
} catch (error) {
|
|
45
47
|
// eslint-disable-next-line no-console
|
|
46
48
|
console.error(error)
|
|
49
|
+
setTitle(prev)
|
|
47
50
|
setRenaming(true)
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -78,18 +81,22 @@ export const HistoryItem = ({ item, interceptors }: { item: ConversationResponse
|
|
|
78
81
|
setLoading(true)
|
|
79
82
|
try {
|
|
80
83
|
const chat = await aiClient.chat.query({ conversationId: item.id })
|
|
81
|
-
const [stack, workspace] = await Promise.all([
|
|
84
|
+
const [stack, workspace, agents] = await Promise.all([
|
|
85
|
+
findStack(chat.ai_stack_id),
|
|
86
|
+
findWorkspace(chat.workspace_id),
|
|
87
|
+
getAllAgents(),
|
|
88
|
+
])
|
|
82
89
|
widget.chatTabs.add(new ChatState({
|
|
83
90
|
id: chat.id,
|
|
84
|
-
initial: { label: chat.title, stack, workspace },
|
|
91
|
+
initial: { label: chat.title, stack, workspace, agent: agents.find(a => a.id === last(chat.history)?.custom_agent?.id) },
|
|
85
92
|
interceptors,
|
|
86
93
|
entries: chat.history?.map(item => new ChatEntry({
|
|
87
|
-
|
|
94
|
+
agentType: item.agent === 'USER' ? 'user' : 'bot',
|
|
88
95
|
content: item.content,
|
|
89
96
|
type: item.agent === 'USER' ? 'text' : 'md',
|
|
90
|
-
|
|
97
|
+
agent: agents.find(a => a.id === item.custom_agent?.id),
|
|
91
98
|
messageId: item.message_id,
|
|
92
|
-
knowledgeSources: genericSourcesToKnowledgeSources(item.sources),
|
|
99
|
+
knowledgeSources: item.agent === 'USER' ? undefined : genericSourcesToKnowledgeSources(item.sources),
|
|
93
100
|
updated: item.updated,
|
|
94
101
|
})),
|
|
95
102
|
}))
|
|
@@ -111,7 +118,12 @@ export const HistoryItem = ({ item, interceptors }: { item: ConversationResponse
|
|
|
111
118
|
<HistoryItemBox className={isLoading ? 'loading' : ''}>
|
|
112
119
|
{isRenaming ? (
|
|
113
120
|
<>
|
|
114
|
-
<Input
|
|
121
|
+
<Input
|
|
122
|
+
ref={renameInput}
|
|
123
|
+
value={renamed}
|
|
124
|
+
onChange={e => setRenamed(e.target.value)}
|
|
125
|
+
onKeyDown={e => e.key === 'Enter' && onSubmitRename()}
|
|
126
|
+
/>
|
|
115
127
|
<IconButton onClick={onSubmitRename}><Check /></IconButton>
|
|
116
128
|
</>
|
|
117
129
|
) : (
|
|
@@ -1,4 +1,4 @@
|
|
|
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
3
|
|
|
4
4
|
export async function findStack(id: string | null): Promise<ChatProperties['stack'] | undefined> {
|
|
@@ -24,3 +24,14 @@ export async function findWorkspace(id: string | null): Promise<ChatProperties['
|
|
|
24
24
|
return { id, label: id }
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
export async function getAllAgents(): Promise<Required<ChatProperties>['agent'][]> {
|
|
29
|
+
try {
|
|
30
|
+
const [agents, publicAgents] = await Promise.all([agentClient.agents.query({}), agentClient.publicAgents.query({})])
|
|
31
|
+
return [...agents, ...publicAgents].map(a => ({ id: a.id, label: a.name, image: a.avatar }))
|
|
32
|
+
} catch (error) {
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.error(error)
|
|
35
|
+
return []
|
|
36
|
+
}
|
|
37
|
+
}
|