Compare commits

...
Author SHA1 Message Date
Korolev DmitryandGitHub 4072c66b5f Merge branch 'main' into dmkorolev/threads 2025-11-19 12:58:45 +01:00
Korolev Dmitry 0c02824853 rm 2025-11-17 21:53:35 +01:00
Korolev Dmitry 836d22b205 comments 2025-11-17 21:51:52 +01:00
Korolev Dmitry 19b6f3a5d9 unblock 2025-11-17 21:50:47 +01:00
Korolev Dmitry 3b80c9e50d via agent thread store 2025-11-17 19:03:21 +01:00
3 changed files with 26 additions and 3 deletions
@@ -34,6 +34,7 @@ internal sealed partial class IdGenerator
this._random = randomSeed.HasValue ? new Random(randomSeed.Value) : null; this._random = randomSeed.HasValue ? new Random(randomSeed.Value) : null;
this.ResponseId = responseId ?? NewId("resp", random: this._random); this.ResponseId = responseId ?? NewId("resp", random: this._random);
this.ConversationId = conversationId ?? NewId("conv", random: this._random); this.ConversationId = conversationId ?? NewId("conv", random: this._random);
this.IsNewConversation = conversationId is null;
this._partitionId = GetPartitionIdOrDefault(this.ConversationId) ?? string.Empty; this._partitionId = GetPartitionIdOrDefault(this.ConversationId) ?? string.Empty;
} }
@@ -59,6 +60,11 @@ internal sealed partial class IdGenerator
/// </summary> /// </summary>
public string ConversationId { get; } public string ConversationId { get; }
/// <summary>
/// Gets a value indicating whether this is a new conversation.
/// </summary>
public bool IsNewConversation { get; }
/// <summary> /// <summary>
/// Generates a new ID. /// Generates a new ID.
/// </summary> /// </summary>
@@ -26,6 +26,11 @@ internal sealed class AgentInvocationContext(IdGenerator idGenerator, JsonSerial
/// </summary> /// </summary>
public string ConversationId => this.IdGenerator.ConversationId; public string ConversationId => this.IdGenerator.ConversationId;
/// <summary>
/// Returns true, if conversation is new.
/// </summary>
public bool IsNewConversation => this.IdGenerator.IsNewConversation;
/// <summary> /// <summary>
/// Gets the JSON serializer options. /// Gets the JSON serializer options.
/// </summary> /// </summary>
@@ -81,10 +81,13 @@ internal sealed class HostedAgentResponseExecutor : IResponseExecutor
[EnumeratorCancellation] CancellationToken cancellationToken = default) [EnumeratorCancellation] CancellationToken cancellationToken = default)
{ {
string agentName = GetAgentName(request)!; string agentName = GetAgentName(request)!;
AIAgent agent = this._serviceProvider.GetRequiredKeyedService<AIAgent>(agentName); string conversationId = context.ConversationId;
var agent = this._serviceProvider.GetRequiredKeyedService<AIAgent>(agentName);
var threadStore = this._serviceProvider.GetKeyedService<AgentThreadStore>(agent.Name);
var chatOptions = new ChatOptions var chatOptions = new ChatOptions
{ {
ConversationId = request.Conversation?.Id,
Temperature = (float?)request.Temperature, Temperature = (float?)request.Temperature,
TopP = (float?)request.TopP, TopP = (float?)request.TopP,
MaxOutputTokens = request.MaxOutputTokens, MaxOutputTokens = request.MaxOutputTokens,
@@ -94,16 +97,25 @@ internal sealed class HostedAgentResponseExecutor : IResponseExecutor
var options = new ChatClientAgentRunOptions(chatOptions); var options = new ChatClientAgentRunOptions(chatOptions);
var messages = new List<ChatMessage>(); var messages = new List<ChatMessage>();
AgentThread thread = !context.IsNewConversation && threadStore is not null
? await threadStore.GetThreadAsync(agent, conversationId, cancellationToken).ConfigureAwait(false)
: agent.GetNewThread();
foreach (var inputMessage in request.Input.GetInputMessages()) foreach (var inputMessage in request.Input.GetInputMessages())
{ {
messages.Add(inputMessage.ToChatMessage()); messages.Add(inputMessage.ToChatMessage());
} }
await foreach (var streamingEvent in agent.RunStreamingAsync(messages, options: options, cancellationToken: cancellationToken) await foreach (var streamingEvent in agent.RunStreamingAsync(messages, thread, options: options, cancellationToken: cancellationToken)
.ToStreamingResponseAsync(request, context, cancellationToken).ConfigureAwait(false)) .ToStreamingResponseAsync(request, context, cancellationToken).ConfigureAwait(false))
{ {
yield return streamingEvent; yield return streamingEvent;
} }
if (threadStore is not null && thread is not null)
{
await threadStore.SaveThreadAsync(agent, conversationId, thread, cancellationToken).ConfigureAwait(false);
}
} }
/// <summary> /// <summary>