Skip to content

Commit 5576f9a

Browse files
committed
Fix the ConversationID handling of LLMGateway
1 parent 42e22f9 commit 5576f9a

File tree

6 files changed

+463
-35
lines changed

6 files changed

+463
-35
lines changed

docs-builder.sln

Lines changed: 251 additions & 5 deletions
Large diffs are not rendered by default.

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { AiProviderSelector } from './AiProviderSelector'
33
import { AskAiSuggestions } from './AskAiSuggestions'
44
import { ChatMessageList } from './ChatMessageList'
5-
import { useChatActions, useChatMessages } from './chat.store'
5+
import {useChatActions, useChatMessages, useConversationId} from './chat.store'
66
import {
77
useEuiOverflowScroll,
88
EuiButtonEmpty,
@@ -68,6 +68,7 @@ export const Chat = () => {
6868
const scrollRef = useRef<HTMLDivElement>(null)
6969
const lastMessageStatusRef = useRef<string | null>(null)
7070
const [inputValue, setInputValue] = useState('')
71+
const conversationId = useConversationId()
7172

7273
const dynamicScrollableStyles = css`
7374
${scrollableStyles}
@@ -124,6 +125,7 @@ export const Chat = () => {
124125
css={containerStyles}
125126
>
126127
<EuiSpacer size="m" />
128+
ConversationId: {conversationId}
127129

128130
{messages.length > 0 && (
129131
<NewConversationHeader onClick={clearChat} />

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ private async Task ProcessPipeAsync(PipeReader reader, PipeWriter writer, string
129129
/// Default implementation parses SSE events and JSON, then calls TransformJsonEvent.
130130
/// </summary>
131131
/// <returns>Stream processing result with metrics and captured output</returns>
132-
private async Task ProcessStreamAsync(PipeReader reader, PipeWriter writer, string? conversationId, Activity? parentActivity, CancellationToken cancellationToken)
132+
protected virtual async Task ProcessStreamAsync(PipeReader reader, PipeWriter writer, string? conversationId, Activity? parentActivity, CancellationToken cancellationToken)
133133
{
134134
using var activity = StreamTransformerActivitySource.StartActivity(nameof(ProcessStreamAsync));
135135

@@ -255,7 +255,7 @@ private async Task ProcessStreamAsync(PipeReader reader, PipeWriter writer, stri
255255
/// <summary>
256256
/// Write a transformed event to the output stream
257257
/// </summary>
258-
private async Task WriteEventAsync(AskAiEvent? transformedEvent, PipeWriter writer, CancellationToken cancellationToken)
258+
protected async Task WriteEventAsync(AskAiEvent? transformedEvent, PipeWriter writer, CancellationToken cancellationToken)
259259
{
260260
if (transformedEvent == null)
261261
return;

src/api/Elastic.Documentation.Api.Infrastructure/Adapters/AskAi/LlmGatewayAskAiGateway.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static LlmGatewayRequest CreateFromRequest(AskAiRequest request) =>
6868
new ChatInput("user", AskAiRequest.SystemPrompt),
6969
new ChatInput("user", request.Message)
7070
],
71-
ThreadId: request.ConversationId ?? "elastic-docs-" + Guid.NewGuid()
71+
ThreadId: request.ConversationId ?? Guid.NewGuid().ToString()
7272
);
7373
}
7474

src/api/Elastic.Documentation.Api.Infrastructure/Adapters/AskAi/LlmGatewayStreamTransformer.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using System.Diagnostics;
6+
using System.IO.Pipelines;
57
using System.Text.Json;
68
using Elastic.Documentation.Api.Core.AskAi;
79
using Microsoft.Extensions.Logging;
@@ -15,6 +17,36 @@ public class LlmGatewayStreamTransformer(ILogger<LlmGatewayStreamTransformer> lo
1517
{
1618
protected override string GetAgentId() => LlmGatewayAskAiGateway.ModelName;
1719
protected override string GetAgentProvider() => LlmGatewayAskAiGateway.ProviderName;
20+
21+
/// <summary>
22+
/// Override to emit ConversationStart event when conversationId is null (new conversation)
23+
/// </summary>
24+
protected override async Task ProcessStreamAsync(PipeReader reader, PipeWriter writer, string? conversationId, Activity? parentActivity, CancellationToken cancellationToken)
25+
{
26+
// If conversationId is null, generate a new one and emit ConversationStart event
27+
// This matches the ThreadId format used in LlmGatewayAskAiGateway
28+
var actualConversationId = conversationId;
29+
if (conversationId == null)
30+
{
31+
actualConversationId = Guid.NewGuid().ToString();
32+
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
33+
var conversationStartEvent = new AskAiEvent.ConversationStart(
34+
Id: Guid.NewGuid().ToString(),
35+
Timestamp: timestamp,
36+
ConversationId: actualConversationId
37+
);
38+
39+
// Set activity tags for the new conversation
40+
_ = parentActivity?.SetTag("gen_ai.conversation.id", actualConversationId);
41+
Logger.LogDebug("LLM Gateway conversation started: {ConversationId}", actualConversationId);
42+
43+
// Write the ConversationStart event to the stream
44+
await WriteEventAsync(conversationStartEvent, writer, cancellationToken);
45+
}
46+
47+
// Continue with normal stream processing using the actual conversation ID
48+
await base.ProcessStreamAsync(reader, writer, actualConversationId, parentActivity, cancellationToken);
49+
}
1850
protected override AskAiEvent? TransformJsonEvent(string? conversationId, string? eventType, JsonElement json)
1951
{
2052
// LLM Gateway format: ["custom", {type: "...", ...}]

0 commit comments

Comments
 (0)