@stack-spot/ai-chat-widget 1.0.0-dev.1769120820021 → 1.0.0-dev.1769797270860
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 +27 -0
- package/README.md +2 -1
- package/dist/StackspotAIWidget.d.ts +1 -0
- package/dist/StackspotAIWidget.d.ts.map +1 -1
- package/dist/StackspotAIWidget.js +1 -0
- package/dist/StackspotAIWidget.js.map +1 -1
- package/dist/app-metadata.json +15 -3
- package/dist/chat-interceptors/quick-commands.d.ts +15 -0
- package/dist/chat-interceptors/quick-commands.d.ts.map +1 -1
- package/dist/chat-interceptors/quick-commands.js +29 -234
- 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 +39 -38
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/FileDescription.js +1 -1
- package/dist/components/FileDescription.js.map +1 -1
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +22 -6
- package/dist/components/Markdown.js.map +1 -1
- package/dist/components/Selector/index.d.ts +1 -1
- 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/TabManager.d.ts.map +1 -1
- package/dist/components/TabManager.js +20 -4
- package/dist/components/TabManager.js.map +1 -1
- package/dist/state/ChatState.d.ts +5 -0
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js.map +1 -1
- package/dist/utils/chat.d.ts +4 -12
- package/dist/utils/chat.d.ts.map +1 -1
- package/dist/utils/chat.js +20 -19
- package/dist/utils/chat.js.map +1 -1
- package/dist/utils/knowledge-source.js +1 -1
- package/dist/utils/knowledge-source.js.map +1 -1
- package/dist/views/ChatHistory/utils.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.js +9 -5
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/Editor.d.ts.map +1 -1
- package/dist/views/Editor.js +16 -2
- package/dist/views/Editor.js.map +1 -1
- package/dist/views/KnowledgeSources.js +1 -1
- package/dist/views/KnowledgeSources.js.map +1 -1
- package/dist/views/MessageInput/ContextBar.js +1 -1
- package/dist/views/MessageInput/ContextBar.js.map +1 -1
- package/dist/views/Steps/dictionary.d.ts +1 -1
- package/package.json +5 -2
- package/src/StackspotAIWidget.tsx +1 -0
- package/src/app-metadata.json +15 -3
- package/src/chat-interceptors/quick-commands.ts +38 -278
- package/src/chat-interceptors/send-message.ts +40 -40
- package/src/components/FileDescription.tsx +1 -1
- package/src/components/Markdown.tsx +42 -23
- package/src/components/Selector/index.tsx +6 -6
- package/src/components/TabManager.tsx +31 -8
- package/src/state/ChatState.ts +6 -0
- package/src/utils/chat.ts +23 -22
- package/src/utils/knowledge-source.ts +1 -1
- package/src/views/ChatHistory/utils.ts +11 -6
- package/src/views/Editor.tsx +20 -3
- package/src/views/KnowledgeSources.tsx +1 -1
- package/src/views/MessageInput/ContextBar.tsx +1 -1
|
@@ -5,7 +5,7 @@ import { Button, IconButton } from '@stack-spot/citric-react'
|
|
|
5
5
|
import { listToClass, theme } from '@stack-spot/portal-theme'
|
|
6
6
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
7
7
|
import { last } from 'lodash'
|
|
8
|
-
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
8
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
9
9
|
import { styled } from 'styled-components'
|
|
10
10
|
import { ButtonAction } from '../types'
|
|
11
11
|
import { FadingOverflow } from './FadingOverflow'
|
|
@@ -152,19 +152,31 @@ export function TabManager<T, Key extends React.Key>(
|
|
|
152
152
|
const t = useTranslate(dictionary)
|
|
153
153
|
const tabList = useRef<HTMLUListElement>(null)
|
|
154
154
|
const lastNumberOfTabs = useRef(tabs.length)
|
|
155
|
+
const [ariaMessage, setAriaMessage] = useState('')
|
|
155
156
|
|
|
156
157
|
const onClickTab = useCallback((event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
157
158
|
const target = event.target as HTMLElement
|
|
158
159
|
if (target.tagName === 'LI') target.querySelector('button')?.click()
|
|
159
160
|
}, [])
|
|
160
161
|
|
|
161
|
-
const tabItems = useMemo(() => tabs.map((tab) =>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
const tabItems = useMemo(() => tabs.map((tab, idx) => {
|
|
163
|
+
const tabLabel = typeof renderLabel(tab) === 'string'
|
|
164
|
+
? renderLabel(tab)
|
|
165
|
+
: `${t.tab} ${idx + 1}`
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<li key={keygen(tab)} className={keygen(tab) === active ? 'active' : undefined} onClick={onClickTab}>
|
|
169
|
+
<button className="label" aria-label={`${t.chat} ${tabLabel}`} onClick={() => onSelect(tab)}>
|
|
170
|
+
<FadingOverflow>{renderLabel(tab)}</FadingOverflow>
|
|
171
|
+
</button>
|
|
172
|
+
{tabs.length > 1 &&
|
|
173
|
+
<IconButton appearance="text" icon="TimesMini" aria-label={`${tabLabel}`}
|
|
174
|
+
title={t.close} onClick={() => { onRemove(tab), setAriaMessage(`${t.chat} ${tabLabel} ${t.closed}`)
|
|
175
|
+
|
|
176
|
+
// Clears the message after a short time to prevent it from repeating
|
|
177
|
+
setTimeout(() => setAriaMessage(''), 1000)}} />}
|
|
178
|
+
</li>
|
|
179
|
+
)}), [tabs, active])
|
|
168
180
|
|
|
169
181
|
const extras = useMemo(() => buttons.map(({
|
|
170
182
|
ariaLabel, title, label, onClick, group, icon, appearance, size, className, style, disabled }) => label
|
|
@@ -204,6 +216,11 @@ export function TabManager<T, Key extends React.Key>(
|
|
|
204
216
|
|
|
205
217
|
return (
|
|
206
218
|
<Tabs $numberOfExtraButtons={buttons.length} className="tabs">
|
|
219
|
+
<div
|
|
220
|
+
aria-live="polite" aria-atomic="true"
|
|
221
|
+
style={{ position: 'absolute', left: '-9999px', width: '1px', height: '1px', overflow: 'hidden' }}>
|
|
222
|
+
{ariaMessage}
|
|
223
|
+
</div>
|
|
207
224
|
<FadingOverflow className="list-overflow" scroll="arrows" enableHorizontalScrollWithVerticalWheel>
|
|
208
225
|
<ul ref={tabList}>{tabItems}</ul>
|
|
209
226
|
</FadingOverflow>
|
|
@@ -215,9 +232,15 @@ export function TabManager<T, Key extends React.Key>(
|
|
|
215
232
|
const dictionary = {
|
|
216
233
|
en: {
|
|
217
234
|
close: 'Close',
|
|
235
|
+
tab: 'Tab',
|
|
236
|
+
chat: 'Chat',
|
|
237
|
+
closed: 'Closed',
|
|
218
238
|
},
|
|
219
239
|
pt: {
|
|
220
240
|
close: 'Fechar',
|
|
241
|
+
tab: 'Aba',
|
|
242
|
+
chat: 'Chat',
|
|
243
|
+
closed: 'Fechado',
|
|
221
244
|
},
|
|
222
245
|
} satisfies Dictionary
|
|
223
246
|
|
package/src/state/ChatState.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ChatRequest } from '@stack-spot/portal-network/api/genAiInference'
|
|
1
2
|
import { dropRight, last, pull } from 'lodash'
|
|
2
3
|
import { ulid } from 'ulid'
|
|
3
4
|
import { AbortedError } from '../AbortedError'
|
|
@@ -63,6 +64,11 @@ export interface ChatPropertiesWithOptionalFeatures {
|
|
|
63
64
|
* The current LLM (Large Language Model) being used for this chat.
|
|
64
65
|
*/
|
|
65
66
|
selected_model_id?: string,
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Extra options to be merged into the chat request payload sent to the backend.
|
|
70
|
+
*/
|
|
71
|
+
requestOptions?: Pick<ChatRequest, 'deep_search_ks' | 'return_ks_in_response' | 'use_conversation'>,
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
export interface ChatProperties extends ChatPropertiesWithOptionalFeatures {
|
package/src/utils/chat.ts
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
import { FixedChatRequest } from '@stack-spot/portal-network'
|
|
1
|
+
import type { FixedChatRequest } from '@stack-spot/portal-network'
|
|
2
2
|
import appData from '../app-metadata.json'
|
|
3
|
-
import { ChatEntry } from '../state/ChatEntry'
|
|
4
|
-
import { ChatState } from '../state/ChatState'
|
|
5
|
-
import { defaultLanguage } from './programming-languages'
|
|
3
|
+
import type { ChatEntry } from '../state/ChatEntry'
|
|
4
|
+
import type { ChatState } from '../state/ChatState'
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
* The conversation context is needed by most backend services.
|
|
11
|
-
*
|
|
12
|
-
* @param state the ChatState to build the context from.
|
|
13
|
-
* @returns the conversation context ready to be sent to the backend.
|
|
14
|
-
*/
|
|
15
|
-
export function buildConversationContext(state: ChatState, message?: ChatEntry): FixedChatRequest['context'] {
|
|
6
|
+
export function buildConversationContext(state: ChatState, message?: ChatEntry | undefined, userPrompt?: string): FixedChatRequest {
|
|
7
|
+
const requestOptions = state.get('requestOptions')
|
|
16
8
|
return {
|
|
17
|
-
|
|
9
|
+
streaming: true,
|
|
10
|
+
show_chat_processing_state: true,
|
|
11
|
+
return_ks_in_response: state.get('features')?.showSourcesInResponse,
|
|
12
|
+
deep_search_ks: false,
|
|
13
|
+
stackspot_knowledge: false,
|
|
14
|
+
user_prompt: userPrompt ?? '',
|
|
15
|
+
use_conversation: true,
|
|
18
16
|
conversation_id: state.id,
|
|
17
|
+
workspace_id: state.get('workspace')?.id,
|
|
19
18
|
stack_id: state.get('stack')?.id,
|
|
20
|
-
language: state.get('codeLanguage') || (state.get('codeSelection') ? defaultLanguage : undefined),
|
|
21
19
|
knowledge_sources: state.get('knowledgeSources')?.map(ks => ks.id),
|
|
22
20
|
upload_ids: message?.getValue().upload?.map(f => f.id),
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
selected_model: state.get('selected_model_id') ?? null,
|
|
22
|
+
agent_version_number: state.get('agent')?.agent_version_number,
|
|
23
|
+
context: {
|
|
24
|
+
os: navigator.userAgent,
|
|
25
|
+
platform: 'web-widget',
|
|
26
|
+
platform_version: navigator.userAgent,
|
|
27
|
+
stackspot_ai_version: appData.version,
|
|
28
|
+
agent_id: state.get('agent')?.id,
|
|
29
|
+
},
|
|
30
|
+
...requestOptions,
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -62,7 +62,7 @@ export function extractCodeFromKSDocument(document: DocumentResponse): { languag
|
|
|
62
62
|
export function genericSourcesToKnowledgeSources(
|
|
63
63
|
sources: (SourceStackAi | SourceKnowledgeSource | SourceProjectFile3)[] | undefined,
|
|
64
64
|
): KnowledgeSource[] | undefined {
|
|
65
|
-
return sources?.filter(s => s.type === 'knowledge_source').map(ks => {
|
|
65
|
+
return sources?.filter(s => s.type === 'knowledge_source' || s.type === 'cross_account').map(ks => {
|
|
66
66
|
const { document_id: documentId, document_score: documentScore, name, slug } = ks as SourceKnowledgeSource
|
|
67
67
|
return { documentId, documentScore, name, slug }
|
|
68
68
|
})
|
|
@@ -140,10 +140,12 @@ function toolsFromAgentInfo(agentInfo: any[] | null | undefined): string[] {
|
|
|
140
140
|
*/
|
|
141
141
|
export async function loadChat(widget: WidgetState, conversationId: string) {
|
|
142
142
|
const chat = await aiClient.chat.query({ conversationId })
|
|
143
|
-
const
|
|
143
|
+
const addedIds: Record<string, boolean> = {}
|
|
144
|
+
const historyAgents = chat.history?.reduce<{ agent_core_id: string, version_number?: number }[]>((accumulator, item) => {
|
|
144
145
|
const agentId = item.custom_agent?.id
|
|
145
|
-
if (agentId !== undefined) {
|
|
146
|
-
|
|
146
|
+
if (agentId !== undefined && !addedIds[agentId]) {
|
|
147
|
+
addedIds[agentId] = true
|
|
148
|
+
accumulator.push({ agent_core_id: agentId, version_number: item.custom_agent?.version_number })
|
|
147
149
|
}
|
|
148
150
|
return accumulator
|
|
149
151
|
}, []) ?? []
|
|
@@ -155,13 +157,16 @@ export async function loadChat(widget: WidgetState, conversationId: string) {
|
|
|
155
157
|
])
|
|
156
158
|
const agentsAsLabeledAgents: LabeledAgent[] = agents
|
|
157
159
|
.map((a) => ({ ...a, label: a.name, image: a.avatar ?? '', builtIn: a.visibility_level === 'built_in' }))
|
|
158
|
-
|
|
160
|
+
|
|
159
161
|
const agent = agentsAsLabeledAgents.find(a => a.id === last(chat.history)?.custom_agent?.id)
|
|
160
162
|
const builtIn = !!last(chat.history ?? [])?.custom_agent?.built_in
|
|
161
163
|
widget.chatTabs.add(new ChatState({
|
|
162
164
|
id: chat.id,
|
|
163
|
-
initial: {
|
|
164
|
-
|
|
165
|
+
initial: {
|
|
166
|
+
features: widget.chatFeatures, label: chat.title, stack, workspace, agent: agent ? {
|
|
167
|
+
...agent, builtIn,
|
|
168
|
+
} : undefined,
|
|
169
|
+
},
|
|
165
170
|
interceptors: widget.interceptors,
|
|
166
171
|
entries: await Promise.all(chat.history?.map(async (item, index) => new ChatEntry({
|
|
167
172
|
agentType: item.agent === 'USER' ? 'user' : 'bot',
|
package/src/views/Editor.tsx
CHANGED
|
@@ -21,6 +21,7 @@ const EditorBox = styled.div`
|
|
|
21
21
|
--vscode-editor-background: transparent !important;
|
|
22
22
|
--vscode-editorGutter-background: transparent !important;
|
|
23
23
|
}
|
|
24
|
+
|
|
24
25
|
`
|
|
25
26
|
|
|
26
27
|
const TitleBox = styled.div`
|
|
@@ -69,7 +70,7 @@ const Title = () => {
|
|
|
69
70
|
|
|
70
71
|
return (
|
|
71
72
|
<TitleBox>
|
|
72
|
-
<Text appearance="h5">Editor</Text>
|
|
73
|
+
<Text appearance="h5" tag="h2" >Editor</Text>
|
|
73
74
|
<Select
|
|
74
75
|
options={languages}
|
|
75
76
|
renderLabel={l => l?.label ?? defaultLanguage}
|
|
@@ -91,6 +92,22 @@ const EditorPanel = () => {
|
|
|
91
92
|
const selectionObserver = useRef<IDisposable | undefined>()
|
|
92
93
|
|
|
93
94
|
const setup: OnMount = useCallback(async (editor) => {
|
|
95
|
+
const container = editor.getContainerDomNode()
|
|
96
|
+
const el = container.querySelector('.native-edit-context')
|
|
97
|
+
if (el && el.hasAttribute('aria-label')) {
|
|
98
|
+
el.removeAttribute('aria-label')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const observer = new MutationObserver(() => {
|
|
102
|
+
const el = container.querySelector('.native-edit-context')
|
|
103
|
+
if (el && el.hasAttribute('aria-label')) {
|
|
104
|
+
el.removeAttribute('aria-label')
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
observer.observe(container, { subtree: true, attributes: true, childList: true })
|
|
108
|
+
|
|
109
|
+
editor.onDidDispose(() => observer.disconnect())
|
|
110
|
+
|
|
94
111
|
selectionObserver.current = editor.onDidChangeCursorSelection(debounce((e) => {
|
|
95
112
|
const selectedText = editor.getModel()?.getValueInRange(e.selection)
|
|
96
113
|
chat.set('codeSelection', selectedText?.trim() ? selectedText : undefined)
|
|
@@ -110,14 +127,14 @@ const EditorPanel = () => {
|
|
|
110
127
|
selectionObserver.current?.dispose()
|
|
111
128
|
}
|
|
112
129
|
}, [])
|
|
113
|
-
|
|
130
|
+
|
|
114
131
|
return (
|
|
115
132
|
<EditorBox>
|
|
116
133
|
<MonacoEditor
|
|
117
134
|
height="100%"
|
|
118
135
|
language={language}
|
|
119
136
|
theme={themeKind === 'dark' ? 'vs-dark' : 'light'}
|
|
120
|
-
options={{ minimap: { enabled: false } }}
|
|
137
|
+
options={{ minimap: { enabled: false }, accessibilitySupport: 'off' }}
|
|
121
138
|
value={value}
|
|
122
139
|
onChange={v => chat.set('code', v)}
|
|
123
140
|
loading={<ProgressCircular />}
|
|
@@ -189,7 +189,7 @@ export const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit, workspaceId,
|
|
|
189
189
|
hasNextPage={hasNextPage && !workspaceId}
|
|
190
190
|
fetchNextPage={fetchNextPage}
|
|
191
191
|
data={(ks: KnowledgeSourceItemResponse) => ({
|
|
192
|
-
idOrSlug: ks.
|
|
192
|
+
idOrSlug: ks.id, description: ks.description, name: ks.name, listFavorites,
|
|
193
193
|
onAddFavorite, onRemoveFavorite,
|
|
194
194
|
})}
|
|
195
195
|
emptyResults={<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />}
|
|
@@ -21,7 +21,7 @@ const ContextBadge = ({ label, color, dismiss, onDismiss }: ContextBadgeProps) =
|
|
|
21
21
|
appearance="none"
|
|
22
22
|
onClick={onDismiss}
|
|
23
23
|
title={dismiss}
|
|
24
|
-
|
|
24
|
+
aria-label={dismiss}
|
|
25
25
|
style={{ padding: '2px 0 2px 2px', color: 'inherit' }}
|
|
26
26
|
/>}
|
|
27
27
|
</Badge>
|