Skip to content

Commit ababd61

Browse files
committed
Address feedback
1 parent 47d3d76 commit ababd61

File tree

9 files changed

+164
-89
lines changed

9 files changed

+164
-89
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public string Text
8585
{
8686
0 => string.Empty,
8787
1 => messages[0].Text,
88-
_ => messages.SelectMany(m => m.Contents).ConcatText(),
88+
_ => string.Join(Environment.NewLine, messages.Select(m => m.Text).Where(s => !string.IsNullOrEmpty(s))),
8989
};
9090
}
9191
}

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ public interface IChatClient : IDisposable
3232
/// <returns>The response messages generated by the client.</returns>
3333
/// <exception cref="ArgumentNullException"><paramref name="chatMessages"/> is <see langword="null"/>.</exception>
3434
/// <remarks>
35-
/// The response message generated by <see cref="GetResponseAsync"/> is both returned from the method as well as automatically
36-
/// added into <paramref name="chatMessages"/>. Any intermediate messages generated implicitly as part of the interaction are
37-
/// also added to the chat history. For example, if as part of satisfying this request, the <see cref="GetResponseAsync"/> method
35+
/// The response messages generated by <see cref="GetResponseAsync"/> are returned from the method as well as automatically
36+
/// added into <paramref name="chatMessages"/>. This includes any messages generated implicitly as part of the interaction.
37+
/// For example, if as part of satisfying this request, the <see cref="GetResponseAsync"/> method
3838
/// itself issues multiple requests to one or more underlying <see cref="IChatClient"/> instances, all of those messages will also
3939
/// be included in <paramref name="chatMessages"/>.
4040
/// </remarks>
@@ -50,9 +50,9 @@ Task<ChatResponse> GetResponseAsync(
5050
/// <returns>The response messages generated by the client.</returns>
5151
/// <exception cref="ArgumentNullException"><paramref name="chatMessages"/> is <see langword="null"/>.</exception>
5252
/// <remarks>
53-
/// The response updates generated by <see cref="GetStreamingResponseAsync"/> are both stream from the method as well as automatically
54-
/// added into <paramref name="chatMessages"/>. Any intermediate messages generated implicitly as part of the interaction are
55-
/// also added to the chat history. For example, if as part of satisfying this request, the <see cref="GetStreamingResponseAsync"/> method
53+
/// The response updates generated by <see cref="GetStreamingResponseAsync"/> are streamed from the method as well as automatically
54+
/// added into <paramref name="chatMessages"/>. This includes any messages generated implicitly as part of the interaction.
55+
/// For example, if as part of satisfying this request, the <see cref="GetStreamingResponseAsync"/> method
5656
/// itself issues multiple requests to one or more underlying <see cref="IChatClient"/> instances, all of those messages will also
5757
/// be included in <paramref name="chatMessages"/>.
5858
/// </remarks>

src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ From the command-line:
1010
dotnet add package Microsoft.Extensions.AI.Abstractions
1111
```
1212

13-
Or directly in the C# project file:
13+
or directly in the C# project file:
1414

1515
```xml
1616
<ItemGroup>

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

+59-45
Original file line numberDiff line numberDiff line change
@@ -112,59 +112,73 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
112112
}
113113

