Skip to content

Commit 5588c16

Browse files
authored
Add User Memory Feature (#390)
1 parent 517b748 commit 5588c16

File tree

109 files changed

+4285
-80
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+4285
-80
lines changed

.github/copilot-instructions.md

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,17 @@ If CloudSmith is inaccessible, only asset builds and code analysis are possible.
276276
- **Using statements**:
277277
- Sort System directives first
278278
- Prefer simple using statements over braces when possible
279+
- Remove unused usings from the code you touch
279280

280281
#### Code Preferences
281282
- **Range/Index operators**: Avoid using range/index operators (enforced as warning)
282283
- **Code Analysis**: `AnalysisLevel` is set to `latest-Recommended`
283284
- **Implicit usings**: Enabled globally
285+
- **Database IDs**: Use `IdGenerator.GenerateId()` when creating database IDs manually. Generated IDs are always 26 characters long.
284286
- **Date/time**: Never use `DateTime.UtcNow`. Always inject `IClock` in the constructor (e.g., `IClock clock`) and store it as `private readonly IClock _clock = clock;`, then call `_clock.UtcNow` in methods
285287
- **Localization extraction**: When using `ILocalizer`, the property/variable must be named `S`, and localized strings must use the literal pattern `S["This is a localized string"]`. Do not use variables inside the brackets because extraction tooling looks specifically for `S["..."]`.
286288
- **One type per file**: Every public type must live in its own file. The file name must always match the type name (e.g., `MyService.cs` for `class MyService`)
289+
- **Global usings**: Do not add `GlobalUsings.cs` files or new global using directives; prefer explicit file-level usings
287290
- **sealed classes**: Seal all classes by default (`sealed class`), **except** ViewModel classes that are consumed by any Orchard Core display driver — those must remain unsealed because the framework creates runtime proxies for them and proxies cannot be created from sealed types
288291

289292
### Module Structure Conventions
@@ -524,20 +527,20 @@ services.AddNavigationProvider<MyAdminMenu>();
524527

525528
## Anti-Patterns to Avoid
526529

527-
- Don't use static mutable state
528-
- Don't create tight coupling between modules
529-
- Don't bypass Orchard Core's dependency injection
530-
- Don't hardcode connection strings or secrets
531-
- Don't use synchronous I/O operations (use async/await)
532-
- Don't ignore compiler warnings (TreatWarningsAsErrors is enabled) — fix all warnings in the entire project, not just changed files
533-
- Don't skip writing tests for new features
534-
- Don't commit commented-out code
535-
- Don't use `System.Range` or `System.Index` operators (enforced as warning)
536-
- Don't leave unused services injected through dependency injection
537-
- Don't leave unused `using` statements in source files
538-
- Don't use `DateTime.UtcNow` — inject `IClock` and use `_clock.UtcNow` instead
539-
- Don't seal ViewModel classes that are used by any Orchard Core display driver — the framework requires unsealed types to generate runtime proxies
540-
- Don't put multiple public types in a single file — each public type must be in its own file whose name matches the type name
530+
- Don't use static mutable state
531+
- Don't create tight coupling between modules
532+
- Don't bypass Orchard Core's dependency injection
533+
- Don't hardcode connection strings or secrets
534+
- Don't use synchronous I/O operations (use async/await)
535+
- Don't ignore compiler warnings (TreatWarningsAsErrors is enabled) — fix all warnings in the entire project, not just changed files
536+
- Don't skip writing tests for new features
537+
- Don't commit commented-out code
538+
- Don't use `System.Range` or `System.Index` operators (enforced as warning)
539+
- Don't leave unused services injected through dependency injection
540+
- Don't leave unused `using` statements in source files
541+
- Don't use `DateTime.UtcNow` — inject `IClock` and use `_clock.UtcNow` instead
542+
- Don't seal ViewModel classes that are used by any Orchard Core display driver — the framework requires unsealed types to generate runtime proxies
543+
- Don't put multiple public types in a single file — each public type must be in its own file whose name matches the type name
541544

542545
## Code Cleanup (Required After Completing Work)
543546

CrestApps.OrchardCore.slnx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Documents.OpenXml/CrestApps.OrchardCore.AI.Documents.OpenXml.csproj" />
4646
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Documents.Pdf/CrestApps.OrchardCore.AI.Documents.Pdf.csproj" />
4747
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Documents/CrestApps.OrchardCore.AI.Documents.csproj" />
48+
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Memory/CrestApps.OrchardCore.AI.Memory.csproj" />
49+
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Memory.AzureAI/CrestApps.OrchardCore.AI.Memory.AzureAI.csproj" />
50+
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Memory.Elasticsearch/CrestApps.OrchardCore.AI.Memory.Elasticsearch.csproj" />
4851
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Mcp.Resources.Ftp/CrestApps.OrchardCore.AI.Mcp.Resources.Ftp.csproj" />
4952
<Project Path="src/Modules/CrestApps.OrchardCore.AI.Mcp.Resources.Sftp/CrestApps.OrchardCore.AI.Mcp.Resources.Sftp.csproj" />
5053
<Project Path="src/Modules/CrestApps.OrchardCore.AI.A2A/CrestApps.OrchardCore.AI.A2A.csproj" />

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# CrestApps - Orchard Core
22

3-
CrestApps provides a collection of open-source modules designed to enhance **Orchard Core**, a powerful application framework built on **ASP.NET Core**.
3+
CrestApps provides a collection of open-source modules designed to enhance **Orchard Core**, a powerful application framework built on **ASP.NET Core**.
44

55
## 📖 Documentation
66

77
For detailed guides, tutorials, and API references, visit the **[CrestApps Orchard Core Documentation](https://orchardcore.crestapps.com/)**.
88

99
The documentation covers:
1010
- **[Getting Started](https://orchardcore.crestapps.com/docs/getting-started)** — Installation and setup
11-
- **[AI Suite](https://orchardcore.crestapps.com/docs/ai/overview)** — AI modules, profiles, tools, and orchestration
11+
- **[AI Suite](https://orchardcore.crestapps.com/docs/ai/overview)**One place for AI integrations and solutions across chat, orchestration, management, RAG, MCP, A2A, and automation
1212
- **[AI Providers](https://orchardcore.crestapps.com/docs/providers/overview)** — Configuring OpenAI, Azure, Ollama, and more
1313
- **[Consuming AI Services](https://orchardcore.crestapps.com/docs/ai/consuming-ai-services)** — Programmatic usage via code
1414
- **[MCP](https://orchardcore.crestapps.com/docs/ai/mcp/)** — Model Context Protocol client and server support
@@ -91,8 +91,12 @@ You can install individual modules into your web project as needed, or install t
9191

9292
### Artificial Intelligence Suite
9393

94+
The CrestApps AI Suite is a comprehensive and extensible AI ecosystem built on Orchard Core, designed to unify and streamline AI integration and management. It combines provider integrations, flexible deployment and connection management, AI profiles, and advanced orchestration into a cohesive platform.
95+
96+
The suite enables highly customizable chat experiences, along with robust prompt and tool management, retrieval workflows, and long-term memory capabilities. It also supports MCP and Agent-to-Agent integrations, delivering a powerful foundation for building intelligent, interconnected systems—all seamlessly managed within the Orchard Core admin experience.
97+
9498
#### AI Module
95-
Provides services for all AI modules and provide the interface for managing AI profiles and AI Deployments. See the [AI Services documentation](https://orchardcore.crestapps.com/docs/ai/ai-services) for more details.
99+
Provides the foundation for the AI ecosystem, including shared AI services, orchestration infrastructure, profile management, connection and deployment management, and site-level AI settings. See the [AI Services documentation](https://orchardcore.crestapps.com/docs/ai/ai-services) for more details.
96100

97101
#### AI Chat Module
98102
Provides interface for interacting with AI chat models like **ChatGPT** and others. See the [AI Chat documentation](https://orchardcore.crestapps.com/docs/ai/ai-chat) for more details.

src/Abstractions/CrestApps.OrchardCore.AI.Abstractions/AICompletionContextKeys.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,11 @@ public static class AICompletionContextKeys
1414
/// to be included in the tool registry.
1515
/// </summary>
1616
public const string HasDocuments = "HasDocuments";
17+
18+
/// <summary>
19+
/// When set to <see langword="true"/> in <see cref="Models.AICompletionContext.AdditionalProperties"/>,
20+
/// indicates that authenticated user memory is available for the current request. This enables
21+
/// memory-related system tools to be included in the tool registry.
22+
/// </summary>
23+
public const string HasMemory = "HasMemory";
1724
}

src/Abstractions/CrestApps.OrchardCore.AI.Abstractions/AIToolPurposes.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,9 @@ public static class AIToolPurposes
2121
/// Tools that search data source embeddings for RAG (Retrieval-Augmented Generation).
2222
/// </summary>
2323
public const string DataSourceSearch = "data_source_search";
24+
25+
/// <summary>
26+
/// Tools that recall, list, or store authenticated user memory.
27+
/// </summary>
28+
public const string Memory = "memory";
2429
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace CrestApps.OrchardCore.AI;
2+
3+
public interface IAIMemorySafetyService
4+
{
5+
bool TryValidate(string name, string description, string content, out string errorMessage);
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using CrestApps.OrchardCore.AI.Models;
2+
using CrestApps.OrchardCore.Services;
3+
4+
namespace CrestApps.OrchardCore.AI;
5+
6+
public interface IAIMemoryStore : ICatalog<AIMemoryEntry>
7+
{
8+
Task<int> CountByUserAsync(string userId);
9+
10+
Task<AIMemoryEntry> FindByUserAndNameAsync(string userId, string name);
11+
12+
Task<IReadOnlyCollection<AIMemoryEntry>> GetByUserAsync(string userId, int limit = 100);
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using CrestApps.OrchardCore.AI.Models;
2+
using OrchardCore.Indexing.Models;
3+
4+
namespace CrestApps.OrchardCore.AI;
5+
6+
public interface IMemoryVectorSearchService
7+
{
8+
Task<IEnumerable<AIMemorySearchResult>> SearchAsync(
9+
IndexProfile indexProfile,
10+
float[] embedding,
11+
string userId,
12+
int topN,
13+
CancellationToken cancellationToken = default);
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using CrestApps.OrchardCore.Models;
2+
3+
namespace CrestApps.OrchardCore.AI.Models;
4+
5+
public sealed class AIMemoryEntry : CatalogItem
6+
{
7+
public string UserId { get; set; }
8+
9+
public string Name { get; set; }
10+
11+
public string Description { get; set; }
12+
13+
public string Content { get; set; }
14+
15+
public DateTime CreatedUtc { get; set; }
16+
17+
public DateTime UpdatedUtc { get; set; }
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace CrestApps.OrchardCore.AI.Models;
2+
3+
public sealed class AIMemorySearchResult
4+
{
5+
public string MemoryId { get; set; }
6+
7+
public string Name { get; set; }
8+
9+
public string Description { get; set; }
10+
11+
public string Content { get; set; }
12+
13+
public DateTime? UpdatedUtc { get; set; }
14+
15+
public float Score { get; set; }
16+
}

0 commit comments

Comments
 (0)