I want to implement a custom ChatHistoryProvider. How can I get the ConversationId through the Session property of the context, when the current ConversationId value is null? #7501
Replies: 1 comment
|
A null
If no service is holding the conversation, nothing has assigned it an id — so there's none for the framework to hand you. Your provider is the thing that owns conversation identity; it isn't a consumer of it. The built-in providers all solve this the same way: take the identity as a constructor delegate and let the session persist it. public CosmosChatHistoryProvider(
CosmosClient cosmosClient,
string databaseId,
string containerId,
Func<AgentSession?, State> stateInitializer, // conversationId / tenantId / userId come from here
...)
{
this._sessionState = new ProviderSessionState<State>(
Throw.IfNull(stateInitializer),
stateKey ?? this.GetType().Name);and then on each call: var state = this._sessionState.GetOrInitializeState(context.Session);
var partitionKey = BuildPartitionKey(state);
public TState GetOrInitializeState(AgentSession? session)
{
if (session?.StateBag.TryGetValue<TState>(this.StateKey, out var state, this._jsonSerializerOptions) is true && state is not null)
{
return state;
}
state = this._stateInitializer(session);
if (session is not null)
{
session.StateBag.SetValue(this.StateKey, state, this._jsonSerializerOptions);
}
return state;
}Applied to a custom provider: public sealed class MyChatHistoryProvider : ChatHistoryProvider
{
public sealed class State
{
public string ConversationId { get; set; } = string.Empty;
}
private readonly ProviderSessionState<State> _sessionState;
public MyChatHistoryProvider(Func<AgentSession?, State> stateInitializer, string? stateKey = null)
=> this._sessionState = new ProviderSessionState<State>(
stateInitializer,
stateKey ?? nameof(MyChatHistoryProvider));
public override IReadOnlyList<string> StateKeys => [this._sessionState.StateKey];
protected override ValueTask<IEnumerable<ChatMessage>> ProvideChatHistoryAsync(
InvokingContext context, CancellationToken cancellationToken = default)
{
State state = this._sessionState.GetOrInitializeState(context.Session);
// state.ConversationId is stable for the life of the session
...
}
protected override ValueTask StoreChatHistoryAsync(
InvokedContext context, CancellationToken cancellationToken = default)
{
State state = this._sessionState.GetOrInitializeState(context.Session);
...
}
}wired with whatever id your application already owns: new MyChatHistoryProvider(_ => new MyChatHistoryProvider.State
{
ConversationId = myConversationId
});If you want the id generated on first use instead of supplied, put that in the initializer — Two details that will bite otherwise:
Note that the overrides to implement are |
Uh oh!
There was an error while loading. Please reload this page.
I want to implement a custom ChatHistoryProvider. How can I get the ConversationId through the Session property of the context, when the current ConversationId value is null?
All reactions