114114
// Process each update.
115-
await foreach (var update in updates.ConfigureAwait(false))
115+
List<ChatResponseUpdate> responseUpdates = [];
116+
try
116117
{
117-
switch (update)
118+
string? responseId = null;
119+
await foreach (var update in updates.ConfigureAwait(false))
118120
{
119-
case MessageContentUpdate mcu:
120-
yield return new(mcu.Role == MessageRole.User ? ChatRole.User : ChatRole.Assistant, mcu.Text)
121-
{
122-
ChatThreadId = threadId,
123-
RawRepresentation = mcu,
124-
};
125-
break;
121+
switch (update)
122+
{
123+
case MessageContentUpdate mcu:
124+
ChatResponseUpdate responseUpdate = new(mcu.Role == MessageRole.User ? ChatRole.User : ChatRole.Assistant, mcu.Text)
125+
{
126+
ChatThreadId = threadId,
127+
RawRepresentation = mcu,
128+
ResponseId = responseId,
129+
};
130+
responseUpdates.Add(responseUpdate);
131+
yield return responseUpdate;
132+
break;
126133

127-
case ThreadUpdate tu when options is not null:
128-
threadId ??= tu.Value.Id;
129-
break;
134+
case ThreadUpdate tu when options is not null:
135+
threadId ??= tu.Value.Id;
136+
break;
130137

131-
case RunUpdate ru:
132-
threadId ??= ru.Value.ThreadId;
138+
case RunUpdate ru:
139+
threadId ??= ru.Value.ThreadId;
140+
responseId ??= ru.Value.Id;
133141

134-
ChatResponseUpdate ruUpdate = new()
135-
{
136-
AuthorName = ru.Value.AssistantId,
137-
ChatThreadId = threadId,
138-
CreatedAt = ru.Value.CreatedAt,
139-
ModelId = ru.Value.Model,
140-
RawRepresentation = ru,
141-
ResponseId = ru.Value.Id,
142-
Role = ChatRole.Assistant,
143-
};
144-
145-
if (ru.Value.Usage is { } usage)
146-
{
147-
ruUpdate.Contents.Add(new UsageContent(new()
142+
ChatResponseUpdate ruUpdate = new()
148143
{
149-
InputTokenCount = usage.InputTokenCount,
150-
OutputTokenCount = usage.OutputTokenCount,
151-
TotalTokenCount = usage.TotalTokenCount,
152-
}));
153-
}
154-
155-
if (ru is RequiredActionUpdate rau && rau.ToolCallId is string toolCallId && rau.FunctionName is string functionName)
156-
{
157-
ruUpdate.Contents.Add(
158-
new FunctionCallContent(
159-
JsonSerializer.Serialize(new[] { ru.Value.Id, toolCallId }, OpenAIJsonContext.Default.StringArray!),
160-
functionName,
161-
JsonSerializer.Deserialize(rau.FunctionArguments, OpenAIJsonContext.Default.IDictionaryStringObject)!));
162-
}
163-
164-
yield return ruUpdate;
165-
break;
144+
AuthorName = ru.Value.AssistantId,
145+
ChatThreadId = threadId,
146+
CreatedAt = ru.Value.CreatedAt,
147+
ModelId = ru.Value.Model,
148+
RawRepresentation = ru,
149+
ResponseId = responseId,
150+
Role = ChatRole.Assistant,
151+
};
152+
153+
if (ru.Value.Usage is { } usage)
154+
{
155+
ruUpdate.Contents.Add(new UsageContent(new()
156+
{
157+
InputTokenCount = usage.InputTokenCount,
158+
OutputTokenCount = usage.OutputTokenCount,
159+
TotalTokenCount = usage.TotalTokenCount,
160+
}));
161+
}
162+
163+
if (ru is RequiredActionUpdate rau && rau.ToolCallId is string toolCallId && rau.FunctionName is string functionName)
164+
{
165+
ruUpdate.Contents.Add(
166+
new FunctionCallContent(
167+
JsonSerializer.Serialize(new[] { ru.Value.Id, toolCallId }, OpenAIJsonContext.Default.StringArray!),
168+
functionName,
169+
JsonSerializer.Deserialize(rau.FunctionArguments, OpenAIJsonContext.Default.IDictionaryStringObject)!));
170+
}
171+
172+
responseUpdates.Add(ruUpdate);
173+
yield return ruUpdate;
174+
break;
175+
}
166176
}
167177
}
178+
finally
179+
{
180+
chatMessages.AddRangeFromUpdates(responseUpdates);
181+
}
168182
}
169183

170184
/// <inheritdoc />

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

+4-10
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,9 @@ public override async Task<ChatResponse> GetResponseAsync(IList<ChatMessage> cha
5656

5757
if (await ReadCacheAsync(cacheKey, cancellationToken).ConfigureAwait(false) is { } result)
5858
{
59-
if (options?.ChatThreadId is null)
59+
foreach (ChatMessage message in result.Messages)
6060
{
61-
foreach (ChatMessage message in result.Messages)
62-
{
63-
chatMessages.Add(message);
64-
}
61+
chatMessages.Add(message);
6562
}
6663
}
6764
else
@@ -94,12 +91,9 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
9491
yield return chunk;
9592
}
9693

97-
if (chatResponse.ChatThreadId is null)
94+
foreach (ChatMessage message in chatResponse.Messages)
9895
{
99-
foreach (ChatMessage message in chatResponse.Messages)
100-
{
101-
chatMessages.Add(message);
102-
}
96+
chatMessages.Add(message);
10397
}
10498
}
10599
else

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

-8
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ public static class ChatClientStructuredOutputExtensions
3636
/// </param>
3737
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
3838
/// <returns>The response messages generated by the client.</returns>
39-
/// <remarks>
40-
/// The returned messages will not have been added to <paramref name="chatMessages"/>. However, any intermediate messages generated implicitly
41-
/// by the client, including any messages for roundtrips to the model as part of the implementation of this request, will be included.
42-
/// </remarks>
4339
/// <typeparam name="T">The type of structured output to request.</typeparam>
4440
public static Task<ChatResponse<T>> GetResponseAsync<T>(
4541
this IChatClient chatClient,
@@ -145,10 +141,6 @@ public static Task<ChatResponse<T>> GetResponseAsync<T>(
145141
/// </param>
146142
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
147143
/// <returns>The response messages generated by the client.</returns>
148-
/// <remarks>
149-
/// The returned messages will not have been added to <paramref name="chatMessages"/>. However, any intermediate messages generated implicitly
150-
/// by the client, including any messages for roundtrips to the model as part of the implementation of this request, will be included.
151-
/// </remarks>
152144
/// <typeparam name="T">The type of structured output to request.</typeparam>
153145
/// <exception cref="ArgumentNullException"><paramref name="chatClient"/> is <see langword="null"/>.</exception>
154146
/// <exception cref="ArgumentNullException"><paramref name="chatMessages"/> is <see langword="null"/>.</exception>

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

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public IList<ChatMessage> ChatMessages
4545
set => _chatMessages = Throw.IfNull(value);
4646
}
4747

48+
/// <summary>Gets or sets the chat options associated with the operation that initiated this function call request.</summary>
49+
public ChatOptions? Options { get; set; }
50+
4851
/// <summary>Gets or sets the AI function to be invoked.</summary>
4952
public AIFunction Function
5053
{

0 commit comments

Comments
 (0)