Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ public ChatMessage Message
/// <summary>Gets or sets the ID of the chat completion.</summary>
public string? CompletionId { get; set; }

/// <summary>Gets or sets the chat thread ID associated with this chat completion.</summary>
/// <remarks>
/// Some <see cref="IChatClient"/> implementations are capable of storing the state for a chat thread, such that
/// the input messages supplied to <see cref="IChatClient.CompleteAsync"/> need only be the additional messages beyond
/// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
/// and it should be used in a subsequent <see cref="ChatOptions.ChatThreadId"/> instead of supplying the same messages
/// (and this <see cref="ChatCompletion"/>'s message) as part of the <c>chatMessages</c> parameter.
/// </remarks>
public string? ChatThreadId { get; set; }

/// <summary>Gets or sets the model ID used in the creation of the chat completion.</summary>
public string? ModelId { get; set; }

Expand Down Expand Up @@ -133,6 +143,7 @@ public StreamingChatCompletionUpdate[] ToStreamingChatCompletionUpdates()
ChatMessage choice = Choices[choiceIndex];
updates[choiceIndex] = new StreamingChatCompletionUpdate
{
ChatThreadId = ChatThreadId,
ChoiceIndex = choiceIndex,

AdditionalProperties = choice.AdditionalProperties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ public ChatMessage(
_contents = Throw.IfNull(contents);
}

/// <summary>Clones the <see cref="ChatMessage"/> to a new <see cref="ChatMessage"/> instance.</summary>
/// <returns>A shallow clone of the original message object.</returns>
/// <remarks>
/// This is a shallow clone. The returned instance is different from the original, but all properties
/// refer to the same objects as the original.
/// </remarks>
public ChatMessage Clone() =>
new()
{
AdditionalProperties = AdditionalProperties,
_authorName = _authorName,
_contents = _contents,
RawRepresentation = RawRepresentation,
Role = Role,
};

/// <summary>Gets or sets the name of the author of the message.</summary>
public string? AuthorName
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ namespace Microsoft.Extensions.AI;
/// <summary>Represents the options for a chat request.</summary>
public class ChatOptions
{
/// <summary>Gets or sets an optional identifier used to associate a request with an existing chat thread.</summary>
public string? ChatThreadId { get; set; }

/// <summary>Gets or sets the temperature for generating chat responses.</summary>
public float? Temperature { get; set; }

Expand Down Expand Up @@ -72,6 +75,7 @@ public virtual ChatOptions Clone()
{
ChatOptions options = new()
{
ChatThreadId = ChatThreadId,
Temperature = Temperature,
MaxOutputTokens = MaxOutputTokens,
TopP = TopP,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ public IList<AIContent> Contents
/// <summary>Gets or sets the ID of the completion of which this update is a part.</summary>
public string? CompletionId { get; set; }

/// <summary>Gets or sets the chat thread ID associated with the chat completion of which this update is a part.</summary>
/// <remarks>
/// Some <see cref="IChatClient"/> implementations are capable of storing the state for a chat thread, such that
/// the input messages supplied to <see cref="IChatClient.CompleteStreamingAsync"/> need only be the additional messages beyond
/// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
/// and it should be used in a subsequent <see cref="ChatOptions.ChatThreadId"/> instead of supplying the same messages
/// (and this streaming message) as part of the <c>chatMessages</c> parameter.
/// </remarks>
public string? ChatThreadId { get; set; }

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#pragma warning disable S109 // Magic numbers should not be used
#pragma warning disable S127 // "for" loop stop conditions should be invariant
#pragma warning disable S1121 // Assignments should not be made from within sub-expressions

namespace Microsoft.Extensions.AI;

Expand Down Expand Up @@ -103,7 +104,21 @@ private static void ProcessUpdate(StreamingChatCompletionUpdate update, Dictiona
}
#endif

((List<AIContent>)message.Contents).AddRange(update.Contents);
// Incorporate all content from the update into the completion.
foreach (var content in update.Contents)
{
switch (content)
{
// Usage content is treated specially and propagated to the completion's Usage.
case UsageContent usage:
(completion.Usage ??= new()).Add(usage.Details);
break;

default:
message.Contents.Add(content);
break;
}
}

message.AuthorName ??= update.AuthorName;
if (update.Role is ChatRole role && message.Role == default)
Expand Down Expand Up @@ -178,20 +193,6 @@ static void AddMessage(ChatCompletion completion, bool coalesceContent, KeyValue
}

completion.Choices.Add(entry.Value);

if (completion.Usage is null)
{
foreach (var content in entry.Value.Contents)
{
if (content is UsageContent c)
{
completion.Usage = c.Details;
entry.Value.Contents = entry.Value.Contents.ToList();
_ = entry.Value.Contents.Remove(c);
break;
}
}
}
}
}

Expand Down
Loading