@stack-spot/ai-chat-widget 0.1.0 → 0.2.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.d.ts.map +1 -1
- package/dist/StackspotAIWidget.js +3 -1
- package/dist/StackspotAIWidget.js.map +1 -1
- package/dist/chat-interceptors/send-message.d.ts.map +1 -1
- package/dist/chat-interceptors/send-message.js +19 -7
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/RightPanelTabs.d.ts.map +1 -1
- package/dist/components/RightPanelTabs.js +1 -0
- package/dist/components/RightPanelTabs.js.map +1 -1
- package/dist/components/form/styled.d.ts.map +1 -1
- package/dist/components/form/styled.js +2 -1
- package/dist/components/form/styled.js.map +1 -1
- package/dist/context/hooks.d.ts.map +1 -1
- package/dist/context/hooks.js +1 -5
- package/dist/context/hooks.js.map +1 -1
- package/dist/features.d.ts.map +1 -1
- package/dist/features.js +1 -0
- package/dist/features.js.map +1 -1
- package/dist/right-panel/DefaultPanel.d.ts +2 -2
- package/dist/right-panel/DefaultPanel.d.ts.map +1 -1
- package/dist/right-panel/DefaultPanel.js +2 -1
- package/dist/right-panel/DefaultPanel.js.map +1 -1
- package/dist/right-panel/hooks.d.ts +2 -2
- package/dist/right-panel/hooks.d.ts.map +1 -1
- package/dist/state/ChatEntry.d.ts +7 -0
- package/dist/state/ChatEntry.d.ts.map +1 -1
- package/dist/state/ChatEntry.js +0 -3
- package/dist/state/ChatEntry.js.map +1 -1
- package/dist/state/ChatState.d.ts +4 -1
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js.map +1 -1
- package/dist/state/WidgetState.d.ts +19 -8
- package/dist/state/WidgetState.d.ts.map +1 -1
- package/dist/state/WidgetState.js +0 -19
- package/dist/state/WidgetState.js.map +1 -1
- package/dist/utils/chat.js +1 -1
- package/dist/utils/chat.js.map +1 -1
- package/dist/utils/knowledge-source.d.ts +7 -0
- package/dist/utils/knowledge-source.d.ts.map +1 -0
- package/dist/utils/knowledge-source.js +38 -0
- package/dist/utils/knowledge-source.js.map +1 -0
- package/dist/views/Agents.d.ts.map +1 -1
- package/dist/views/Agents.js +129 -1
- package/dist/views/Agents.js.map +1 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +9 -4
- 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 +24 -0
- package/dist/views/Chat/styled.js.map +1 -1
- package/dist/views/KSDocument.d.ts +2 -0
- package/dist/views/KSDocument.d.ts.map +1 -0
- package/dist/views/KSDocument.js +40 -0
- package/dist/views/KSDocument.js.map +1 -0
- package/dist/views/KnowledgeSources.js +7 -5
- package/dist/views/KnowledgeSources.js.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.js +5 -3
- package/dist/views/MessageInput/ButtonGroup.js.map +1 -1
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/dist/views/MessageInput/index.d.ts.map +1 -1
- package/dist/views/MessageInput/index.js +2 -4
- package/dist/views/MessageInput/index.js.map +1 -1
- package/dist/views/MessageInput/styled.d.ts +2 -0
- package/dist/views/MessageInput/styled.d.ts.map +1 -1
- package/dist/views/MessageInput/styled.js +11 -3
- package/dist/views/MessageInput/styled.js.map +1 -1
- package/dist/views/Stacks.js +7 -5
- package/dist/views/Stacks.js.map +1 -1
- package/dist/views/Workspaces.js +7 -5
- package/dist/views/Workspaces.js.map +1 -1
- package/package.json +2 -2
- package/src/StackspotAIWidget.tsx +4 -0
- package/src/chat-interceptors/send-message.ts +20 -8
- package/src/components/RightPanelTabs.tsx +1 -0
- package/src/components/form/styled.ts +2 -1
- package/src/context/hooks.ts +1 -4
- package/src/features.ts +1 -0
- package/src/right-panel/DefaultPanel.tsx +5 -4
- package/src/right-panel/hooks.tsx +2 -2
- package/src/state/ChatEntry.ts +8 -3
- package/src/state/ChatState.ts +5 -1
- package/src/state/WidgetState.ts +14 -26
- package/src/utils/chat.ts +1 -1
- package/src/utils/knowledge-source.ts +43 -0
- package/src/views/Agents.tsx +186 -1
- package/src/views/Chat/ChatMessage.tsx +18 -4
- package/src/views/Chat/styled.ts +24 -0
- package/src/views/KSDocument.tsx +58 -0
- package/src/views/KnowledgeSources.tsx +8 -5
- package/src/views/MessageInput/ButtonGroup.tsx +9 -7
- package/src/views/MessageInput/index.tsx +2 -5
- package/src/views/MessageInput/styled.ts +11 -3
- package/src/views/Stacks.tsx +8 -5
- package/src/views/Workspaces.tsx +8 -5
package/dist/views/Workspaces.js
CHANGED
|
@@ -12,13 +12,13 @@ import { useCurrentChat, useWidget, useWidgetState } from '../context/hooks.js';
|
|
|
12
12
|
import { useRightPanel } from '../right-panel/hooks.js';
|
|
13
13
|
export const Workspaces = () => {
|
|
14
14
|
const t = useTranslate(dictionary);
|
|
15
|
-
const
|
|
15
|
+
const panel = useWidgetState('panel');
|
|
16
16
|
const { open } = useRightPanel();
|
|
17
17
|
const widget = useWidget();
|
|
18
18
|
useEffect(() => {
|
|
19
|
-
if (
|
|
20
|
-
open(_jsx(RightPanelForm, { children: _jsx(WorkspacesPanel, {}) }), { title: t.title, description: t.description, onClose: () => widget.set('
|
|
21
|
-
}, [
|
|
19
|
+
if (panel === 'workspace')
|
|
20
|
+
open(_jsx(RightPanelForm, { children: _jsx(WorkspacesPanel, {}) }), { title: t.title, description: t.description, onClose: () => widget.set('panel', undefined) });
|
|
21
|
+
}, [panel, t]);
|
|
22
22
|
return null;
|
|
23
23
|
};
|
|
24
24
|
const WorkspacesPanel = () => {
|
|
@@ -34,7 +34,9 @@ const WorkspacesPanel = () => {
|
|
|
34
34
|
chat.set('workspace', { id: value.id, label: value.name });
|
|
35
35
|
close();
|
|
36
36
|
}
|
|
37
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "content", children: [_jsx(IconInput, { icon: _jsx(Search, {}), value: filter, onChange: setFilter, className: "search" }), !!filtered.length && _jsx(DescribedRadioGroup, { options: filtered, keygen: w => w.id, value: value, onChange: setValue, renderLabel: w => w.name, renderDescription: w => w.description, optionClassName: w => (w === value && filter && !w.name.
|
|
37
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "content", children: [_jsx(IconInput, { icon: _jsx(Search, {}), value: filter, onChange: setFilter, className: "search" }), !!filtered.length && _jsx(DescribedRadioGroup, { options: filtered, keygen: w => w.id, value: value, onChange: setValue, renderLabel: w => w.name, renderDescription: w => w.description, optionClassName: w => (w === value && filter && !w.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
|
|
38
|
+
? 'filtered-out'
|
|
39
|
+
: '', className: "option-list" }), !!workspaces.length && !filtered.length && _jsx(Placeholder, { title: t.noSearchResults, description: t.noSearchResultsDescription }), !workspaces.length && _jsx(Placeholder, { title: t.noData, description: t.noDataDescription })] }), _jsx(Button, { onClick: submit, disabled: !value, children: t.apply })] }));
|
|
38
40
|
};
|
|
39
41
|
const dictionary = {
|
|
40
42
|
en: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Workspaces.js","sourceRoot":"","sources":["../../src/views/Workspaces.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAE5D,OAAO,EAAc,YAAY,EAAE,MAAM,8BAA8B,CAAA;AACvE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,EAAE;IAC7B,MAAM,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAAA;IAClC,MAAM,
|
|
1
|
+
{"version":3,"file":"Workspaces.js","sourceRoot":"","sources":["../../src/views/Workspaces.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stack-spot/ai-chat-widget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"@citric/icons": "^5.4.0 || ^6.0.0",
|
|
10
10
|
"@citric/ui": "^5.4.0 || ^6.0.0",
|
|
11
11
|
"@stack-spot/portal-components": "^2.6.1",
|
|
12
|
-
"@stack-spot/portal-network": "^0.
|
|
12
|
+
"@stack-spot/portal-network": "^0.41.0",
|
|
13
13
|
"@stack-spot/portal-theme": "^1.0.0",
|
|
14
14
|
"@stack-spot/portal-translate": "^1.1.0",
|
|
15
15
|
"lodash": "^4.17.0",
|
|
@@ -10,10 +10,12 @@ import { RightPanel } from './right-panel/RightPanel'
|
|
|
10
10
|
import { RightPanelProvider } from './right-panel/RightPanelProvider'
|
|
11
11
|
import { MessageInterceptor } from './state/ChatState'
|
|
12
12
|
import { MinimizedActions } from './types'
|
|
13
|
+
import { Agents } from './views/Agents'
|
|
13
14
|
import { Chat } from './views/Chat'
|
|
14
15
|
import { ChatTabSelection } from './views/ChatTabSelection'
|
|
15
16
|
import { Home } from './views/Home'
|
|
16
17
|
import { KnowledgeSources } from './views/KnowledgeSources'
|
|
18
|
+
import { KSDocument } from './views/KSDocument'
|
|
17
19
|
import { MessageInput } from './views/MessageInput'
|
|
18
20
|
import { MinimizedHeader } from './views/MinimizedHeader'
|
|
19
21
|
import { Stacks } from './views/Stacks'
|
|
@@ -57,6 +59,8 @@ export const StackspotAIWidget = (
|
|
|
57
59
|
<Stacks />
|
|
58
60
|
<Workspaces />
|
|
59
61
|
<KnowledgeSources />
|
|
62
|
+
<KSDocument />
|
|
63
|
+
<Agents />
|
|
60
64
|
{!isMinimized && <div className="chat-right-panel" ref={rightPanelRef}><RightPanel /></div>}
|
|
61
65
|
</div>
|
|
62
66
|
</RightPanelProvider>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { aiClient, StackspotAPIError } from '@stack-spot/portal-network'
|
|
2
|
-
import { ChatEntry } from '../state/ChatEntry'
|
|
2
|
+
import { ChatEntry, KnowledgeSource } from '../state/ChatEntry'
|
|
3
3
|
import { ChatState } from '../state/ChatState'
|
|
4
4
|
import { buildConversationContext } from '../utils/chat'
|
|
5
5
|
|
|
@@ -15,13 +15,25 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState)
|
|
|
15
15
|
const stream = aiClient.sendChatMessage({ context, user_prompt: content })
|
|
16
16
|
const botEntry = ChatEntry.createStreamedBotEntry(() => stream.cancel())
|
|
17
17
|
chat.pushMessage(botEntry)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
let knowledgeSources: KnowledgeSource[] | undefined
|
|
19
|
+
stream.onChange(value => {
|
|
20
|
+
if (value.sources?.length !== knowledgeSources?.length) {
|
|
21
|
+
knowledgeSources = value.sources?.filter(s => s.type === 'knowledge_source').map(ks => ({
|
|
22
|
+
documentId: ks.document_id,
|
|
23
|
+
documentScore: ks.document_score,
|
|
24
|
+
name: ks.name,
|
|
25
|
+
slug: ks.slug,
|
|
26
|
+
}))
|
|
27
|
+
}
|
|
28
|
+
botEntry.setValue({
|
|
29
|
+
agent: 'bot',
|
|
30
|
+
type: 'md',
|
|
31
|
+
content: value.answer ?? '',
|
|
32
|
+
messageId: value.message_id ?? undefined,
|
|
33
|
+
knowledgeSources,
|
|
34
|
+
updated: new Date().toISOString(),
|
|
35
|
+
})
|
|
36
|
+
})
|
|
25
37
|
try {
|
|
26
38
|
await stream.getValue()
|
|
27
39
|
} catch (error: any) {
|
|
@@ -9,7 +9,7 @@ export const RadioCheckBox = styled.ul`
|
|
|
9
9
|
flex-direction: column;
|
|
10
10
|
gap: 6px;
|
|
11
11
|
|
|
12
|
-
li {
|
|
12
|
+
> li {
|
|
13
13
|
display: flex;
|
|
14
14
|
flex-direction: column;
|
|
15
15
|
gap: 8px;
|
|
@@ -26,6 +26,7 @@ export const RadioCheckBox = styled.ul`
|
|
|
26
26
|
input[type="radio"], input[type="checkbox"] {
|
|
27
27
|
border-color: ${theme.color.light[700]};
|
|
28
28
|
background-color: transparent;
|
|
29
|
+
flex-shrink: 0;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
input[type="checkbox"]:checked {
|
package/src/context/hooks.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
-
import { useContext, useEffect,
|
|
2
|
+
import { useContext, useEffect, useState } from 'react'
|
|
3
3
|
import { ChatEntry } from '../state/ChatEntry'
|
|
4
4
|
import { ChatProperties, ChatState, MessageInterceptor } from '../state/ChatState'
|
|
5
5
|
import { ObservableState } from '../state/ObservableState'
|
|
@@ -78,9 +78,6 @@ export function useCurrentChatMessages(): ChatEntry[] {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
export function useChatEntry(entry: ChatEntry) {
|
|
81
|
-
const immutable = useMemo(() => entry.hasFinished(), [])
|
|
82
|
-
// the following condition is not a problem for react hooks, it will always be true or always false.
|
|
83
|
-
if (immutable) return entry.getValue()
|
|
84
81
|
const [content, setContent] = useState(entry.getValue())
|
|
85
82
|
useEffect(() => entry.onChange(setContent), [])
|
|
86
83
|
return content
|
package/src/features.ts
CHANGED
|
@@ -6,8 +6,8 @@ import { styled } from 'styled-components'
|
|
|
6
6
|
import { WithChildren } from '../types'
|
|
7
7
|
|
|
8
8
|
interface Props extends WithChildren {
|
|
9
|
-
title:
|
|
10
|
-
description:
|
|
9
|
+
title: React.ReactNode,
|
|
10
|
+
description: React.ReactNode,
|
|
11
11
|
onClose: () => void,
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -22,6 +22,7 @@ const PanelBox = styled.div`
|
|
|
22
22
|
header {
|
|
23
23
|
display: flex;
|
|
24
24
|
flex-direction: row;
|
|
25
|
+
gap: 10px;
|
|
25
26
|
|
|
26
27
|
.title {
|
|
27
28
|
display: flex;
|
|
@@ -45,8 +46,8 @@ export const DefaultPanel = ({ description, onClose, title, children }: Props) =
|
|
|
45
46
|
<PanelBox>
|
|
46
47
|
<header>
|
|
47
48
|
<div className="title">
|
|
48
|
-
<Text appearance="h5">{title}</Text>
|
|
49
|
-
<Text colorScheme="light.700">{description}</Text>
|
|
49
|
+
{typeof title === 'string' ? <Text appearance="h5">{title}</Text> : title}
|
|
50
|
+
{typeof description === 'string' ? <Text colorScheme="light.700">{description}</Text> : description}
|
|
50
51
|
</div>
|
|
51
52
|
<IconButton title={t.close} aria-label={t.close} onClick={onClose}>
|
|
52
53
|
<Times />
|
|
@@ -3,8 +3,8 @@ import { DefaultPanel } from './DefaultPanel'
|
|
|
3
3
|
import { RightPanelContext } from './RightPanelProvider'
|
|
4
4
|
|
|
5
5
|
interface RightPanelOptions {
|
|
6
|
-
title:
|
|
7
|
-
description:
|
|
6
|
+
title: React.ReactNode,
|
|
7
|
+
description: React.ReactNode,
|
|
8
8
|
onClose?: () => void,
|
|
9
9
|
}
|
|
10
10
|
|
package/src/state/ChatEntry.ts
CHANGED
|
@@ -16,6 +16,13 @@ export interface ChatAction extends SerializableAction {
|
|
|
16
16
|
appearance?: 'primary' | 'secondary',
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export interface KnowledgeSource {
|
|
20
|
+
name: string,
|
|
21
|
+
slug: string,
|
|
22
|
+
documentScore: number,
|
|
23
|
+
documentId: string,
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
export interface TextChatEntry {
|
|
20
27
|
type: 'text' | 'md',
|
|
21
28
|
agent: 'bot' | 'user' | 'system',
|
|
@@ -23,7 +30,7 @@ export interface TextChatEntry {
|
|
|
23
30
|
actions?: ChatAction[],
|
|
24
31
|
subtitle?: string,
|
|
25
32
|
content: string,
|
|
26
|
-
|
|
33
|
+
knowledgeSources?: KnowledgeSource[],
|
|
27
34
|
updated?: string,
|
|
28
35
|
agentId?: string,
|
|
29
36
|
messageId?: string,
|
|
@@ -68,7 +75,6 @@ export class ChatEntry {
|
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
setValue(value: TextChatEntry) {
|
|
71
|
-
if (this.streamFinished) return
|
|
72
78
|
this.value = value
|
|
73
79
|
this.listeners.forEach(l => l(this.value))
|
|
74
80
|
}
|
|
@@ -79,7 +85,6 @@ export class ChatEntry {
|
|
|
79
85
|
|
|
80
86
|
finish() {
|
|
81
87
|
this.streamFinished = true
|
|
82
|
-
this.listeners = []
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
hasFinished() {
|
package/src/state/ChatState.ts
CHANGED
|
@@ -7,9 +7,13 @@ interface Labeled {
|
|
|
7
7
|
label: string,
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
interface LabeledWithImage extends Labeled {
|
|
11
|
+
image?: string,
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
export interface ChatProperties {
|
|
11
15
|
label: string,
|
|
12
|
-
agent?:
|
|
16
|
+
agent?: LabeledWithImage,
|
|
13
17
|
workspace?: Labeled,
|
|
14
18
|
stack?: Labeled,
|
|
15
19
|
knowledgeSources?: Labeled[],
|
package/src/state/WidgetState.ts
CHANGED
|
@@ -2,41 +2,29 @@ import { ChatTabsController } from './ChatTabsController'
|
|
|
2
2
|
import { ObservableState } from './ObservableState'
|
|
3
3
|
|
|
4
4
|
export interface WidgetProperties {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Current content of the right panel. Undefined for closed right panel.
|
|
7
|
+
*/
|
|
8
|
+
panel?: 'stack' | 'workspace' | 'agent' | 'ks' | 'editor' | 'history' | 'ks-details',
|
|
9
|
+
/**
|
|
10
|
+
* KS to use when the right panel "ks-details" is open.
|
|
11
|
+
*/
|
|
12
|
+
currentKSInPanel?: { name: string, slug: string, score: number, documentId: string },
|
|
13
|
+
/**
|
|
14
|
+
* Whether or not the widget is in its minimized version.
|
|
15
|
+
*/
|
|
11
16
|
isMinimized?: boolean,
|
|
17
|
+
/**
|
|
18
|
+
* The current code in the editor.
|
|
19
|
+
*/
|
|
12
20
|
code?: string,
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
const panelKeys: (keyof WidgetProperties)[] = [
|
|
16
|
-
'isAgentSelectionOpen', 'isChatHistoryOpen', 'isCodeEditorOpen', 'isKnowledgeSourceSelectionOpen', 'isStackSelectionOpen',
|
|
17
|
-
'isWorkspaceSelectionOpen',
|
|
18
|
-
]
|
|
19
|
-
|
|
20
23
|
export class WidgetState extends ObservableState<WidgetProperties> {
|
|
21
24
|
readonly chatTabs: ChatTabsController
|
|
22
25
|
|
|
23
26
|
constructor(initial: WidgetProperties = {}, chatTabs?: ChatTabsController) {
|
|
24
27
|
super(initial)
|
|
25
28
|
this.chatTabs = chatTabs ?? new ChatTabsController()
|
|
26
|
-
this.createCloseOthersBehavior()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
private createCloseOthersBehavior() {
|
|
30
|
-
panelKeys.forEach((key) => {
|
|
31
|
-
this.onChange(key, (value) => {
|
|
32
|
-
if (value) this.closeOtherPanels(key)
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private closeOtherPanels(leaveOpen: keyof WidgetProperties) {
|
|
38
|
-
panelKeys.forEach((key) => {
|
|
39
|
-
if (key !== leaveOpen) this.set(key, false)
|
|
40
|
-
})
|
|
41
29
|
}
|
|
42
30
|
}
|
package/src/utils/chat.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function buildConversationContext(state: ChatState): FixedChatRequest['co
|
|
|
21
21
|
language: getLanguage(),
|
|
22
22
|
knowledge_sources: state.get('knowledgeSources')?.map(ks => ks.id),
|
|
23
23
|
agent_id: state.get('agent')?.id,
|
|
24
|
-
agent_built_in:
|
|
24
|
+
agent_built_in: !state.get('agent'),
|
|
25
25
|
os: navigator.userAgent,
|
|
26
26
|
platform: 'web-widget',
|
|
27
27
|
platform_version: navigator.userAgent,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DocumentResponse } from '@stack-spot/portal-network/api/ai'
|
|
2
|
+
|
|
3
|
+
function attemptToParseMalFormedJson(str: string) {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(str)
|
|
6
|
+
} catch {
|
|
7
|
+
try {
|
|
8
|
+
const fixed = str
|
|
9
|
+
.replace(/"/g, '-_-')
|
|
10
|
+
.replace(/'/g, '"')
|
|
11
|
+
.replace(/-_-/g, "'")
|
|
12
|
+
.replace(
|
|
13
|
+
/": (\w+)/g,
|
|
14
|
+
(_, value) => (['null', 'true', 'false'].includes(value) || value.match(/\d+/)) ? `": ${value}` : `": "${value}"`,
|
|
15
|
+
)
|
|
16
|
+
return JSON.parse(fixed)
|
|
17
|
+
} catch {
|
|
18
|
+
return undefined
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function extractCodeFromKSDocument(document: DocumentResponse): { language?: string, snippet: string, text?: string } {
|
|
24
|
+
const language = (document?.metadata as any)?.language
|
|
25
|
+
if (language) {
|
|
26
|
+
const pageContent: string = document?.page_content ?? ''
|
|
27
|
+
let text: string | undefined = undefined
|
|
28
|
+
let snippet = pageContent
|
|
29
|
+
const match = pageContent.match(/^use case: .*\n/i)
|
|
30
|
+
if (match) {
|
|
31
|
+
text = match[0]
|
|
32
|
+
snippet = pageContent.replace(text, '')
|
|
33
|
+
}
|
|
34
|
+
return { language, snippet, text }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (document?.page_content) {
|
|
38
|
+
const parsed = attemptToParseMalFormedJson(document.page_content)
|
|
39
|
+
return parsed ? { language: 'json', snippet: JSON.stringify(parsed, null, 2) } : { snippet: document.page_content }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return typeof document === 'object' ? { language: 'json', snippet: JSON.stringify(document, null, 2) } : { snippet: String(document) }
|
|
43
|
+
}
|
package/src/views/Agents.tsx
CHANGED
|
@@ -1 +1,186 @@
|
|
|
1
|
-
|
|
1
|
+
import { Button, Text } from '@citric/core'
|
|
2
|
+
import { Search } from '@citric/icons'
|
|
3
|
+
import { Badge } from '@citric/ui'
|
|
4
|
+
import { Placeholder } from '@stack-spot/portal-components/Placeholder'
|
|
5
|
+
import { agentClient } from '@stack-spot/portal-network'
|
|
6
|
+
import { AgentResponse, VisibilityLevel } from '@stack-spot/portal-network/api/agent'
|
|
7
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
8
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
9
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
10
|
+
import { styled } from 'styled-components'
|
|
11
|
+
import { DescribedRadioGroup } from '../components/form/DescribedRadioGroup'
|
|
12
|
+
import { IconInput } from '../components/IconInput'
|
|
13
|
+
import { RightPanelTabs } from '../components/RightPanelTabs'
|
|
14
|
+
import { useCurrentChat, useWidget, useWidgetState } from '../context/hooks'
|
|
15
|
+
import { useRightPanel } from '../right-panel/hooks'
|
|
16
|
+
|
|
17
|
+
const AgentLabel = styled.div`
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: row;
|
|
20
|
+
align-items: center;
|
|
21
|
+
gap: 6px;
|
|
22
|
+
|
|
23
|
+
img {
|
|
24
|
+
width: 20px;
|
|
25
|
+
height: 20px;
|
|
26
|
+
border-radius: 50%;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
}
|
|
29
|
+
`
|
|
30
|
+
|
|
31
|
+
const AgentDescription = styled.div`
|
|
32
|
+
color: ${theme.color.light[700]};
|
|
33
|
+
line-height: 18px;
|
|
34
|
+
|
|
35
|
+
section {
|
|
36
|
+
border-bottom: 1px solid ${theme.color.light[600]};
|
|
37
|
+
padding-bottom: 10px;
|
|
38
|
+
margin-bottom: 10px;
|
|
39
|
+
|
|
40
|
+
&:last-child {
|
|
41
|
+
padding-bottom: 0;
|
|
42
|
+
margin-bottom: 0;
|
|
43
|
+
border-bottom: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.title {
|
|
47
|
+
display: block;
|
|
48
|
+
margin-bottom: 6px;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ul {
|
|
53
|
+
padding: 0;
|
|
54
|
+
margin: 0;
|
|
55
|
+
list-style: none;
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: row;
|
|
58
|
+
flex-wrap: wrap;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
gap: 6px;
|
|
61
|
+
|
|
62
|
+
li {
|
|
63
|
+
margin: 3px 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`
|
|
67
|
+
|
|
68
|
+
export const Agents = () => {
|
|
69
|
+
const t = useTranslate(dictionary)
|
|
70
|
+
const panel = useWidgetState('panel')
|
|
71
|
+
const { open } = useRightPanel()
|
|
72
|
+
const widget = useWidget()
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (panel === 'agent') open(
|
|
76
|
+
<AgentsPanel />,
|
|
77
|
+
{ title: t.title, description: t.description, onClose: () => widget.set('panel', undefined) },
|
|
78
|
+
)
|
|
79
|
+
}, [panel, t])
|
|
80
|
+
|
|
81
|
+
return null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const AgentsPanel = () => {
|
|
85
|
+
const t = useTranslate(dictionary)
|
|
86
|
+
|
|
87
|
+
return <RightPanelTabs tabs={[
|
|
88
|
+
{ title: t.personal, content: <AgentsTab key="personal" visibility="PERSONAL" /> },
|
|
89
|
+
{ title: t.builtin, content: <AgentsTab key="builtin" visibility="BUILT-IN" /> },
|
|
90
|
+
{ title: t.shared, content: <AgentsTab key="shared" visibility="SHARED" /> },
|
|
91
|
+
{ title: t.account, content: <AgentsTab key="account" visibility="ACCOUNT" /> },
|
|
92
|
+
]} />
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT-IN' }) => {
|
|
96
|
+
const t = useTranslate(dictionary)
|
|
97
|
+
const { close } = useRightPanel()
|
|
98
|
+
const chat = useCurrentChat()
|
|
99
|
+
const [filter, setFilter] = useState('')
|
|
100
|
+
const agents = visibility === 'BUILT-IN' ? agentClient.publicAgents.useQuery({}) : agentClient.agents.useQuery({ visibility })
|
|
101
|
+
const [value, setValue] = useState<AgentResponse | undefined>(agents.find(a => a.id === chat.get('agent')?.id))
|
|
102
|
+
const filtered = useMemo(
|
|
103
|
+
() => filter ? agents.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : agents,
|
|
104
|
+
[agents, filter, value],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
function submit() {
|
|
108
|
+
if (value) chat.set('agent', { id: value.id, label: value.name, image: value.avatar })
|
|
109
|
+
close()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<div className="content">
|
|
115
|
+
<IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
|
|
116
|
+
{!!filtered.length && <DescribedRadioGroup
|
|
117
|
+
options={filtered}
|
|
118
|
+
keygen={a => a.id}
|
|
119
|
+
value={value}
|
|
120
|
+
onChange={setValue}
|
|
121
|
+
renderLabel={({ name, avatar }) => (
|
|
122
|
+
<AgentLabel>
|
|
123
|
+
{avatar && <img src={avatar} />}
|
|
124
|
+
<Text>{name}</Text>
|
|
125
|
+
</AgentLabel>
|
|
126
|
+
)}
|
|
127
|
+
renderDescription={({ description, llm_config: llmc, knowledge_sources_config: ksc }) => (
|
|
128
|
+
<AgentDescription>
|
|
129
|
+
{description && <section>
|
|
130
|
+
<Text appearance="microtext1" className="title">Description</Text>
|
|
131
|
+
<Text>{description}</Text>
|
|
132
|
+
</section>}
|
|
133
|
+
{!!ksc?.knowledge_sources?.length && <section>
|
|
134
|
+
<Text appearance="microtext1" className="title">Knowledge sources</Text>
|
|
135
|
+
<ul>
|
|
136
|
+
{ksc.knowledge_sources.map((ks, index) => <li key={index}><Badge palette="teal" appearance="square">{ks}</Badge></li>)}
|
|
137
|
+
</ul>
|
|
138
|
+
</section>}
|
|
139
|
+
{llmc?.model_slug && <section>
|
|
140
|
+
<Text appearance="microtext1" className="title">LLM</Text>
|
|
141
|
+
<Badge palette="orange" appearance="square">{llmc.model_slug}</Badge>
|
|
142
|
+
</section>}
|
|
143
|
+
</AgentDescription>
|
|
144
|
+
)}
|
|
145
|
+
optionClassName={a => (a === value && filter && !a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
|
|
146
|
+
? 'filtered-out'
|
|
147
|
+
: ''
|
|
148
|
+
}
|
|
149
|
+
className="option-list"
|
|
150
|
+
/>}
|
|
151
|
+
{!!agents.length && !filtered.length && <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />}
|
|
152
|
+
{!agents.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
|
|
153
|
+
</div>
|
|
154
|
+
<Button onClick={submit} disabled={!value}>{t.apply}</Button>
|
|
155
|
+
</>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const dictionary = {
|
|
160
|
+
en: {
|
|
161
|
+
title: 'Agents',
|
|
162
|
+
description: 'By selecting an Agent, it will be consulted to generate the answers.',
|
|
163
|
+
personal: 'Personal',
|
|
164
|
+
builtin: 'Built-in',
|
|
165
|
+
shared: 'Shared',
|
|
166
|
+
account: 'Account',
|
|
167
|
+
apply: 'Apply',
|
|
168
|
+
noSearchResults: "Your search didn't yield results.",
|
|
169
|
+
noSearchResultsDescription: 'Please, try another search term.',
|
|
170
|
+
noData: 'There are no agents in this category yet.',
|
|
171
|
+
noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new agents.',
|
|
172
|
+
},
|
|
173
|
+
pt: {
|
|
174
|
+
title: 'Agentes',
|
|
175
|
+
description: 'Ao selecionar um Agente, ele será consultado para gerar as respostas.',
|
|
176
|
+
personal: 'Pessoal',
|
|
177
|
+
builtin: 'Embutido',
|
|
178
|
+
shared: 'Compartilhado',
|
|
179
|
+
account: 'Conta',
|
|
180
|
+
apply: 'Aplicar',
|
|
181
|
+
noSearchResults: 'Sua busca não produziu resultados.',
|
|
182
|
+
noSearchResultsDescription: 'Por favor, tente outra busca.',
|
|
183
|
+
noData: 'Ainda não há agentes nesta categoria.',
|
|
184
|
+
noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos agentes.',
|
|
185
|
+
},
|
|
186
|
+
} satisfies Dictionary
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { IconBox, Text } from '@citric/core'
|
|
1
|
+
import { Button, IconBox, Text } from '@citric/core'
|
|
2
2
|
import { Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
|
|
3
3
|
import { Avatar, IconButton } from '@citric/ui'
|
|
4
4
|
import { aiClient } from '@stack-spot/portal-network'
|
|
5
5
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
6
|
-
import { useMemo, useRef, useState } from 'react'
|
|
6
|
+
import { useCallback, useMemo, useRef, useState } from 'react'
|
|
7
7
|
import { Markdown } from '../../components/Markdown'
|
|
8
|
-
import { useChatEntry } from '../../context/hooks'
|
|
8
|
+
import { useChatEntry, useWidget } from '../../context/hooks'
|
|
9
9
|
import { useChatScrollToBottomEffect } from '../../hooks/chat-scroll'
|
|
10
|
-
import { ChatEntry } from '../../state/ChatEntry'
|
|
10
|
+
import { ChatEntry, TextChatEntry } from '../../state/ChatEntry'
|
|
11
11
|
import { useDateFormatter } from '../../utils/date'
|
|
12
12
|
import { AgentInfo } from './AgentInfo'
|
|
13
13
|
|
|
@@ -20,8 +20,14 @@ export const ChatMessage = ({ message, username }: { message: ChatEntry, usernam
|
|
|
20
20
|
const date = new Date(entry.updated ?? '')
|
|
21
21
|
const shouldShowDate = entry.updated && !isNaN(date.getTime())
|
|
22
22
|
const ref = useRef<HTMLLIElement>(null)
|
|
23
|
+
const widget = useWidget()
|
|
23
24
|
useChatScrollToBottomEffect(ref, [entry])
|
|
24
25
|
|
|
26
|
+
const detailKS = useCallback(({ name, slug, documentScore, documentId }: Required<TextChatEntry>['knowledgeSources'][number]) => {
|
|
27
|
+
widget.set('currentKSInPanel', { name, slug, score: documentScore, documentId })
|
|
28
|
+
widget.set('panel', 'ks-details')
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
25
31
|
const { like, dislike } = useMemo(() => {
|
|
26
32
|
async function feedback(like: boolean) {
|
|
27
33
|
if (!entry.messageId || like === liked) return
|
|
@@ -62,6 +68,14 @@ export const ChatMessage = ({ message, username }: { message: ChatEntry, usernam
|
|
|
62
68
|
<Text appearance="microtext1">{entry.error}</Text>
|
|
63
69
|
</div>
|
|
64
70
|
)}
|
|
71
|
+
{!!entry.knowledgeSources?.length && <div className="ks-box">
|
|
72
|
+
<Text appearance="microtext1" colorScheme="light.700">Knowledge Sources:</Text>
|
|
73
|
+
<ul>{entry.knowledgeSources.map(ks => (
|
|
74
|
+
<li key={ks.slug}>
|
|
75
|
+
<Button size="sm" colorScheme="light" onClick={() => detailKS(ks)}>{ks.name}</Button>
|
|
76
|
+
</li>
|
|
77
|
+
))}</ul>
|
|
78
|
+
</div>}
|
|
65
79
|
<div className="message-footer">
|
|
66
80
|
{entry.agent === 'bot' && entry.messageId && !entry.error && <div className="message-actions">
|
|
67
81
|
<IconButton title={t.like} aria-label={t.like} onClick={like}>
|
package/src/views/Chat/styled.ts
CHANGED
|
@@ -113,4 +113,28 @@ export const ChatList = styled.ul`
|
|
|
113
113
|
.plain-text {
|
|
114
114
|
margin: 0
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
.ks-box {
|
|
118
|
+
border-radius: 8px;
|
|
119
|
+
display: flex;
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
gap: 10px;
|
|
122
|
+
padding: 8px;
|
|
123
|
+
border: 1px solid ${theme.color.light[500]};
|
|
124
|
+
|
|
125
|
+
> ul {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: row;
|
|
128
|
+
flex-wrap: wrap;
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
margin: 0;
|
|
131
|
+
padding: 0;
|
|
132
|
+
list-style: none;
|
|
133
|
+
gap: 6px;
|
|
134
|
+
|
|
135
|
+
> li button {
|
|
136
|
+
margin: 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
116
140
|
`
|