mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Porting Mem0Provider to Agent Framework (#1601)
* Porting Mem0Provider to AF from SK * Switch integration tests to manual * Address issues * Move Mem0Provider to separate project. * Move integration tests to new project * Address PR comments.
This commit is contained in:
committed by
GitHub
Unverified
parent
8408209c70
commit
742203fb12
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Mem0;
|
||||
|
||||
/// <summary>
|
||||
/// Client for the Mem0 memory service.
|
||||
/// </summary>
|
||||
internal sealed class Mem0Client
|
||||
{
|
||||
private static readonly Uri s_searchUri = new("/v1/memories/search/", UriKind.Relative);
|
||||
private static readonly Uri s_createMemoryUri = new("/v1/memories/", UriKind.Relative);
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mem0Client"/> class.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">Configured <see cref="HttpClient"/> pointing at the Mem0 service (base address + auth headers).</param>
|
||||
public Mem0Client(HttpClient httpClient)
|
||||
{
|
||||
this._httpClient = Throw.IfNull(httpClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for memories related to an input query.
|
||||
/// </summary>
|
||||
/// <param name="applicationId">Optional application scope.</param>
|
||||
/// <param name="agentId">Optional agent scope.</param>
|
||||
/// <param name="threadId">Optional thread scope.</param>
|
||||
/// <param name="userId">Optional user scope.</param>
|
||||
/// <param name="inputText">Query text.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Enumerable of memory strings.</returns>
|
||||
public async Task<IEnumerable<string>> SearchAsync(string? applicationId, string? agentId, string? threadId, string? userId, string? inputText, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(applicationId)
|
||||
&& string.IsNullOrWhiteSpace(agentId)
|
||||
&& string.IsNullOrWhiteSpace(threadId)
|
||||
&& string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
throw new ArgumentException("At least one of applicationId, agentId, threadId, or userId must be provided.");
|
||||
}
|
||||
|
||||
var searchRequest = new SearchRequest
|
||||
{
|
||||
AppId = applicationId,
|
||||
AgentId = agentId,
|
||||
RunId = threadId,
|
||||
UserId = userId,
|
||||
Query = inputText ?? string.Empty
|
||||
};
|
||||
|
||||
using var content = new StringContent(JsonSerializer.Serialize(searchRequest, Mem0SourceGenerationContext.Default.SearchRequest), Encoding.UTF8, "application/json");
|
||||
using var responseMessage = await this._httpClient.PostAsync(s_searchUri, content, cancellationToken).ConfigureAwait(false);
|
||||
responseMessage.EnsureSuccessStatusCode();
|
||||
|
||||
#if NET
|
||||
var response = await responseMessage.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
var response = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
#endif
|
||||
var searchResponseItems = JsonSerializer.Deserialize(response, Mem0SourceGenerationContext.Default.SearchResponseItemArray);
|
||||
return searchResponseItems?.Select(item => item.Memory) ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a memory for the provided message content.
|
||||
/// </summary>
|
||||
public async Task CreateMemoryAsync(string? applicationId, string? agentId, string? threadId, string? userId, string messageContent, string messageRole, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(applicationId)
|
||||
&& string.IsNullOrWhiteSpace(agentId)
|
||||
&& string.IsNullOrWhiteSpace(threadId)
|
||||
&& string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
throw new ArgumentException("At least one of applicationId, agentId, threadId, or userId must be provided.");
|
||||
}
|
||||
|
||||
#pragma warning disable CA1308 // Lowercase required by service
|
||||
var createMemoryRequest = new CreateMemoryRequest
|
||||
{
|
||||
AppId = applicationId,
|
||||
AgentId = agentId,
|
||||
RunId = threadId,
|
||||
UserId = userId,
|
||||
Messages = new[]
|
||||
{
|
||||
new CreateMemoryMessage
|
||||
{
|
||||
Content = messageContent,
|
||||
Role = messageRole.ToLowerInvariant()
|
||||
}
|
||||
}
|
||||
};
|
||||
#pragma warning restore CA1308
|
||||
|
||||
using var content = new StringContent(JsonSerializer.Serialize(createMemoryRequest, Mem0SourceGenerationContext.Default.CreateMemoryRequest), Encoding.UTF8, "application/json");
|
||||
using var responseMessage = await this._httpClient.PostAsync(s_createMemoryUri, content, cancellationToken).ConfigureAwait(false);
|
||||
responseMessage.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears memories for the provided scope combination.
|
||||
/// </summary>
|
||||
public async Task ClearMemoryAsync(string? applicationId, string? agentId, string? threadId, string? userId, CancellationToken cancellationToken)
|
||||
{
|
||||
string[] paramNames = ["app_id", "agent_id", "run_id", "user_id"];
|
||||
|
||||
var querystringParams = new string?[4] { applicationId, agentId, threadId, userId }
|
||||
.Select((param, index) => string.IsNullOrWhiteSpace(param) ? null : $"{paramNames[index]}={param}")
|
||||
.Where(x => x is not null);
|
||||
var queryString = string.Join("&", querystringParams);
|
||||
var clearMemoryUrl = new Uri($"/v1/memories/?{queryString}", UriKind.Relative);
|
||||
|
||||
using var responseMessage = await this._httpClient.DeleteAsync(clearMemoryUrl, cancellationToken).ConfigureAwait(false);
|
||||
responseMessage.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
internal sealed class CreateMemoryRequest
|
||||
{
|
||||
[JsonPropertyName("app_id")] public string? AppId { get; set; }
|
||||
[JsonPropertyName("agent_id")] public string? AgentId { get; set; }
|
||||
[JsonPropertyName("run_id")] public string? RunId { get; set; }
|
||||
[JsonPropertyName("user_id")] public string? UserId { get; set; }
|
||||
[JsonPropertyName("messages")] public CreateMemoryMessage[] Messages { get; set; } = Array.Empty<CreateMemoryMessage>();
|
||||
}
|
||||
|
||||
internal sealed class CreateMemoryMessage
|
||||
{
|
||||
[JsonPropertyName("content")] public string Content { get; set; } = string.Empty;
|
||||
[JsonPropertyName("role")] public string Role { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
internal sealed class SearchRequest
|
||||
{
|
||||
[JsonPropertyName("app_id")] public string? AppId { get; set; }
|
||||
[JsonPropertyName("agent_id")] public string? AgentId { get; set; }
|
||||
[JsonPropertyName("run_id")] public string? RunId { get; set; }
|
||||
[JsonPropertyName("user_id")] public string? UserId { get; set; }
|
||||
[JsonPropertyName("query")] public string Query { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
internal sealed class SearchResponseItem
|
||||
{
|
||||
[JsonPropertyName("id")] public string Id { get; set; } = string.Empty;
|
||||
[JsonPropertyName("memory")] public string Memory { get; set; } = string.Empty;
|
||||
[JsonPropertyName("hash")] public string Hash { get; set; } = string.Empty;
|
||||
[JsonPropertyName("metadata")] public object? Metadata { get; set; }
|
||||
[JsonPropertyName("score")] public double Score { get; set; }
|
||||
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
|
||||
[JsonPropertyName("updated_at")] public DateTime? UpdatedAt { get; set; }
|
||||
[JsonPropertyName("user_id")] public string UserId { get; set; } = string.Empty;
|
||||
[JsonPropertyName("app_id")] public string? AppId { get; set; }
|
||||
[JsonPropertyName("agent_id")] public string AgentId { get; set; } = string.Empty;
|
||||
[JsonPropertyName("session_id")] public string RunId { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(JsonSerializerDefaults.General,
|
||||
UseStringEnumConverter = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
WriteIndented = false)]
|
||||
[JsonSerializable(typeof(Mem0Client.CreateMemoryRequest))]
|
||||
[JsonSerializable(typeof(Mem0Client.SearchRequest))]
|
||||
[JsonSerializable(typeof(Mem0Client.SearchResponseItem[]))]
|
||||
internal partial class Mem0SourceGenerationContext : JsonSerializerContext;
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Mem0;
|
||||
|
||||
/// <summary>Provides a collection of utility methods for working with JSON data in the context of mem0.</summary>
|
||||
public static partial class Mem0JsonUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="JsonSerializerOptions"/> singleton used as the default in JSON serialization operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For Native AOT or applications disabling <see cref="JsonSerializer.IsReflectionEnabledByDefault"/>, this instance
|
||||
/// includes source generated contracts for all common exchange types contained in this library.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It additionally turns on the following settings:
|
||||
/// <list type="number">
|
||||
/// <item>Enables <see cref="JsonSerializerDefaults.Web"/> defaults.</item>
|
||||
/// <item>Enables <see cref="JsonIgnoreCondition.WhenWritingNull"/> as the default ignore condition for properties.</item>
|
||||
/// <item>Enables <see cref="JsonNumberHandling.AllowReadingFromString"/> as the default number handling for number types.</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Creates default options to use for agents-related serialization.
|
||||
/// </summary>
|
||||
/// <returns>The configured options.</returns>
|
||||
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
|
||||
private static JsonSerializerOptions CreateDefaultOptions()
|
||||
{
|
||||
// Copy the configuration from the source generated context.
|
||||
JsonSerializerOptions options = new(JsonContext.Default.Options)
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as in AIJsonUtilities
|
||||
};
|
||||
|
||||
// Chain with all supported types from Microsoft.Extensions.AI.Abstractions.
|
||||
options.TypeInfoResolverChain.Add(AIJsonUtilities.DefaultOptions.TypeInfoResolver!);
|
||||
if (JsonSerializer.IsReflectionEnabledByDefault)
|
||||
{
|
||||
options.Converters.Add(new JsonStringEnumConverter());
|
||||
}
|
||||
|
||||
options.MakeReadOnly();
|
||||
return options;
|
||||
}
|
||||
|
||||
// Keep in sync with CreateDefaultOptions above.
|
||||
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
|
||||
UseStringEnumConverter = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString)]
|
||||
|
||||
// Agent abstraction types
|
||||
[JsonSerializable(typeof(Mem0Provider.Mem0State))]
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal sealed partial class JsonContext : JsonSerializerContext;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Mem0;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a Mem0 backed <see cref="AIContextProvider"/> that persists conversation messages as memories
|
||||
/// and retrieves related memories to augment the agent invocation context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The provider stores user, assistant and system messages as Mem0 memories and retrieves relevant memories
|
||||
/// for new invocations using a semantic search endpoint. Retrieved memories are injected as user messages
|
||||
/// to the model, prefixed by a configurable context prompt.
|
||||
/// </remarks>
|
||||
public sealed class Mem0Provider : AIContextProvider
|
||||
{
|
||||
private const string DefaultContextPrompt = "## Memories\nConsider the following memories when answering user questions:";
|
||||
|
||||
private readonly string _contextPrompt;
|
||||
|
||||
private readonly Mem0Client _client;
|
||||
private readonly ILogger<Mem0Provider>? _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mem0Provider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">Configured <see cref="HttpClient"/> (base address + auth).</param>
|
||||
/// <param name="options">Provider options.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory.</param>
|
||||
/// <remarks>
|
||||
/// The base address of the required mem0 service, and any authentication headers, should be set on the <paramref name="httpClient"/>
|
||||
/// already, when passed as a parameter here. E.g.:
|
||||
/// <code>
|
||||
/// using var httpClient = new HttpClient();
|
||||
/// httpClient.BaseAddress = new Uri("https://api.mem0.ai");
|
||||
/// httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "<Your APIKey>");
|
||||
/// new Mem0AIContextProvider(httpClient);
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public Mem0Provider(HttpClient httpClient, Mem0ProviderOptions? options = null, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(httpClient.BaseAddress?.AbsoluteUri))
|
||||
{
|
||||
throw new ArgumentException("The HttpClient BaseAddress must be set for Mem0 operations.", nameof(httpClient));
|
||||
}
|
||||
|
||||
this.ApplicationId = options?.ApplicationId;
|
||||
this.AgentId = options?.AgentId;
|
||||
this.ThreadId = options?.ThreadId;
|
||||
this.UserId = options?.UserId;
|
||||
this._contextPrompt = options?.ContextPrompt ?? DefaultContextPrompt;
|
||||
|
||||
this._logger = loggerFactory?.CreateLogger<Mem0Provider>();
|
||||
this._client = new Mem0Client(httpClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mem0Provider"/> class, with existing state from a serialized JSON element.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">Configured <see cref="HttpClient"/> (base address + auth).</param>
|
||||
/// <param name="serializedState">A <see cref="JsonElement"/> representing the serialized state of the store.</param>
|
||||
/// <param name="jsonSerializerOptions">Optional settings for customizing the JSON deserialization process.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory.</param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <remarks>
|
||||
/// The base address of the required mem0 service, and any authentication headers, should be set on the <paramref name="httpClient"/>
|
||||
/// already, when passed as a parameter here. E.g.:
|
||||
/// <code>
|
||||
/// using var httpClient = new HttpClient();
|
||||
/// httpClient.BaseAddress = new Uri("https://api.mem0.ai");
|
||||
/// httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "<Your APIKey>");
|
||||
/// new Mem0AIContextProvider(httpClient, state);
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public Mem0Provider(HttpClient httpClient, JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(httpClient.BaseAddress?.AbsoluteUri))
|
||||
{
|
||||
throw new ArgumentException("The HttpClient BaseAddress must be set for Mem0 operations.", nameof(httpClient));
|
||||
}
|
||||
|
||||
var jso = jsonSerializerOptions ?? Mem0JsonUtilities.DefaultOptions;
|
||||
var state = serializedState.Deserialize(jso.GetTypeInfo(typeof(Mem0State))) as Mem0State;
|
||||
|
||||
this.ApplicationId = state?.ApplicationId;
|
||||
this.AgentId = state?.AgentId;
|
||||
this.ThreadId = state?.ThreadId;
|
||||
this.UserId = state?.UserId;
|
||||
this._contextPrompt = state?.ContextPrompt ?? DefaultContextPrompt;
|
||||
|
||||
this._logger = loggerFactory?.CreateLogger<Mem0Provider>();
|
||||
this._client = new Mem0Client(httpClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the application to scope memories to.
|
||||
/// </summary>
|
||||
public string? ApplicationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the agent to scope memories to.
|
||||
/// </summary>
|
||||
public string? AgentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the thread to scope memories to.
|
||||
/// </summary>
|
||||
public string? ThreadId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the user to scope memories to.
|
||||
/// </summary>
|
||||
public string? UserId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask<AIContext> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(context);
|
||||
|
||||
string queryText = string.Join(
|
||||
Environment.NewLine,
|
||||
context.RequestMessages.Where(m => !string.IsNullOrWhiteSpace(m.Text)).Select(m => m.Text));
|
||||
|
||||
var memories = (await this._client.SearchAsync(
|
||||
this.ApplicationId,
|
||||
this.AgentId,
|
||||
this.ThreadId,
|
||||
this.UserId,
|
||||
queryText,
|
||||
cancellationToken).ConfigureAwait(false)).ToList();
|
||||
|
||||
var contextInstructions = memories.Count == 0
|
||||
? null
|
||||
: $"{this._contextPrompt}\n{string.Join(Environment.NewLine, memories)}";
|
||||
|
||||
if (this._logger is not null)
|
||||
{
|
||||
this._logger.LogInformation("Mem0AIContextProvider retrieved {Count} memories.", memories.Count);
|
||||
if (contextInstructions is not null)
|
||||
{
|
||||
this._logger.LogTrace("Mem0AIContextProvider instructions: {Instructions}", contextInstructions);
|
||||
}
|
||||
}
|
||||
|
||||
return new AIContext
|
||||
{
|
||||
Messages = [new ChatMessage(ChatRole.User, contextInstructions)]
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask InvokedAsync(InvokedContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Persist request and response messages after invocation.
|
||||
await this.PersistMessagesAsync(context.RequestMessages, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (context.ResponseMessages is not null)
|
||||
{
|
||||
await this.PersistMessagesAsync(context.ResponseMessages, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears stored memories for the configured scopes.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
public Task ClearStoredMemoriesAsync(CancellationToken cancellationToken = default) =>
|
||||
this._client.ClearMemoryAsync(
|
||||
this.ApplicationId,
|
||||
this.AgentId,
|
||||
this.ThreadId,
|
||||
this.UserId,
|
||||
cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
|
||||
{
|
||||
var state = new Mem0State
|
||||
{
|
||||
ApplicationId = this.ApplicationId,
|
||||
AgentId = this.AgentId,
|
||||
ThreadId = this.ThreadId,
|
||||
UserId = this.UserId,
|
||||
ContextPrompt = this._contextPrompt == DefaultContextPrompt ? null : this._contextPrompt
|
||||
};
|
||||
|
||||
var jso = jsonSerializerOptions ?? Mem0JsonUtilities.DefaultOptions;
|
||||
return JsonSerializer.SerializeToElement(state, jso.GetTypeInfo(typeof(Mem0State)));
|
||||
}
|
||||
|
||||
private async Task PersistMessagesAsync(IEnumerable<ChatMessage> messages, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var message in messages)
|
||||
{
|
||||
switch (message.Role)
|
||||
{
|
||||
case ChatRole u when u == ChatRole.User:
|
||||
case ChatRole a when a == ChatRole.Assistant:
|
||||
case ChatRole s when s == ChatRole.System:
|
||||
break;
|
||||
default:
|
||||
continue; // ignore other roles
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message.Text))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await this._client.CreateMemoryAsync(
|
||||
this.ApplicationId,
|
||||
this.AgentId,
|
||||
this.ThreadId,
|
||||
this.UserId,
|
||||
message.Text,
|
||||
message.Role.Value,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Mem0State
|
||||
{
|
||||
public string? ApplicationId { get; set; }
|
||||
public string? AgentId { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? ThreadId { get; set; }
|
||||
public string? ContextPrompt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Microsoft.Agents.AI.Mem0;
|
||||
|
||||
/// <summary>
|
||||
/// Options for configuring the <see cref="Mem0Provider"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Mem0 memories can be scoped by one or more of: application, agent, thread, and user.
|
||||
/// At least one scope must be provided; otherwise Mem0 will reject requests.
|
||||
/// </remarks>
|
||||
public sealed class Mem0ProviderOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the application to scope memories to.
|
||||
/// </summary>
|
||||
/// <remarks>If not set, the scope of the memories will span all applications.</remarks>
|
||||
public string? ApplicationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the agent to scope memories to.
|
||||
/// </summary>
|
||||
/// <remarks>If not set, the scope of the memories will span all agents.</remarks>
|
||||
public string? AgentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the thread to scope memories to.
|
||||
/// </summary>
|
||||
public string? ThreadId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional ID for the user to scope memories to.
|
||||
/// </summary>
|
||||
/// <remarks>If not set, the scope of the memories will span all users.</remarks>
|
||||
public string? UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When providing memories to the model, this string is prefixed to the retrieved memories to supply context.
|
||||
/// </summary>
|
||||
/// <value>Defaults to "## Memories\nConsider the following memories when answering user questions:".</value>
|
||||
public string? ContextPrompt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||
<VersionSuffix>preview</VersionSuffix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Agents.AI.Abstractions\Microsoft.Agents.AI.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- NuGet Package Settings -->
|
||||
<Title>Microsoft Agent Framework - Mem0 integration</Title>
|
||||
<Description>Provides Mem0 integration for Microsoft Agent Framework.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.Mem0.UnitTests" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -43,7 +43,7 @@ internal static partial class AgentJsonUtilities
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as in AgentAbstractionsJsonUtilities and AIJsonUtilities
|
||||
};
|
||||
|
||||
// Chain with all supported types from Microsoft.Extensions.AI.Abstractions.
|
||||
// Chain with all supported types from Microsoft.Agents.AI.Abstractions.
|
||||
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
|
||||
if (JsonSerializer.IsReflectionEnabledByDefault)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Shared.IntegrationTests;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
#pragma warning disable CA1812 // Internal class that is apparently never instantiated.
|
||||
|
||||
internal sealed class Mem0Configuration
|
||||
{
|
||||
public string ServiceUri { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user