Skip to content

Commit a9ff0cc

Browse files
SteveSandersonMSjeffhandley
authored andcommitted
Rename ChatThreadId to ConversationId (#6300)
* In OpenAI responses client, use response ID as ChatThreadId * Rename ChatThreadId -> ConversationId * Related renames
1 parent a6a2f4f commit a9ff0cc

File tree

12 files changed

+61
-51
lines changed

12 files changed

+61
-51
lines changed

src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatOptions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace Microsoft.Extensions.AI;
99
/// <summary>Represents the options for a chat request.</summary>
1010
public class ChatOptions
1111
{
12-
/// <summary>Gets or sets an optional identifier used to associate a request with an existing chat thread.</summary>
13-
public string? ChatThreadId { get; set; }
12+
/// <summary>Gets or sets an optional identifier used to associate a request with an existing conversation.</summary>
13+
public string? ConversationId { get; set; }
1414

1515
/// <summary>Gets or sets the temperature for generating chat responses.</summary>
1616
/// <remarks>
@@ -105,7 +105,7 @@ public virtual ChatOptions Clone()
105105
{
106106
ChatOptions options = new()
107107
{
108-
ChatThreadId = ChatThreadId,
108+
ConversationId = ConversationId,
109109
Temperature = Temperature,
110110
MaxOutputTokens = MaxOutputTokens,
111111
TopP = TopP,

src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponse.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,17 @@ public IList<ChatMessage> Messages
6363
/// <summary>Gets or sets the ID of the chat response.</summary>
6464
public string? ResponseId { get; set; }
6565

66-
/// <summary>Gets or sets the chat thread ID associated with this chat response.</summary>
66+
/// <summary>Gets or sets an identifier for the state of the conversation.</summary>
6767
/// <remarks>
68-
/// Some <see cref="IChatClient"/> implementations are capable of storing the state for a chat thread, such that
68+
/// Some <see cref="IChatClient"/> implementations are capable of storing the state for a conversation, such that
6969
/// the input messages supplied to <see cref="IChatClient.GetResponseAsync"/> need only be the additional messages beyond
7070
/// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
71-
/// and it should be used in a subsequent <see cref="ChatOptions.ChatThreadId"/> instead of supplying the same messages
72-
/// (and this <see cref="ChatResponse"/>'s message) as part of the <c>messages</c> parameter.
71+
/// and it should be used in a subsequent <see cref="ChatOptions.ConversationId"/> instead of supplying the same messages
72+
/// (and this <see cref="ChatResponse"/>'s message) as part of the <c>messages</c> parameter. Note that the value may
73+
/// or may not differ on every response, depending on whether the underlying provider uses a fixed ID for each conversation
74+
/// or updates it for each message.
7375
/// </remarks>
74-
public string? ChatThreadId { get; set; }
76+
public string? ConversationId { get; set; }
7577

7678
/// <summary>Gets or sets the model ID used in the creation of the chat response.</summary>
7779
public string? ModelId { get; set; }
@@ -127,7 +129,7 @@ public ChatResponseUpdate[] ToChatResponseUpdates()
127129
ChatMessage message = _messages![i];
128130
updates[i] = new ChatResponseUpdate
129131
{
130-
ChatThreadId = ChatThreadId,
132+
ConversationId = ConversationId,
131133

132134
AdditionalProperties = message.AdditionalProperties,
133135
AuthorName = message.AuthorName,

src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,9 @@ private static void ProcessUpdate(ChatResponseUpdate update, ChatResponse respon
320320
response.ResponseId = update.ResponseId;
321321
}
322322

323-
if (update.ChatThreadId is not null)
323+
if (update.ConversationId is not null)
324324
{
325-
response.ChatThreadId = update.ChatThreadId;
325+
response.ConversationId = update.ConversationId;
326326
}
327327

328328
if (update.CreatedAt is not null)

src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseUpdate.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,16 @@ public IList<AIContent> Contents
116116
/// </remarks>
117117
public string? MessageId { get; set; }
118118

119-
/// <summary>Gets or sets the chat thread ID associated with the chat response of which this update is a part.</summary>
119+
/// <summary>Gets or sets an identifier for the state of the conversation of which this update is a part.</summary>
120120
/// <remarks>
121-
/// Some <see cref="IChatClient"/> implementations are capable of storing the state for a chat thread, such that
121+
/// Some <see cref="IChatClient"/> implementations are capable of storing the state for a conversation, such that
122122
/// the input messages supplied to <see cref="IChatClient.GetStreamingResponseAsync"/> need only be the additional messages beyond
123123
/// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
124-
/// and it should be used in a subsequent <see cref="ChatOptions.ChatThreadId"/> instead of supplying the same messages
125-
/// (and this streaming message) as part of the <c>messages</c> parameter.
124+
/// and it should be used in a subsequent <see cref="ChatOptions.ConversationId"/> instead of supplying the same messages
125+
/// (and this streaming message) as part of the <c>messages</c> parameter. Note that the value may or may not differ on every
126+
/// response, depending on whether the underlying provider uses a fixed ID for each conversation or updates it for each message.
126127
/// </remarks>
127-
public string? ChatThreadId { get; set; }
128+
public string? ConversationId { get; set; }
128129

129130
/// <summary>Gets or sets a timestamp for the response update.</summary>
130131
public DateTimeOffset? CreatedAt { get; set; }

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public async Task<ChatResponse> GetResponseAsync(
8787
ChatResponse response = new()
8888
{
8989
ResponseId = openAIResponse.Id,
90+
ConversationId = openAIResponse.Id,
9091
CreatedAt = openAIResponse.CreatedAt,
9192
FinishReason = ToFinishReason(openAIResponse.IncompleteStatusDetails?.Reason),
9293
Messages = [new(ChatRole.Assistant, [])],
@@ -176,6 +177,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
176177
Contents = ToUsageDetails(completedUpdate.Response) is { } usage ? [new UsageContent(usage)] : [],
177178
CreatedAt = createdAt,
178179
ResponseId = responseId,
180+
ConversationId = responseId,
179181
FinishReason =
180182
ToFinishReason(completedUpdate.Response?.IncompleteStatusDetails?.Reason) ??
181183
(functionCallInfos is not null ? ChatFinishReason.ToolCalls : ChatFinishReason.Stop),
@@ -213,6 +215,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
213215
MessageId = lastMessageId,
214216
ModelId = modelId,
215217
ResponseId = responseId,
218+
ConversationId = responseId,
216219
};
217220
break;
218221

@@ -246,6 +249,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
246249
MessageId = lastMessageId,
247250
ModelId = modelId,
248251
ResponseId = responseId,
252+
ConversationId = responseId,
249253
};
250254
}
251255

@@ -259,6 +263,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
259263
MessageId = lastMessageId,
260264
ModelId = modelId,
261265
ResponseId = responseId,
266+
ConversationId = responseId,
262267
Contents =
263268
[
264269
new ErrorContent(errorUpdate.Message)
@@ -304,7 +309,7 @@ private static ResponseCreationOptions ToOpenAIResponseCreationOptions(ChatOptio
304309
{
305310
// Handle strongly-typed properties.
306311
result.MaxOutputTokenCount = options.MaxOutputTokens;
307-
result.PreviousResponseId = options.ChatThreadId;
312+
result.PreviousResponseId = options.ConversationId;
308313
result.TopP = options.TopP;
309314
result.Temperature = options.Temperature;
310315

src/Libraries/Microsoft.Extensions.AI/ChatCompletion/CachingChatClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
105105
if (await ReadCacheStreamingAsync(cacheKey, cancellationToken) is { } existingChunks)
106106
{
107107
// Yield all of the cached items.
108-
string? chatThreadId = null;
108+
string? conversationId = null;
109109
foreach (var chunk in existingChunks)
110110
{
111-
chatThreadId ??= chunk.ChatThreadId;
111+
conversationId ??= chunk.ConversationId;
112112
yield return chunk;
113113
}
114114
}

src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ChatResponse{T}.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public ChatResponse(ChatResponse response, JsonSerializerOptions serializerOptio
3939
{
4040
_serializerOptions = Throw.IfNull(serializerOptions);
4141
AdditionalProperties = response.AdditionalProperties;
42-
ChatThreadId = response.ChatThreadId;
42+
ConversationId = response.ConversationId;
4343
CreatedAt = response.CreatedAt;
4444
FinishReason = response.FinishReason;
4545
ModelId = response.ModelId;

src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ public override async Task<ChatResponse> GetResponseAsync(
224224
List<ChatMessage>? responseMessages = null; // tracked list of messages, across multiple turns, to be used for the final response
225225
UsageDetails? totalUsage = null; // tracked usage across all turns, to be used for the final response
226226
List<FunctionCallContent>? functionCallContents = null; // function call contents that need responding to in the current turn
227-
bool lastIterationHadThreadId = false; // whether the last iteration's response had a ChatThreadId set
227+
bool lastIterationHadConversationId = false; // whether the last iteration's response had a ConversationId set
228228
int consecutiveErrorCount = 0;
229229

230230
for (int iteration = 0; ; iteration++)
@@ -274,7 +274,7 @@ public override async Task<ChatResponse> GetResponseAsync(
274274
}
275275

276276
// Prepare the history for the next iteration.
277-
FixupHistories(originalMessages, ref messages, ref augmentedHistory, response, responseMessages, ref lastIterationHadThreadId);
277+
FixupHistories(originalMessages, ref messages, ref augmentedHistory, response, responseMessages, ref lastIterationHadConversationId);
278278

279279
// Add the responses from the function calls into the augmented history and also into the tracked
280280
// list of response messages.
@@ -287,7 +287,7 @@ public override async Task<ChatResponse> GetResponseAsync(
287287
break;
288288
}
289289

290-
UpdateOptionsForNextIteration(ref options!, response.ChatThreadId);
290+
UpdateOptionsForNextIteration(ref options!, response.ConversationId);
291291
}
292292

293293
Debug.Assert(responseMessages is not null, "Expected to only be here if we have response messages.");
@@ -318,7 +318,7 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
318318
List<ChatMessage>? augmentedHistory = null; // the actual history of messages sent on turns other than the first
319319
List<FunctionCallContent>? functionCallContents = null; // function call contents that need responding to in the current turn
320320
List<ChatMessage>? responseMessages = null; // tracked list of messages, across multiple turns, to be used in fallback cases to reconstitute history
321-
bool lastIterationHadThreadId = false; // whether the last iteration's response had a ChatThreadId set
321+
bool lastIterationHadConversationId = false; // whether the last iteration's response had a ConversationId set
322322
List<ChatResponseUpdate> updates = []; // updates from the current response
323323
int consecutiveErrorCount = 0;
324324

@@ -368,7 +368,7 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
368368
(responseMessages ??= []).AddRange(response.Messages);
369369

370370
// Prepare the history for the next iteration.
371-
FixupHistories(originalMessages, ref messages, ref augmentedHistory, response, responseMessages, ref lastIterationHadThreadId);
371+
FixupHistories(originalMessages, ref messages, ref augmentedHistory, response, responseMessages, ref lastIterationHadConversationId);
372372

373373
// Process all of the functions, adding their results into the history.
374374
var modeAndMessages = await ProcessFunctionCallsAsync(augmentedHistory, options, functionCallContents, iteration, consecutiveErrorCount, cancellationToken);
@@ -390,7 +390,7 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
390390
{
391391
AdditionalProperties = message.AdditionalProperties,
392392
AuthorName = message.AuthorName,
393-
ChatThreadId = response.ChatThreadId,
393+
ConversationId = response.ConversationId,
394394
CreatedAt = DateTimeOffset.UtcNow,
395395
Contents = message.Contents,
396396
RawRepresentation = message.RawRepresentation,
@@ -408,7 +408,7 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
408408
break;
409409
}
410410

411-
UpdateOptionsForNextIteration(ref options, response.ChatThreadId);
411+
UpdateOptionsForNextIteration(ref options, response.ConversationId);
412412
}
413413

414414
AddUsageTags(activity, totalUsage);
@@ -437,18 +437,18 @@ private static void AddUsageTags(Activity? activity, UsageDetails? usage)
437437
/// <param name="augmentedHistory">The augmented history containing all the messages to be sent.</param>
438438
/// <param name="response">The most recent response being handled.</param>
439439
/// <param name="allTurnsResponseMessages">A list of all response messages received up until this point.</param>
440-
/// <param name="lastIterationHadThreadId">Whether the previous iteration's response had a thread id.</param>
440+
/// <param name="lastIterationHadConversationId">Whether the previous iteration's response had a conversation id.</param>
441441
private static void FixupHistories(
442442
IEnumerable<ChatMessage> originalMessages,
443443
ref IEnumerable<ChatMessage> messages,
444444
[NotNull] ref List<ChatMessage>? augmentedHistory,
445445
ChatResponse response,
446446
List<ChatMessage> allTurnsResponseMessages,
447-
ref bool lastIterationHadThreadId)
447+
ref bool lastIterationHadConversationId)
448448
{
449449
// We're now going to need to augment the history with function result contents.
450450
// That means we need a separate list to store the augmented history.
451-
if (response.ChatThreadId is not null)
451+
if (response.ConversationId is not null)
452452
{
453453
// The response indicates the inner client is tracking the history, so we don't want to send
454454
// anything we've already sent or received.
@@ -461,9 +461,9 @@ private static void FixupHistories(
461461
augmentedHistory = [];
462462
}
463463

464-
lastIterationHadThreadId = true;
464+
lastIterationHadConversationId = true;
465465
}
466-
else if (lastIterationHadThreadId)
466+
else if (lastIterationHadConversationId)
467467
{
468468
// In the very rare case where the inner client returned a response with a thread ID but then
469469
// returned a subsequent response without one, we want to reconstitue the full history. To do that,
@@ -474,7 +474,7 @@ private static void FixupHistories(
474474
augmentedHistory.AddRange(originalMessages);
475475
augmentedHistory.AddRange(allTurnsResponseMessages);
476476

477-
lastIterationHadThreadId = false;
477+
lastIterationHadConversationId = false;
478478
}
479479
else
480480
{
@@ -486,7 +486,7 @@ private static void FixupHistories(
486486
// Now add the most recent response messages.
487487
augmentedHistory.AddMessages(response);
488488

489-
lastIterationHadThreadId = false;
489+
lastIterationHadConversationId = false;
490490
}
491491

492492
// Use the augmented history as the new set of messages to send.
@@ -525,22 +525,22 @@ private static bool CopyFunctionCalls(
525525
return any;
526526
}
527527

528-
private static void UpdateOptionsForNextIteration(ref ChatOptions options, string? chatThreadId)
528+
private static void UpdateOptionsForNextIteration(ref ChatOptions options, string? conversationId)
529529
{
530530
if (options.ToolMode is RequiredChatToolMode)
531531
{
532532
// We have to reset the tool mode to be non-required after the first iteration,
533533
// as otherwise we'll be in an infinite loop.
534534
options = options.Clone();
535535
options.ToolMode = null;
536-
options.ChatThreadId = chatThreadId;
536+
options.ConversationId = conversationId;
537537
}
538-
else if (options.ChatThreadId != chatThreadId)
538+
else if (options.ConversationId != conversationId)
539539
{
540540
// As with the other modes, ensure we've propagated the chat thread ID to the options.
541541
// We only need to clone the options if we're actually mutating it.
542542
options = options.Clone();
543-
options.ChatThreadId = chatThreadId;
543+
options.ConversationId = conversationId;
544544
}
545545
}
546546

test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/ChatCompletion/ChatOptionsTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ChatOptionsTests
1313
public void Constructor_Parameterless_PropsDefaulted()
1414
{
1515
ChatOptions options = new();
16-
Assert.Null(options.ChatThreadId);
16+
Assert.Null(options.ConversationId);
1717
Assert.Null(options.Temperature);
1818
Assert.Null(options.MaxOutputTokens);
1919
Assert.Null(options.TopP);
@@ -29,7 +29,7 @@ public void Constructor_Parameterless_PropsDefaulted()
2929
Assert.Null(options.AdditionalProperties);
3030

3131
ChatOptions clone = options.Clone();
32-
Assert.Null(options.ChatThreadId);
32+
Assert.Null(options.ConversationId);
3333
Assert.Null(clone.Temperature);
3434
Assert.Null(clone.MaxOutputTokens);
3535
Assert.Null(clone.TopP);
@@ -67,7 +67,7 @@ public void Properties_Roundtrip()
6767
["key"] = "value",
6868
};
6969

70-
options.ChatThreadId = "12345";
70+
options.ConversationId = "12345";
7171
options.Temperature = 0.1f;
7272
options.MaxOutputTokens = 2;
7373
options.TopP = 0.3f;
@@ -82,7 +82,7 @@ public void Properties_Roundtrip()
8282
options.Tools = tools;
8383
options.AdditionalProperties = additionalProps;
8484

85-
Assert.Equal("12345", options.ChatThreadId);
85+
Assert.Equal("12345", options.ConversationId);
8686
Assert.Equal(0.1f, options.Temperature);
8787
Assert.Equal(2, options.MaxOutputTokens);
8888
Assert.Equal(0.3f, options.TopP);
@@ -98,7 +98,7 @@ public void Properties_Roundtrip()
9898
Assert.Same(additionalProps, options.AdditionalProperties);
9999

100100
ChatOptions clone = options.Clone();
101-
Assert.Equal("12345", options.ChatThreadId);
101+
Assert.Equal("12345", options.ConversationId);
102102
Assert.Equal(0.1f, clone.Temperature);
103103
Assert.Equal(2, clone.MaxOutputTokens);
104104
Assert.Equal(0.3f, clone.TopP);
@@ -130,7 +130,7 @@ public void JsonSerialization_Roundtrips()
130130
["key"] = "value",
131131
};
132132

133-
options.ChatThreadId = "12345";
133+
options.ConversationId = "12345";
134134
options.Temperature = 0.1f;
135135
options.MaxOutputTokens = 2;
136136
options.TopP = 0.3f;
@@ -154,7 +154,7 @@ public void JsonSerialization_Roundtrips()
154154
ChatOptions? deserialized = JsonSerializer.Deserialize(json, TestJsonSerializerContext.Default.ChatOptions);
155155
Assert.NotNull(deserialized);
156156

157-
Assert.Equal("12345", deserialized.ChatThreadId);
157+
Assert.Equal("12345", deserialized.ConversationId);
158158
Assert.Equal(0.1f, deserialized.Temperature);
159159
Assert.Equal(2, deserialized.MaxOutputTokens);
160160
Assert.Equal(0.3f, deserialized.TopP);

0 commit comments

Comments
 (0)