Skip to content

Commit 62f372b

Browse files
committed
Refactor
1 parent 2093224 commit 62f372b

File tree

5 files changed

+91
-86
lines changed

5 files changed

+91
-86
lines changed

src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/AskAi/ChatMessage.tsx

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,59 @@ const getMessageState = (message: ChatMessageType) => ({
8383
hasError: message.status === 'error',
8484
})
8585

86+
// Helper functions for computing AI status
87+
const getToolCallSearchQuery = (
88+
messages: LlmGatewayMessage[]
89+
): string | null => {
90+
const toolCallMessage = messages.find((m) => m.type === 'tool_call')
91+
if (!toolCallMessage) return null
92+
93+
try {
94+
const toolCalls = toolCallMessage.data?.toolCalls
95+
if (toolCalls && toolCalls.length > 0) {
96+
const firstToolCall = toolCalls[0]
97+
return firstToolCall.args?.searchQuery || null
98+
}
99+
} catch (e) {
100+
console.error('Error extracting search query from tool call:', e)
101+
}
102+
103+
return null
104+
}
105+
106+
const hasContentStarted = (messages: LlmGatewayMessage[]): boolean => {
107+
return messages.some((m) => m.type === 'ai_message_chunk' && m.data.content)
108+
}
109+
110+
const hasReachedReferences = (messages: LlmGatewayMessage[]): boolean => {
111+
const accumulatedContent = messages
112+
.filter((m) => m.type === 'ai_message_chunk')
113+
.map((m) => m.data.content)
114+
.join('')
115+
return accumulatedContent.includes('--- references ---')
116+
}
117+
118+
const computeAiStatus = (
119+
llmMessages: LlmGatewayMessage[],
120+
isComplete: boolean
121+
): string | null => {
122+
if (isComplete) return null
123+
124+
const searchQuery = getToolCallSearchQuery(llmMessages)
125+
const contentStarted = hasContentStarted(llmMessages)
126+
const reachedReferences = hasReachedReferences(llmMessages)
127+
128+
if (reachedReferences) {
129+
return 'Gathering resources'
130+
} else if (contentStarted) {
131+
return 'Generating'
132+
} else if (searchQuery) {
133+
return `Searching for "${searchQuery}"`
134+
}
135+
136+
return 'Thinking'
137+
}
138+
86139
// Action bar for complete AI messages
87140
const ActionBar = ({
88141
content,
@@ -187,16 +240,30 @@ export const ChatMessage = ({
187240

188241
const hasError = message.status === 'error' || !!error
189242

190-
const { mainContent, referencesJson } = useMemo(
191-
() => splitContentAndReferences(content),
192-
[content]
193-
)
243+
// Only split content and references when complete for better performance
244+
const { mainContent, referencesJson } = useMemo(() => {
245+
if (isComplete) {
246+
return splitContentAndReferences(content)
247+
}
248+
// During streaming, strip out unparsed references but don't parse them yet
249+
const delimiter = '--- references ---'
250+
const delimiterIndex = content.indexOf(delimiter)
251+
if (delimiterIndex !== -1) {
252+
return { mainContent: content.substring(0, delimiterIndex).trim(), referencesJson: null }
253+
}
254+
return { mainContent: content, referencesJson: null }
255+
}, [content, isComplete])
194256

195257
const parsed = useMemo(() => {
196258
const html = markedInstance.parse(mainContent) as string
197259
return DOMPurify.sanitize(html)
198260
}, [mainContent])
199261

262+
const aiStatus = useMemo(
263+
() => computeAiStatus(llmMessages, isComplete),
264+
[llmMessages, isComplete]
265+
)
266+
200267
const ref = React.useRef<HTMLDivElement>(null)
201268

202269
useEffect(() => {
@@ -279,15 +346,12 @@ export const ChatMessage = ({
279346
)}
280347

281348
{content && isLoading && <EuiSpacer size="m" />}
282-
<GeneratingStatus
283-
llmMessages={llmMessages}
284-
isComplete={isComplete}
285-
/>
349+
<GeneratingStatus status={aiStatus} />
286350

287351
{isComplete && content && (
288352
<>
289353
<EuiSpacer size="m" />
290-
<ActionBar content={content} onRetry={onRetry} />
354+
<ActionBar content={mainContent} onRetry={onRetry} />
291355
</>
292356
)}
293357

src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/AskAi/GeneratingStatus.tsx

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/** @jsxImportSource @emotion/react */
2-
import { LlmGatewayMessage } from './useLlmGateway'
32
import {
43
EuiFlexGroup,
54
EuiFlexItem,
@@ -9,73 +8,22 @@ import {
98
import * as React from 'react'
109

1110
interface GeneratingStatusProps {
12-
llmMessages: LlmGatewayMessage[]
13-
isComplete?: boolean
11+
status: string | null
1412
}
1513

16-
const getToolCallSearchQuery = (
17-
messages: LlmGatewayMessage[]
18-
): string | null => {
19-
const toolCallMessage = messages.find((m) => m.type === 'tool_call')
20-
if (!toolCallMessage) return null
21-
22-
try {
23-
const toolCalls = toolCallMessage.data?.toolCalls
24-
if (toolCalls && toolCalls.length > 0) {
25-
const firstToolCall = toolCalls[0]
26-
return firstToolCall.args?.searchQuery || null
27-
}
28-
} catch (e) {
29-
console.error('Error extracting search query from tool call:', e)
30-
}
31-
32-
return null
33-
}
34-
35-
const hasContentStarted = (messages: LlmGatewayMessage[]): boolean => {
36-
return messages.some((m) => m.type === 'ai_message_chunk' && m.data.content)
37-
}
38-
39-
const hasReachedReferences = (messages: LlmGatewayMessage[]): boolean => {
40-
const accumulatedContent = messages
41-
.filter((m) => m.type === 'ai_message_chunk')
42-
.map((m) => m.data.content)
43-
.join('')
44-
return accumulatedContent.includes('--- references ---')
45-
}
46-
47-
export const GeneratingStatus = ({
48-
llmMessages,
49-
isComplete = false,
50-
}: GeneratingStatusProps) => {
51-
const searchQuery = getToolCallSearchQuery(llmMessages)
52-
const contentStarted = hasContentStarted(llmMessages)
53-
const reachedReferences = hasReachedReferences(llmMessages)
54-
55-
// If complete, don't show anything
56-
if (isComplete) {
14+
export const GeneratingStatus = ({ status }: GeneratingStatusProps) => {
15+
if (!status) {
5716
return null
5817
}
5918

60-
// Loading states
61-
let statusText = 'Thinking'
62-
63-
if (reachedReferences) {
64-
statusText = 'Finding sources'
65-
} else if (contentStarted) {
66-
statusText = 'Generating'
67-
} else if (searchQuery) {
68-
statusText = `Searching for "${searchQuery}"`
69-
}
70-
7119
return (
7220
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
7321
<EuiFlexItem grow={false}>
7422
<EuiLoadingSpinner size="s" />
7523
</EuiFlexItem>
7624
<EuiFlexItem grow={false}>
7725
<EuiText size="xs" color="subdued">
78-
{statusText}...
26+
{status}...
7927
</EuiText>
8028
</EuiFlexItem>
8129
</EuiFlexGroup>

src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/AskAi/RelatedResources.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,6 @@ const parseReferences = (jsonString: string): Reference[] => {
4747
}
4848
return []
4949
} catch (e) {
50-
console.error(
51-
'Failed to parse references JSON:',
52-
e,
53-
'Input:',
54-
jsonString
55-
)
5650
return []
5751
}
5852
}
@@ -67,7 +61,7 @@ export const References = ({ referencesJson }: ReferencesProps) => {
6761

6862
return (
6963
<>
70-
<EuiSpacer size="m" />
64+
<EuiSpacer size="l" />
7165
<EuiPanel
7266
hasShadow={false}
7367
paddingSize="m"

src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/Search/SearchResults.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ function Breadcrumbs({ parents }: { parents: SearchResultItem['parents'] }) {
192192
css={css`
193193
margin-top: 2px;
194194
display: flex;
195-
gap: 0 ${euiTheme.size.xs};
195+
gap: 0 ${euiTheme.size.s};
196196
flex-wrap: wrap;
197197
list-style: none;
198198
`}
@@ -205,26 +205,18 @@ function Breadcrumbs({ parents }: { parents: SearchResultItem['parents'] }) {
205205
css={css`
206206
&:not(:last-child)::after {
207207
content: '/';
208-
margin-left: ${euiTheme.size.xs};
208+
margin-left: ${euiTheme.size.s};
209209
font-size: ${smallFontsize};
210-
color: ${euiTheme.colors.text};
210+
color: ${euiTheme.colors.textSubdued};
211211
margin-top: -1px;
212212
}
213213
display: inline-flex;
214214
`}
215215
>
216-
<EuiLink href={parent.url} color="text" tabIndex={-1}>
216+
<EuiLink href={parent.url} color="subdued" tabIndex={-1}>
217217
<EuiText
218218
size="xs"
219219
color="subdued"
220-
css={css`
221-
.euiMark {
222-
background-color: transparent;
223-
text-decoration: underline;
224-
color: inherit;
225-
font-weight: inherit;
226-
}
227-
`}
228220
>
229221
{parent.title}
230222
</EuiText>

src/api/Elastic.Documentation.Api.Core/AskAi/AskAiUsecase.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,24 @@ public record AskAiRequest(string Message, string? ThreadId)
3434
3535
- Do not mention that you are a language model or AI.
3636
- Do not provide answers based on your general knowledge.
37+
- Do not add a heading for the references section.
38+
- Do not include any preamble or explanation before the sources section.
3739
3840
## Formatting Guidelines:
3941
- Use Markdown for formatting your response.
4042
- Use headings, bullet points, and numbered lists to organize information clearly.
4143
- Use sentence case for headings.
4244
4345
## Sources and References Extraction *IMPORTANT*:
44-
- Do *NOT* add a heading for the sources section.
46+
4547
- When you provide an answer, *ALWAYS* include a references at the end of your response.
4648
- List all relevant document titles or sections that you referenced to formulate your answer.
49+
- Also add the links of the documents you used in your answer.
4750
- Only use the documents provided to you; do not reference any external sources.
48-
- If no relevant documents were used, state "No sources available."
51+
- If no relevant documents were used return an empty list.
52+
- The JSON is hidden from the user so exclude any preamble or explanation about it.
4953
- Use this schema:
54+
```
5055
{
5156
"$schema": "http://json-schema.org/draft-07/schema#",
5257
"title": "List of Documentation Resources",
@@ -66,6 +71,7 @@ public record AskAiRequest(string Message, string? ThreadId)
6671
},
6772
"description": {
6873
"description": "A brief description of the resource.",
74+
"maxLength": 150,
6975
"type": "string"
7076
}
7177
},
@@ -76,6 +82,7 @@ public record AskAiRequest(string Message, string? ThreadId)
7682
]
7783
}
7884
}
85+
```
7986
- Ensure that the URLs you provide are directly relevant to the user's question and the content of the documents.
8087
- Add a delimiter "--- references ---" before the sources section
8188

0 commit comments

Comments
 (0)