diff --git a/Directory.Build.props b/Directory.Build.props index 767ac459..c65ebdc3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -30,7 +30,7 @@ https://github.com/CrestApps/CrestApps.Core https://crestapps.com CrestApps.Core provides the shared, framework-agnostic CrestApps libraries for AI, orchestration, chat, templating, document processing, storage, and MVC sample applications built on ASP.NET Core. - CrestApps Core AI ASP.NET + CrestApps-Core diff --git a/README.md b/README.md index 46f226e7..59f53a7b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # CrestApps.Core -**CrestApps.Core is the AI application framework for .NET.** It gives you the orchestration, management, chat, document, RAG, MCP, A2A, agent, reporting, and extensibility building blocks needed to add production-ready AI to an existing application without stitching together a pile of disconnected SDK samples. +![CrestApps.Core for .NET](src/CrestApps.Core.Docs/static/img/docs/crestapps.core-dotnet-project.png) + +**CrestApps.Core is a composable AI management and application framework for .NET.** It gives you the building blocks to ship AI chat, agents, RAG, document workflows, MCP, A2A, reporting, and custom AI tooling without stitching together a pile of disconnected provider SDK samples. ## Why it exists @@ -14,14 +16,14 @@ Most teams start AI integration with a provider SDK, then quickly run into the r - Reporting, consumption tracking, and lead workflows - Live-agent handoff and post-session automation - Protocol integration like MCP and A2A -- Orcestrator integration like copilot orchestrator. +- Orchestrator integration like GitHub Copilot orchestration. `CrestApps.Core` packages that complexity into reusable .NET services so you can move faster, keep control of behavior, and ship AI features with less custom plumbing. ## What you get -- **AI management** for profiles, connections, deployments, data sources, templates, MCP resources, prompts, and external hosts -- **Reusable AI profiles** so every session can start with predefined behavior, settings, tools, prompts, and retrieval rules +- **AI management** for connections, deployments, agent profiles, data sources, templates, MCP resources, prompts, and external hosts +- **Reusable AI agent profiles** so every session can start with predefined behavior, settings, tools, prompts, and retrieval rules - **Chat interactions** for provider-agnostic playground and production chat experiences - **Document upload and processing** for summarization, Q&A, extraction, tabulation, and knowledge workflows - **RAG support** across attached documents, search indexes, and user memory, including configurable preemptive RAG @@ -47,7 +49,7 @@ Most teams start AI integration with a provider SDK, then quickly run into the r See the full use-case guide at **[core.crestapps.com](https://core.crestapps.com)**. -## Quick start +## Fastest way to try it ```powershell git clone https://github.com/CrestApps/CrestApps.Core.git @@ -57,6 +59,59 @@ dotnet test .\tests\CrestApps.Core.Tests\CrestApps.Core.Tests.csproj -c Release dotnet run --project .\src\Startup\CrestApps.Core.Mvc.Web\CrestApps.Core.Mvc.Web.csproj ``` +The MVC sample is the quickest way to see the full stack working together: AI connections, deployments, agent profiles, Chat Interactions, document processing, MCP, A2A, storage, and SignalR. + +## Fastest way to consume it + +Install the smallest useful package set for your app: + +```xml + + + + + + +``` + +Register the shared services plus one provider and the playground-style chat UI: + +```csharp +builder.Services.AddCrestAppsCore(crestApps => crestApps + .AddAISuite(ai => ai + .AddOpenAI() + .AddChatInteractions())); +``` + +By default, provider connections are loaded from `CrestApps:AI:Connections` and standalone deployments are loaded from `CrestApps:AI:Deployments`: + +```json +{ + "CrestApps": { + "AI": { + "Connections": [ + { + "Name": "primary-openai", + "ClientName": "OpenAI", + "ApiKey": "YOUR_API_KEY" + } + ], + "Deployments": [ + { + "Name": "gpt-4.1", + "ClientName": "OpenAI", + "ModelName": "gpt-4.1", + "Type": "Chat", + "IsDefault": true + } + ] + } + } +} +``` + +From there, create your first AI profile and use **Chat Interactions** as the easiest playground-style UI to chat against that profile while you tune prompts, providers, and deployments. + ## Learn more - **Documentation:** diff --git a/src/Abstractions/CrestApps.Core.AI.Abstractions/CrestApps.Core.AI.Abstractions.csproj b/src/Abstractions/CrestApps.Core.AI.Abstractions/CrestApps.Core.AI.Abstractions.csproj index b85aa77f..c4bd01ea 100644 --- a/src/Abstractions/CrestApps.Core.AI.Abstractions/CrestApps.Core.AI.Abstractions.csproj +++ b/src/Abstractions/CrestApps.Core.AI.Abstractions/CrestApps.Core.AI.Abstractions.csproj @@ -8,7 +8,7 @@ Core AI abstractions for CrestApps services. Framework-independent, usable in any ASP.NET Core application. - $(PackageTags) AI Abstractions + $(PackageTags) ai abstractions contracts orchestration chat diff --git a/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/AIChatSession.cs b/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/AIChatSession.cs index 52a9f7e3..c5b34956 100644 --- a/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/AIChatSession.cs +++ b/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/AIChatSession.cs @@ -9,48 +9,58 @@ public sealed class AIChatSession : ExtensibleEntity /// This property is used to track and manage the session across its lifecycle. /// public string SessionId { get; set; } + /// /// Gets or sets the profile identifier associated with this chat session. /// It references the user's or client's profile during the session. /// public string ProfileId { get; set; } + /// /// Gets or sets the title of the chat session. /// This can be a descriptive name or label for the session, such as "Customer Support Chat". /// public string Title { get; set; } + /// /// Gets or sets the user identifier who created this session. /// This is used to associate the session with a specific user. If unavailable, is used instead. /// public string UserId { get; set; } + /// /// Gets or sets the client identifier who created this session when is not available. /// This is typically used for cases where the session is initiated by a client or service instead of a specific user. /// public string ClientId { get; set; } + /// /// Gets or sets the collection of document references attached to this session. /// Documents are uploaded by users and used for RAG (Retrieval-Augmented Generation). /// public List Documents { get; set; } = []; + /// /// Gets or sets the UTC date and time when the session was first created. /// This property helps track the start time of the session in a standardized format (UTC). /// public DateTime CreatedUtc { get; set; } + /// /// Gets or sets the UTC date and time of the last activity in this session. /// public DateTime LastActivityUtc { get; set; } + /// /// Gets or sets the UTC date and time when the session was closed due to inactivity. /// public DateTime? ClosedAtUtc { get; set; } + /// /// Gets or sets the status of the chat session. /// public ChatSessionStatus Status { get; set; } + /// /// Gets or sets the technical name of the currently /// handling prompts for this session. When or empty, the default @@ -58,39 +68,47 @@ public sealed class AIChatSession : ExtensibleEntity /// function that transfers the chat to a live-agent platform). /// public string ResponseHandlerName { get; set; } + /// /// Gets or sets the extracted data fields for this session. /// Keys are field names from the data extraction configuration. /// public Dictionary ExtractedData { get; set; } = []; + /// /// Gets or sets the results of post-session processing tasks. /// Keys are task names from the post-session processing configuration. /// Populated after the session is closed. /// public Dictionary PostSessionResults { get; set; } = []; + /// /// Gets or sets the status of post-session processing for this session. /// public PostSessionProcessingStatus PostSessionProcessingStatus { get; set; } + /// /// Gets or sets the number of attempts made to process post-session tasks. /// public int PostSessionProcessingAttempts { get; set; } + /// /// Gets or sets the UTC timestamp of the last post-session processing attempt. /// public DateTime? PostSessionProcessingLastAttemptUtc { get; set; } + /// /// Gets or sets whether post-session tasks (custom AI tasks) have been processed. /// Used to track partial completion so successful steps are not re-run on retry. /// public bool IsPostSessionTasksProcessed { get; set; } + /// /// Gets or sets whether analytics events (resolution detection and session-end metrics) /// have been recorded. Used to track partial completion so successful steps are not re-run on retry. /// public bool IsAnalyticsRecorded { get; set; } + /// /// Gets or sets whether conversion goals have been evaluated. /// Tracked independently from analytics so each step can be retried without re-running the other. diff --git a/src/Abstractions/CrestApps.Core.Abstractions/CrestApps.Core.Abstractions.csproj b/src/Abstractions/CrestApps.Core.Abstractions/CrestApps.Core.Abstractions.csproj index 637d3c2f..8bbde2bb 100644 --- a/src/Abstractions/CrestApps.Core.Abstractions/CrestApps.Core.Abstractions.csproj +++ b/src/Abstractions/CrestApps.Core.Abstractions/CrestApps.Core.Abstractions.csproj @@ -7,7 +7,7 @@ Core abstractions for CrestApps services. Framework-independent, usable in any ASP.NET Core application. - $(PackageTags) Abstractions + $(PackageTags) abstractions contracts catalogs validation diff --git a/src/Abstractions/CrestApps.Core.Infrastructure.Abstractions/CrestApps.Core.Infrastructure.Abstractions.csproj b/src/Abstractions/CrestApps.Core.Infrastructure.Abstractions/CrestApps.Core.Infrastructure.Abstractions.csproj index 2179a1c9..31e4afe5 100644 --- a/src/Abstractions/CrestApps.Core.Infrastructure.Abstractions/CrestApps.Core.Infrastructure.Abstractions.csproj +++ b/src/Abstractions/CrestApps.Core.Infrastructure.Abstractions/CrestApps.Core.Infrastructure.Abstractions.csproj @@ -8,7 +8,7 @@ Core infrastructure abstractions for indexing, search, and data source services. Framework-independent and reusable outside the AI layer. - $(PackageTags) Infrastructure Abstractions Indexing Search + $(PackageTags) infrastructure abstractions indexing search diff --git a/src/CrestApps.Core.Docs/CrestApps.Core.Docs.csproj b/src/CrestApps.Core.Docs/CrestApps.Core.Docs.csproj index f4e56132..913f72be 100644 --- a/src/CrestApps.Core.Docs/CrestApps.Core.Docs.csproj +++ b/src/CrestApps.Core.Docs/CrestApps.Core.Docs.csproj @@ -3,6 +3,7 @@ $(CommonTargetFrameworks) false + $(PackageTags) docs documentation docusaurus website diff --git a/src/CrestApps.Core.Docs/docs/changelog/index.md b/src/CrestApps.Core.Docs/docs/changelog/index.md index 9dde1345..1372fcf4 100644 --- a/src/CrestApps.Core.Docs/docs/changelog/index.md +++ b/src/CrestApps.Core.Docs/docs/changelog/index.md @@ -11,4 +11,4 @@ This section tracks `CrestApps.Core` releases and notable repository-level chang | Version | Highlights | | --- | --- | -| [1.0.0](v1.0.0) | Initial standalone release of the `CrestApps.Core` framework repository | +| [1.0.0](v1.0.0) | Initial standalone release plus merged configuration catalogs, clearer quick-start guidance, and deployment configuration diagnostics | diff --git a/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md b/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md index 42273936..f4cfe735 100644 --- a/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md +++ b/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md @@ -19,3 +19,5 @@ description: Initial standalone release notes for the CrestApps.Core repository. - publishes a framework-focused documentation site at [core.crestapps.com](https://core.crestapps.com) - merges appsettings-backed and UI-managed AI provider connections and deployments through runtime catalogs, so MVC selectors and AI resolution stay current without rebuilding options or restarting the app - exposes merged AI connection and deployment views through `INamedSourceCatalog` registrations, adds generic `Add*DocumentCatalog()` helpers for custom catalog registration, keeps deterministic name conflict handling so UI-managed records override conflicting appsettings entries, and standardizes settings so connections and deployments are configured separately +- documents the default AI configuration sections (`CrestApps:AI:Connections` and `CrestApps:AI:Deployments`), tightens the quick-start path around Chat Interactions, and refreshes the docs navigation and landing page for faster onboarding +- adds Debug-level diagnostics in `ConfigurationAIDeploymentCatalog` so hosts can trace which configuration sections were evaluated and how standalone deployments were parsed diff --git a/src/CrestApps.Core.Docs/docs/core/architecture.md b/src/CrestApps.Core.Docs/docs/core/architecture.md index 04dd06b6..a3e2102b 100644 --- a/src/CrestApps.Core.Docs/docs/core/architecture.md +++ b/src/CrestApps.Core.Docs/docs/core/architecture.md @@ -5,100 +5,46 @@ sidebar_position: 2 # Architecture & Dependency Diagram -This page describes the project architecture and how the various layers depend on each other. +This page describes the project architecture and how the major layers depend on each other. ## Dependency Diagram -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ Application Layer │ -│ │ -│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ -│ │ .Web (MVC App) │ │ Modules │ │ (Future) │ │ -│ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │ -│ │ │ │ │ -└───────────┼─────────────────────┼──────────────────────┼────────────┘ - │ │ │ - ▼ ▼ ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Optional Middle Layer │ -│ │ -│ ┌──────────────────────────┐ ┌──────────────────────────────┐ │ -│ │ (Document Store) │ │ (Shape-based UI) │ │ -│ └────────────┬─────────────┘ └──────────────┬───────────────┘ │ -│ │ │ │ -└───────────────┼────────────────────────────────┼────────────────────┘ - │ │ - ▼ ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ Framework Layer │ -│ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ Core Projects │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ -│ │ │ CrestApps │ │ CrestApps │ │ CrestApps.Core.AI │ │ │ -│ │ │ .AI.Core │ │ .AI.Chat │ │ .OpenAI.Core │ │ │ -│ │ │ │ │ .Core │ │ │ │ │ -│ │ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │ │ -│ │ │ │ │ │ │ -│ │ ┌──────┴───────┐ ┌─────┴────────┐ ┌───────┴─────────┐ │ │ -│ │ │ CrestApps │ │ CrestApps │ │ CrestApps.Core.AI │ │ │ -│ │ │ .Core │ │ .SignalR │ │ .OpenAI.Azure │ │ │ -│ │ │ │ │ .Core │ │ .Core │ │ │ -│ │ └──────┬───────┘ └──────┬───────┘ └────────┬────────┘ │ │ -│ │ │ │ │ │ │ -│ │ ┌──────┴───────┐ ┌─────┴────────┐ ┌───────┴─────────┐ │ │ -│ │ │ CrestApps.Core.AI │ │ CrestApps.Core.AI │ │ CrestApps.Core.AI │ │ │ -│ │ │ .Ollama.Core │ │ .AzureAI │ │ .Mcp.Core │ │ │ -│ │ │ │ │ Inference │ │ │ │ │ -│ │ │ │ │ .Core │ │ │ │ │ -│ │ └──────┬───────┘ └──────┬───────┘ └────────┬────────┘ │ │ -│ │ │ │ │ │ │ -│ │ ┌──────┴───────┐ ┌─────┴────────┐ ┌───────┴─────────┐ │ │ -│ │ │ CrestApps.Core.AI │ │ CrestApps.Core.AI │ │ CrestApps.Core.AI │ │ │ -│ │ │ .Chat │ │ .DataSources │ │ .DataSources │ │ │ -│ │ │ .Copilot │ │ .AzureAI │ │ .Elasticsearch │ │ │ -│ │ └──────────────┘ └──────────────┘ └─────────────────┘ │ │ -│ │ │ │ -│ └─────────┼─────────────────┼────────────────────┼────────────┘ │ -│ │ │ │ │ -│ ┌─────────┴─────────────────┴────────────────────┴────────────┐ │ -│ │ Abstractions │ │ -│ │ │ │ -│ │ ┌──────────────────┐ ┌────────────────────────────────┐ │ │ -│ │ │ CrestApps │ │ CrestApps.Core.AI.Abstractions │ │ │ -│ │ │ .Abstractions │ │ (IAICompletionService, │ │ │ -│ │ │ (ICatalog, │ │ IAIProfileManager, │ │ │ -│ │ │ INamedEntity) │ │ IOrchestrator, etc.) │ │ │ -│ │ └──────────────────┘ └────────────────────────────────┘ │ │ -│ │ │ │ -│ └──────────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌──────────────────────────────────────────────────────────────┐ │ -│ │ Resources │ │ -│ │ ┌──────────────────────────────────────────────────────┐ │ │ -│ │ │ CrestApps.Core.AI.Resources (shared JS: ai-chat, │ │ │ -│ │ │ chat-interaction) │ │ │ -│ │ └──────────────────────────────────────────────────────┘ │ │ -│ └──────────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌──────────────────────────────────────────────────────────────┐ │ -│ │ Utilities │ │ -│ │ ┌────────────────┐ ┌──────────────────────┐ │ │ -│ │ │ CrestApps │ │ CrestApps.Core.AI │ │ │ -│ │ │ .Support │ │ .Prompting │ │ │ -│ │ └────────────────┘ └──────────────────────┘ │ │ -│ └──────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ +```text +┌──────────────────────────────────────────────────────────────────────────────┐ +│ Application hosts │ +│ │ +│ CrestApps.Core.Mvc.Web Aspire AppHost Custom MVC / Razor / Blazor app │ +└───────────────────────────────┬──────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ Core foundation │ +│ │ +│ CrestApps.Core CrestApps.Core.Abstractions │ +│ CrestApps.Core.Infrastructure CrestApps.Core.Infrastructure.Abstractions │ +└───────────────────────────────┬──────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ AI runtime and feature packages │ +│ │ +│ AI runtime Chat A2A MCP SignalR Templates Copilot Markdown │ +│ Azure utilities │ +└───────────────────────────────┬──────────────────────────────────────────────┘ + │ + ┌───────────────┴────────────────┬─────────────────────────────┐ + ▼ ▼ ▼ +┌──────────────────────────────┐ ┌──────────────────────────────┐ ┌──────────────────────────────┐ +│ Provider integrations │ │ Search and data sources │ │ Storage implementations │ +│ │ │ │ │ │ +│ OpenAI / Azure OpenAI │ │ Azure AI Search │ │ Entity Framework Core │ +│ Ollama / Azure AI Inference │ │ Elasticsearch │ │ YesSql │ +│ PDF / OpenXml / FTP / SFTP │ │ │ │ │ +└──────────────────────────────┘ └──────────────────────────────┘ └──────────────────────────────┘ ``` ## Layer Descriptions -### Framework Layer (Top Level) - - | Project | Role | |---------|------| | `CrestApps.Core.Abstractions` | Core interfaces: `ICatalog`, `INamedEntity`, `ExtensibleEntity`, `IODataValidator` | @@ -119,13 +65,14 @@ This page describes the project architecture and how the various layers depend o | `CrestApps.Core.Support` | General utility classes | | `CrestApps.Core.Templates` | Prompt template engine | -### Optional Middle Layer +### Storage layer | Project | Role | |---------|------| +| `CrestApps.Core.Data.EntityCore` | Entity Framework Core-based catalog and store implementation | | `CrestApps.Core.Data.YesSql` | YesSql-based document catalog implementation (SQLite, PostgreSQL, SQL Server) | -### Application Layer +### Application layer | Project | Role | |---------|------| diff --git a/src/CrestApps.Core.Docs/docs/core/chat.md b/src/CrestApps.Core.Docs/docs/core/chat.md index be9714a7..eb2a28a7 100644 --- a/src/CrestApps.Core.Docs/docs/core/chat.md +++ b/src/CrestApps.Core.Docs/docs/core/chat.md @@ -9,16 +9,19 @@ description: Chat session management, interaction handlers, and response routing > Manages chat sessions, routes responses through pluggable handlers, and tracks interaction history. +If you want the easiest playground-style UI for a new host, start here after you have one provider connection, one deployment, and one AI profile configured. + ## Quick Start ```csharp -builder.Services - .AddCoreAIServices() - .AddCoreAIOrchestration() - .AddCoreAIChatInteractions() - .AddCoreAIOpenAI(); +builder.Services.AddCrestAppsCore(crestApps => crestApps + .AddAISuite(ai => ai + .AddOpenAI() + .AddChatInteractions())); ``` +By default, connections are discovered from `CrestApps:AI:Connections` and standalone deployments are discovered from `CrestApps:AI:Deployments`. + ## Problem & Solution A chat experience involves more than sending messages to an LLM: diff --git a/src/CrestApps.Core.Docs/docs/core/getting-started-aspnet.md b/src/CrestApps.Core.Docs/docs/core/getting-started-aspnet.md index 5e0231d7..6d31463a 100644 --- a/src/CrestApps.Core.Docs/docs/core/getting-started-aspnet.md +++ b/src/CrestApps.Core.Docs/docs/core/getting-started-aspnet.md @@ -17,20 +17,20 @@ Start with the smallest set that matches your scenario. - - - - - - - + + + + + + + ``` @@ -41,10 +41,8 @@ In `Program.cs`, compose the framework through the `AddCrestAppsCore(...)` build ```csharp builder.Services.AddCrestAppsCore(crestApps => crestApps .AddAISuite(ai => ai - .AddMarkdown() - .AddChatInteractions() - .AddDocumentProcessing() - .AddOpenAI())); + .AddOpenAI() + .AddChatInteractions())); ``` `AddAISuite(...)` adds the shared CrestApps core services, the AI runtime, and orchestration together. If you prefer the lower-level registrations, the same features are still available as raw `IServiceCollection` extensions such as `AddCoreAIServices()` and `AddCoreAIOrchestration()`. @@ -84,7 +82,26 @@ At minimum, provide a connection and a deployment through configuration or your } ``` -The MVC sample demonstrates the full runtime options pattern, including configuration-backed connections, UI-managed overrides, and merged deployment catalogs. Keep connection credentials under `Connections` and define model/deployment choices under `Deployments`. +The default configuration sections are: + +- `CrestApps:AI:Connections` for provider connections and credentials +- `CrestApps:AI:Deployments` for standalone deployments and deployment metadata + +Keep connection credentials under `Connections` and define model/deployment choices under `Deployments`. + +## Fastest path to a first prompt + +For the smallest useful setup: + +1. Register `AddAISuite(...)` +2. Add one provider like `AddOpenAI()` +3. Add `AddChatInteractions()` +4. Configure one connection in `CrestApps:AI:Connections` +5. Configure one chat deployment in `CrestApps:AI:Deployments` +6. Create an AI profile that points at that deployment +7. Use Chat Interactions as the first playground-style UI + +That path gets you to a working chat experience with the fewest moving parts. ## 4. Pick the application model @@ -92,7 +109,12 @@ The service registrations stay the same; only the UI or endpoint layer changes. ### MVC -Use MVC when you want server-rendered admin pages, controllers, and SignalR chat hubs. The reference implementation is **`CrestApps.Core.Mvc.Web`**. +Use MVC when you want server-rendered admin pages, controllers, and SignalR chat hubs. The reference implementation is **`src\Startup\CrestApps.Core.Mvc.Web`**. + +Use these files as the primary sample: + +- `src\Startup\CrestApps.Core.Mvc.Web\Program.cs` for service registration and feature composition +- `src\Startup\CrestApps.Core.Mvc.Web\appsettings.json` for connection and deployment examples ### Razor Pages @@ -160,7 +182,7 @@ That layering keeps small apps lightweight while letting larger apps grow into a ## 7. Use the MVC sample as the reference host -`src/Startup/CrestApps.Core.Mvc.Web/Program.cs` is the canonical example for: +`src\Startup\CrestApps.Core.Mvc.Web\Program.cs` is the canonical example for: - configuration layering - service registration order diff --git a/src/CrestApps.Core.Docs/docs/core/index.md b/src/CrestApps.Core.Docs/docs/core/index.md index c0b6bc74..180c5401 100644 --- a/src/CrestApps.Core.Docs/docs/core/index.md +++ b/src/CrestApps.Core.Docs/docs/core/index.md @@ -21,8 +21,8 @@ description: The package layout and feature map for the standalone CrestApps.Cor | Capability | What it enables | | --- | --- | -| AI management | Profiles, connections, deployments, data sources, templates, and runtime configuration | -| Reusable AI profiles | Predefined behavior, prompts, model settings, tools, and retrieval rules for every session | +| AI management | Connections, deployments, agent profiles, data sources, templates, and runtime configuration | +| Reusable AI agent profiles | Predefined behavior, prompts, model settings, tools, and retrieval rules for every session | | Chat interactions | Provider-agnostic chat playgrounds and production chat experiences | | Documents and knowledge | Upload files for summarization, extraction, tabulation, Q&A, and retrieval | | RAG | Blend attached documents, data sources, and user memory with configurable preemptive retrieval | @@ -37,14 +37,19 @@ description: The package layout and feature map for the standalone CrestApps.Cor ```csharp builder.Services.AddCrestAppsCore(crestApps => crestApps .AddAISuite(ai => ai - .AddMarkdown() - .AddChatInteractions() - .AddDocumentProcessing() - .AddOpenAI())); + .AddOpenAI() + .AddChatInteractions())); ``` That is enough to start resolving `IAICompletionService`, `IAIClientFactory`, or `IOrchestrator` from DI and composing your own AI experience. +By default: + +- connections are loaded from `CrestApps:AI:Connections` +- deployments are loaded from `CrestApps:AI:Deployments` + +The quickest way to validate the setup is to create an AI profile and use **Chat Interactions** as your first playground-style UI. + ## Package map | Area | Main package | Purpose | diff --git a/src/CrestApps.Core.Docs/docs/core/mvc-example.md b/src/CrestApps.Core.Docs/docs/core/mvc-example.md index 1c88159d..62cd3fed 100644 --- a/src/CrestApps.Core.Docs/docs/core/mvc-example.md +++ b/src/CrestApps.Core.Docs/docs/core/mvc-example.md @@ -134,14 +134,12 @@ The MVC sample explicitly calls `AddMarkdown()` inside `AddAISuite(...)`. That k Registers all supported AI providers: ```csharp - builder.Services.AddCrestAppsCore(crestApps => crestApps .AddAISuite(ai => ai .AddOpenAI() .AddAzureOpenAI() .AddOllama() .AddAzureAIInference())); - ``` The MVC sample still binds static provider metadata from `CrestApps:AI:Providers`, but mutable AI connections and deployments now come from first-class merged catalogs instead of rebuilding `AIProviderOptions` after admin edits. @@ -156,17 +154,17 @@ Both merged catalogs also expose configurable section lists through `AIProviderC { "CrestApps": { "AI": { - "Connections": [ - { - "Name": "WinnerWare", - "ClientName": "AzureOpenAI", - "Endpoint": "https://winnerwareai.openai.azure.com/", - "AuthenticationType": "ApiKey", - "ApiKey": "YOUR_API_KEY", - "DisplayText": "WinnerWare Azure OpenAI" - } - ] - } + "Connections": [ + { + "Name": "WinnerWare", + "ClientName": "AzureOpenAI", + "Endpoint": "https://winnerwareai.openai.azure.com/", + "AuthenticationType": "ApiKey", + "ApiKey": "YOUR_API_KEY", + "DisplayText": "WinnerWare Azure OpenAI" + } + ] + } } } ``` @@ -178,27 +176,27 @@ Provider-grouped connection settings under `CrestApps:Providers:{ProviderName}:C { "CrestApps": { "AI": { - "Deployments": [ - { - "ProviderName": "AzureSpeech", - "Name": "whisper", - "Type": "SpeechToText", - "IsDefault": true, - "Endpoint": "https://eastus.stt.speech.microsoft.com", - "AuthenticationType": "ApiKey", - "ApiKey": "YOUR_API_KEY" - }, - { - "ProviderName": "AzureSpeech", - "Name": "AzureTextToSpeech", - "Type": "TextToSpeech", - "IsDefault": true, - "Endpoint": "https://eastus.tts.speech.microsoft.com", - "AuthenticationType": "ApiKey", - "ApiKey": "YOUR_API_KEY" - } - ] - } + "Deployments": [ + { + "ProviderName": "AzureSpeech", + "Name": "whisper", + "Type": "SpeechToText", + "IsDefault": true, + "Endpoint": "https://eastus.stt.speech.microsoft.com", + "AuthenticationType": "ApiKey", + "ApiKey": "YOUR_API_KEY" + }, + { + "ProviderName": "AzureSpeech", + "Name": "AzureTextToSpeech", + "Type": "TextToSpeech", + "IsDefault": true, + "Endpoint": "https://eastus.tts.speech.microsoft.com", + "AuthenticationType": "ApiKey", + "ApiKey": "YOUR_API_KEY" + } + ] + } } } ``` diff --git a/src/CrestApps.Core.Docs/docs/getting-started.md b/src/CrestApps.Core.Docs/docs/getting-started.md index 6d3c5c16..d96ee178 100644 --- a/src/CrestApps.Core.Docs/docs/getting-started.md +++ b/src/CrestApps.Core.Docs/docs/getting-started.md @@ -7,6 +7,17 @@ description: Build, run, and explore the standalone CrestApps.Core repository. # Getting Started +## Fastest path to a working AI experience + +If you want the least-effort path, use this sequence: + +1. Register `AddCrestAppsCore(...).AddAISuite(...)` +2. Add one provider plus `AddChatInteractions()` +3. Configure `CrestApps:AI:Connections` and `CrestApps:AI:Deployments` +4. Create an AI profile and chat against it through Chat Interactions + +Chat Interactions are the easiest playground-style UI for validating that your connection, deployment, prompts, and profile wiring are all correct before you build a custom experience. + ## Prerequisites @@ -46,19 +57,59 @@ dotnet run --project .\src\Startup\CrestApps.Core.Aspire.AppHost\CrestApps.Core. Use the Aspire host when you want to boot the MVC sample and related sample clients together. -## Learn the registration model +## Smallest useful app integration -The recommended registration surface is the `AddCrestAppsCore(...)` builder, which groups framework features into higher-level suites: +Use the `AddCrestAppsCore(...)` builder as the main entry point: ```csharp builder.Services.AddCrestAppsCore(crestApps => crestApps .AddAISuite(ai => ai - .AddMarkdown() - .AddChatInteractions() - .AddDocumentProcessing() .AddOpenAI())); ``` +Then add the first interactive feature: + +```csharp +builder.Services.AddCrestAppsCore(crestApps => crestApps + .AddAISuite(ai => ai + .AddOpenAI() + .AddChatInteractions())); +``` + +By default: + +- connections are read from `CrestApps:AI:Connections` +- deployments are read from `CrestApps:AI:Deployments` + +```json +{ + "CrestApps": { + "AI": { + "Connections": [ + { + "Name": "primary-openai", + "ClientName": "OpenAI", + "ApiKey": "YOUR_API_KEY" + } + ], + "Deployments": [ + { + "Name": "gpt-4.1", + "ClientName": "OpenAI", + "ModelName": "gpt-4.1", + "Type": "Chat", + "IsDefault": true + } + ] + } + } +} +``` + +Create an AI profile that uses your chat deployment, then use Chat Interactions to test it end to end. + +## Learn the registration model + Under the hood, each builder step still maps to the corresponding `AddCrestApps...` `IServiceCollection` extension, so hosts can still opt into the lower-level registration methods when they want that control. - Start with **[Core overview](core/index.md)** to understand the package layout @@ -68,7 +119,7 @@ Under the hood, each builder step still maps to the corresponding `AddCrestApps. ## Build the docs site ```bash -cd src/CrestApps.Core.Docs +cd src\CrestApps.Core.Docs npm install npm run build ``` diff --git a/src/CrestApps.Core.Docs/docs/intro.md b/src/CrestApps.Core.Docs/docs/intro.md index fb70482e..4b75ab5d 100644 --- a/src/CrestApps.Core.Docs/docs/intro.md +++ b/src/CrestApps.Core.Docs/docs/intro.md @@ -7,12 +7,12 @@ description: The standalone CrestApps framework for building AI-powered ASP.NET # CrestApps.Core -**CrestApps.Core is the AI application framework for .NET.** It is designed for teams that want to add advanced AI capabilities to an existing application without spending months rebuilding orchestration, chat, retrieval, reporting, and integration plumbing from scratch. +**CrestApps.Core is a composable AI management and application framework for .NET.** It is designed for teams that want to add advanced AI capabilities to an existing application without spending months rebuilding orchestration, chat, retrieval, reporting, and integration plumbing from scratch. ## What it delivers -- provider-agnostic AI management with profiles, connections, deployments, and data sources -- reusable AI profiles that define behavior, prompts, models, tools, and defaults +- provider-agnostic AI management with connections, deployments, agent profiles, and data sources +- reusable AI agent profiles that define behavior, prompts, models, tools, and defaults - chat interactions for playground and production scenarios - document upload and processing for Q&A, summarization, extraction, and tabulation - RAG across search indexes, attached files, and user memory @@ -25,7 +25,7 @@ description: The standalone CrestApps framework for building AI-powered ASP.NET | Need | What CrestApps.Core gives you | | --- | --- | | A single framework instead of scattered SDK examples | One composable service model for AI integration in .NET | -| Reusable behavior across sessions | AI profiles, templates, orchestration, and shared defaults | +| Reusable behavior across sessions | AI agent profiles, templates, orchestration, and shared defaults | | Production chat flows | Sessions, widgets, metrics, response handlers, extraction, and escalation workflows | | Knowledge-grounded AI | Documents, data sources, vector search, citations, and configurable preemptive RAG | | Protocol interoperability | MCP server/client support and A2A-ready agent workflows | @@ -42,7 +42,7 @@ The framework fits standard .NET dependency injection and works well in: ## Start here -- **[Getting Started](getting-started.md)** for build, run, and sample-host commands +- **[Getting Started](getting-started.md)** for the quickest path from package install to first prompt - **[Core Overview](core/index.md)** for the feature catalog and package layout - **[AI Chat Use Cases](core/use-cases.md)** for real-world scenarios - **[MVC Example](core/mvc-example.md)** for the complete reference host diff --git a/src/CrestApps.Core.Docs/docusaurus.config.js b/src/CrestApps.Core.Docs/docusaurus.config.js index fb202aea..b1506539 100644 --- a/src/CrestApps.Core.Docs/docusaurus.config.js +++ b/src/CrestApps.Core.Docs/docusaurus.config.js @@ -5,7 +5,7 @@ import { themes as prismThemes } from 'prism-react-renderer'; /** @type {import('@docusaurus/types').Config} */ const config = { title: 'CrestApps Core', - tagline: 'The AI application framework for .NET', + tagline: 'Composable AI management and application framework for .NET', favicon: 'img/favicon.ico', titleDelimiter: '|', diff --git a/src/CrestApps.Core.Docs/sidebars.js b/src/CrestApps.Core.Docs/sidebars.js index 11fbd1fd..249d9097 100644 --- a/src/CrestApps.Core.Docs/sidebars.js +++ b/src/CrestApps.Core.Docs/sidebars.js @@ -10,66 +10,73 @@ const sidebars = { label: 'Core', collapsed: false, items: [ - 'core/index', 'core/architecture', - 'core/getting-started-aspnet', - 'core/use-cases', 'core/core-services', - 'core/ai-core', - 'core/orchestration', - 'core/chat', - 'core/document-processing', - 'core/ai-templates', - 'core/tools', - 'core/agents', - 'core/copilot', - 'core/response-handlers', - 'core/context-builders', - 'core/signalr', - 'core/data-storage', - 'core/ai-documents', - 'core/ai-memory', + 'core/getting-started-aspnet', + 'core/index', 'core/interfaces', + 'core/mvc-example', + ], + }, + { + type: 'category', + label: 'Features', + collapsed: false, + items: [ { type: 'category', - label: 'AI Providers', + label: 'Agent-to-Agent Protocol (A2A)', items: [ - 'providers/index', - 'providers/openai', - 'providers/azure-openai', - 'providers/ollama', - 'providers/azure-ai-inference', + 'a2a/index', + 'a2a/client', + 'a2a/host', ], }, + 'core/agents', + 'core/ai-core', + 'core/ai-documents', + 'core/ai-memory', + 'core/ai-templates', + 'core/chat', + 'core/context-builders', + 'core/copilot', { type: 'category', label: 'Data Sources', items: [ 'data-sources/index', - 'data-sources/elasticsearch', 'data-sources/azure-ai', + 'data-sources/elasticsearch', ], }, + 'core/data-storage', + 'core/document-processing', { type: 'category', label: 'Model Context Protocol (MCP)', items: [ 'mcp/index', 'mcp/client', - 'mcp/server', 'mcp/resource-types', + 'mcp/server', ], }, + 'core/orchestration', { type: 'category', - label: 'Agent-to-Agent Protocol (A2A)', + label: 'AI Providers', items: [ - 'a2a/index', - 'a2a/client', - 'a2a/host', + 'providers/index', + 'providers/azure-ai-inference', + 'providers/azure-openai', + 'providers/ollama', + 'providers/openai', ], }, - 'core/mvc-example', + 'core/response-handlers', + 'core/signalr', + 'core/tools', + 'core/use-cases', ], }, { diff --git a/src/CrestApps.Core.Docs/src/components/HomepageFeatures/index.js b/src/CrestApps.Core.Docs/src/components/HomepageFeatures/index.js index 7209ee60..3693335f 100644 --- a/src/CrestApps.Core.Docs/src/components/HomepageFeatures/index.js +++ b/src/CrestApps.Core.Docs/src/components/HomepageFeatures/index.js @@ -3,62 +3,60 @@ import Heading from '@theme/Heading'; import styles from './styles.module.css'; const FeatureList = [ - { - title: 'AI management and orchestration', - emoji: '🤖', - description: ( - <> - Manage AI profiles, connections, deployments, tools, templates, data sources, - and orchestration in one reusable .NET framework. - - ), - }, - { - title: 'Chat, RAG, and business workflows', - emoji: '💬', - description: ( - <> - Build chat experiences with documents, memory, metrics, reporting, - extraction, lead collection, and live-agent handoff. - - ), - }, - { - title: 'Protocols, agents, and extensibility', - emoji: '🧩', - description: ( - <> - Support MCP, A2A, AI agents, Copilot orchestration, and custom AI functions - while keeping every layer customizable from code. - - ), - }, + { + title: 'Launch AI chat and agent experiences', + emoji: '🚀', + description: ( + <> + Start with Chat Interactions as a playground-style UI, then grow into reusable + AI agents, orchestration flows, and production chat experiences. + + ), + }, + { + title: 'Control models, connections, and runtime behavior', + emoji: '🎛️', + description: ( + <> + Configure providers, credentials, deployments, prompts, and reusable agent + profiles without scattering AI setup across the whole app. + + ), + }, + { + title: 'Connect documents, tools, MCP, and A2A', + emoji: '🧩', + description: ( + <> + Add RAG, document workflows, custom AI tools, Model Context Protocol, and + agent-to-agent integration on the same composable service foundation. + + ), + }, ]; function Feature({ emoji, title, description }) { - return ( -
-
- {emoji} -
-
- {title} -

{description}

-
-
- ); + return ( +
+
+
{emoji}
+ {title} +

{description}

+
+
+ ); } export default function HomepageFeatures() { - return ( -
-
-
- {FeatureList.map((props, idx) => ( - - ))} -
-
-
- ); + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + + ))} +
+
+
+ ); } diff --git a/src/CrestApps.Core.Docs/src/components/HomepageFeatures/styles.module.css b/src/CrestApps.Core.Docs/src/components/HomepageFeatures/styles.module.css index b248eb2e..b8d7eed4 100644 --- a/src/CrestApps.Core.Docs/src/components/HomepageFeatures/styles.module.css +++ b/src/CrestApps.Core.Docs/src/components/HomepageFeatures/styles.module.css @@ -1,11 +1,18 @@ .features { - display: flex; - align-items: center; - padding: 2rem 0; + padding: 1rem 0 3rem; width: 100%; } -.featureSvg { - height: 200px; - width: 200px; +.featureCard { + height: 100%; + padding: 1.5rem; + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 1rem; + background: var(--ifm-background-surface-color); + box-shadow: 0 0.75rem 2rem rgba(0, 0, 0, 0.06); +} + +.featureEmoji { + font-size: 2.5rem; + margin-bottom: 1rem; } diff --git a/src/CrestApps.Core.Docs/src/pages/index.js b/src/CrestApps.Core.Docs/src/pages/index.js index 644136bc..ab394539 100644 --- a/src/CrestApps.Core.Docs/src/pages/index.js +++ b/src/CrestApps.Core.Docs/src/pages/index.js @@ -11,23 +11,36 @@ function HomepageHeader() { const {siteConfig} = useDocusaurusContext(); return (
-
- - {siteConfig.title} - -

{siteConfig.tagline}

-
- - Get Started - - - Quick Start Guide - +
+
+ + Build AI chat, agents, and automation into your .NET app + +

{siteConfig.tagline}

+

+ Start with connections, deployments, and Chat Interactions, then grow into + documents, RAG, MCP, A2A, reporting, and custom AI tooling without + reworking your architecture. +

+
+ + Quick Start Guide + + + ASP.NET Core Setup + +
+
+
+ CrestApps.Core feature overview
@@ -35,11 +48,10 @@ function HomepageHeader() { } export default function Home() { - const {siteConfig} = useDocusaurusContext(); return ( + description="CrestApps.Core is the composable AI management and application framework for .NET with orchestration, chat, RAG, agents, MCP, A2A, reporting, and extensibility.">
diff --git a/src/CrestApps.Core.Docs/src/pages/index.module.css b/src/CrestApps.Core.Docs/src/pages/index.module.css index 9f71a5da..ce868551 100644 --- a/src/CrestApps.Core.Docs/src/pages/index.module.css +++ b/src/CrestApps.Core.Docs/src/pages/index.module.css @@ -5,7 +5,6 @@ .heroBanner { padding: 4rem 0; - text-align: center; position: relative; overflow: hidden; } @@ -16,8 +15,52 @@ } } +.heroContent { + display: grid; + gap: 2rem; + align-items: center; + grid-template-columns: minmax(0, 1.2fr) minmax(280px, 0.8fr); +} + +.heroText { + text-align: left; +} + +.heroLead { + font-size: 1.1rem; + margin: 1.5rem 0; + max-width: 44rem; +} + .buttons { display: flex; align-items: center; + gap: 1rem; + flex-wrap: wrap; +} + +.heroVisual { + display: flex; justify-content: center; } + +.heroImage { + width: 100%; + max-width: 520px; + border-radius: 1rem; + box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, 0.18); +} + +@media screen and (max-width: 996px) { + .heroContent { + grid-template-columns: 1fr; + } + + .heroText { + text-align: center; + } + + .buttons { + justify-content: center; + } +} diff --git a/src/CrestApps.Core.Docs/static/img/docs/crestapps.core-dotnet-project.png b/src/CrestApps.Core.Docs/static/img/docs/crestapps.core-dotnet-project.png new file mode 100644 index 00000000..307abd03 Binary files /dev/null and b/src/CrestApps.Core.Docs/static/img/docs/crestapps.core-dotnet-project.png differ diff --git a/src/Primitives/CrestApps.Core.AI.A2A/CrestApps.Core.AI.A2A.csproj b/src/Primitives/CrestApps.Core.AI.A2A/CrestApps.Core.AI.A2A.csproj index 4423bb11..b0c5cf00 100644 --- a/src/Primitives/CrestApps.Core.AI.A2A/CrestApps.Core.AI.A2A.csproj +++ b/src/Primitives/CrestApps.Core.AI.A2A/CrestApps.Core.AI.A2A.csproj @@ -8,7 +8,7 @@ Framework-independent Agent-to-Agent (A2A) client support for CrestApps AI services. - $(PackageTags) AI A2A AgentToAgent + $(PackageTags) ai agents a2a agent-to-agent protocol diff --git a/src/Primitives/CrestApps.Core.AI.AzureAIInference/CrestApps.Core.AI.AzureAIInference.csproj b/src/Primitives/CrestApps.Core.AI.AzureAIInference/CrestApps.Core.AI.AzureAIInference.csproj index 2f3966fc..e3e611ae 100644 --- a/src/Primitives/CrestApps.Core.AI.AzureAIInference/CrestApps.Core.AI.AzureAIInference.csproj +++ b/src/Primitives/CrestApps.Core.AI.AzureAIInference/CrestApps.Core.AI.AzureAIInference.csproj @@ -8,7 +8,7 @@ Azure AI Inference (GitHub Models) provider implementation for CrestApps AI services. - $(PackageTags) AI Azure AzureAIInference + $(PackageTags) ai azure-ai-inference github-models inference diff --git a/src/Primitives/CrestApps.Core.AI.Chat/CrestApps.Core.AI.Chat.csproj b/src/Primitives/CrestApps.Core.AI.Chat/CrestApps.Core.AI.Chat.csproj index b5883147..f3d7f4eb 100644 --- a/src/Primitives/CrestApps.Core.AI.Chat/CrestApps.Core.AI.Chat.csproj +++ b/src/Primitives/CrestApps.Core.AI.Chat/CrestApps.Core.AI.Chat.csproj @@ -9,7 +9,7 @@ Chat services, SignalR hub contracts, and document processing tools for CrestApps AI. Framework-independent, usable in any ASP.NET Core application. - $(PackageTags) AI Chat SignalR + $(PackageTags) ai chat signalr sessions interactions diff --git a/src/Primitives/CrestApps.Core.AI.Copilot/CrestApps.Core.AI.Copilot.csproj b/src/Primitives/CrestApps.Core.AI.Copilot/CrestApps.Core.AI.Copilot.csproj index 286b451a..a3733bc2 100644 --- a/src/Primitives/CrestApps.Core.AI.Copilot/CrestApps.Core.AI.Copilot.csproj +++ b/src/Primitives/CrestApps.Core.AI.Copilot/CrestApps.Core.AI.Copilot.csproj @@ -8,7 +8,7 @@ GitHub Copilot SDK-based orchestrator for AI chat sessions. - $(PackageTags) AI Copilot + $(PackageTags) ai copilot github-copilot orchestration oauth diff --git a/src/Primitives/CrestApps.Core.AI.Ftp/CrestApps.Core.AI.Ftp.csproj b/src/Primitives/CrestApps.Core.AI.Ftp/CrestApps.Core.AI.Ftp.csproj index add943c0..6c26a1cb 100644 --- a/src/Primitives/CrestApps.Core.AI.Ftp/CrestApps.Core.AI.Ftp.csproj +++ b/src/Primitives/CrestApps.Core.AI.Ftp/CrestApps.Core.AI.Ftp.csproj @@ -8,7 +8,7 @@ FTP/FTPS resource support for CrestApps AI MCP server integrations. - $(PackageTags) AI MCP FTP FTPS + $(PackageTags) ai mcp ftp ftps resources diff --git a/src/Primitives/CrestApps.Core.AI.Markdown/CrestApps.Core.AI.Markdown.csproj b/src/Primitives/CrestApps.Core.AI.Markdown/CrestApps.Core.AI.Markdown.csproj index 15728be5..a5f6e326 100644 --- a/src/Primitives/CrestApps.Core.AI.Markdown/CrestApps.Core.AI.Markdown.csproj +++ b/src/Primitives/CrestApps.Core.AI.Markdown/CrestApps.Core.AI.Markdown.csproj @@ -8,7 +8,7 @@ Markdown-based text normalization helpers for CrestApps AI RAG and document-processing flows. - $(PackageTags) AI Markdown + $(PackageTags) ai markdown documents markdig diff --git a/src/Primitives/CrestApps.Core.AI.Mcp/CrestApps.Core.AI.Mcp.csproj b/src/Primitives/CrestApps.Core.AI.Mcp/CrestApps.Core.AI.Mcp.csproj index 7e5b2e40..ec93d284 100644 --- a/src/Primitives/CrestApps.Core.AI.Mcp/CrestApps.Core.AI.Mcp.csproj +++ b/src/Primitives/CrestApps.Core.AI.Mcp/CrestApps.Core.AI.Mcp.csproj @@ -8,7 +8,7 @@ $(CrestAppsDescription) Model Context Protocol (MCP) implementation for CrestApps AI services. -$(PackageTags) AI MCP +$(PackageTags) ai mcp model-context-protocol client server diff --git a/src/Primitives/CrestApps.Core.AI.Ollama/CrestApps.Core.AI.Ollama.csproj b/src/Primitives/CrestApps.Core.AI.Ollama/CrestApps.Core.AI.Ollama.csproj index 152dd4fc..a7b6de55 100644 --- a/src/Primitives/CrestApps.Core.AI.Ollama/CrestApps.Core.AI.Ollama.csproj +++ b/src/Primitives/CrestApps.Core.AI.Ollama/CrestApps.Core.AI.Ollama.csproj @@ -8,7 +8,7 @@ Ollama provider implementation for CrestApps AI services. - $(PackageTags) AI Ollama + $(PackageTags) ai ollama local-llm self-hosted diff --git a/src/Primitives/CrestApps.Core.AI.OpenAI.Azure/CrestApps.Core.AI.OpenAI.Azure.csproj b/src/Primitives/CrestApps.Core.AI.OpenAI.Azure/CrestApps.Core.AI.OpenAI.Azure.csproj index db84a5bd..b663a504 100644 --- a/src/Primitives/CrestApps.Core.AI.OpenAI.Azure/CrestApps.Core.AI.OpenAI.Azure.csproj +++ b/src/Primitives/CrestApps.Core.AI.OpenAI.Azure/CrestApps.Core.AI.OpenAI.Azure.csproj @@ -8,7 +8,7 @@ $(CrestAppsDescription) Azure OpenAI provider implementation for CrestApps AI services. -$(PackageTags) AI Azure OpenAI +$(PackageTags) ai azure-openai azure-speech openai diff --git a/src/Primitives/CrestApps.Core.AI.OpenAI/CrestApps.Core.AI.OpenAI.csproj b/src/Primitives/CrestApps.Core.AI.OpenAI/CrestApps.Core.AI.OpenAI.csproj index 2f400412..bbb05762 100644 --- a/src/Primitives/CrestApps.Core.AI.OpenAI/CrestApps.Core.AI.OpenAI.csproj +++ b/src/Primitives/CrestApps.Core.AI.OpenAI/CrestApps.Core.AI.OpenAI.csproj @@ -8,7 +8,7 @@ $(CrestAppsDescription) OpenAI provider implementation for CrestApps AI services. -$(PackageTags) AI OpenAI +$(PackageTags) ai openai chat embeddings diff --git a/src/Primitives/CrestApps.Core.AI.OpenXml/CrestApps.Core.AI.OpenXml.csproj b/src/Primitives/CrestApps.Core.AI.OpenXml/CrestApps.Core.AI.OpenXml.csproj index 1b4d4288..ae84f44b 100644 --- a/src/Primitives/CrestApps.Core.AI.OpenXml/CrestApps.Core.AI.OpenXml.csproj +++ b/src/Primitives/CrestApps.Core.AI.OpenXml/CrestApps.Core.AI.OpenXml.csproj @@ -8,7 +8,7 @@ OpenXml document ingestion services for CrestApps AI. - $(PackageTags) AI OpenXml + $(PackageTags) ai openxml documents office docx xlsx pptx diff --git a/src/Primitives/CrestApps.Core.AI.Pdf/CrestApps.Core.AI.Pdf.csproj b/src/Primitives/CrestApps.Core.AI.Pdf/CrestApps.Core.AI.Pdf.csproj index 72e761b5..8aa84f8b 100644 --- a/src/Primitives/CrestApps.Core.AI.Pdf/CrestApps.Core.AI.Pdf.csproj +++ b/src/Primitives/CrestApps.Core.AI.Pdf/CrestApps.Core.AI.Pdf.csproj @@ -8,7 +8,7 @@ PDF document ingestion services for CrestApps AI. - $(PackageTags) AI PDF + $(PackageTags) ai pdf documents extraction diff --git a/src/Primitives/CrestApps.Core.AI.Sftp/CrestApps.Core.AI.Sftp.csproj b/src/Primitives/CrestApps.Core.AI.Sftp/CrestApps.Core.AI.Sftp.csproj index db65fb77..57451f42 100644 --- a/src/Primitives/CrestApps.Core.AI.Sftp/CrestApps.Core.AI.Sftp.csproj +++ b/src/Primitives/CrestApps.Core.AI.Sftp/CrestApps.Core.AI.Sftp.csproj @@ -8,7 +8,7 @@ SFTP resource support for CrestApps AI MCP server integrations. - $(PackageTags) AI MCP SFTP SSH + $(PackageTags) ai mcp sftp ssh resources diff --git a/src/Primitives/CrestApps.Core.AI/CrestApps.Core.AI.csproj b/src/Primitives/CrestApps.Core.AI/CrestApps.Core.AI.csproj index f87587c2..e1e58ff2 100644 --- a/src/Primitives/CrestApps.Core.AI/CrestApps.Core.AI.csproj +++ b/src/Primitives/CrestApps.Core.AI/CrestApps.Core.AI.csproj @@ -9,7 +9,7 @@ Core AI service implementations including orchestration, tool registry, and completion services. Framework-independent, usable in any ASP.NET Core application. - $(PackageTags) AI Core + $(PackageTags) ai orchestration completions deployments profiles diff --git a/src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs b/src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs index 53f0b317..92065018 100644 --- a/src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs +++ b/src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs @@ -135,5 +135,21 @@ private async ValueTask FindBySelectorAsync(string selector) return await FindByNameAsync(selector); } - protected abstract ValueTask GetGlobalDefaultSelectorAsync(AIDeploymentType type); + private async ValueTask GetGlobalDefaultSelectorAsync(AIDeploymentType type) + { + var settings = await GetDefaultAIDeploymentSettingsAsync(); + + return type switch + { + AIDeploymentType.Chat => settings.DefaultChatDeploymentName, + AIDeploymentType.Utility => settings.DefaultUtilityDeploymentName, + AIDeploymentType.Embedding => settings.DefaultEmbeddingDeploymentName, + AIDeploymentType.Image => settings.DefaultImageDeploymentName, + AIDeploymentType.SpeechToText => settings.DefaultSpeechToTextDeploymentName, + AIDeploymentType.TextToSpeech => settings.DefaultTextToSpeechDeploymentName, + _ => null, + }; + } + + protected abstract ValueTask GetDefaultAIDeploymentSettingsAsync(); } diff --git a/src/Primitives/CrestApps.Core.AI/Services/ConfigurationAIDeploymentCatalog.cs b/src/Primitives/CrestApps.Core.AI/Services/ConfigurationAIDeploymentCatalog.cs index eebef1fc..b377e1d8 100644 --- a/src/Primitives/CrestApps.Core.AI/Services/ConfigurationAIDeploymentCatalog.cs +++ b/src/Primitives/CrestApps.Core.AI/Services/ConfigurationAIDeploymentCatalog.cs @@ -152,6 +152,14 @@ private async Task> GetConfigDeploymentsAsync( .Where(static deployment => !string.IsNullOrWhiteSpace(deployment.Name)) .ToDictionary(static deployment => deployment.Name, static deployment => deployment.ItemId, StringComparer.OrdinalIgnoreCase); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Evaluating AI deployment configuration. Stored deployments: {StoredDeploymentCount}. Deployment sections: [{DeploymentSections}]", + storedDeployments.Count, + string.Join(", ", _catalogOptions.DeploymentSections)); + } + try { ReadStandaloneDeployments(deployments, names); @@ -161,6 +169,13 @@ private async Task> GetConfigDeploymentsAsync( _logger.LogError(ex, "Error reading AI deployment configuration."); } + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Finished evaluating AI deployment configuration. Config-backed deployments discovered: {ConfiguredDeploymentCount}.", + deployments.Count); + } + return deployments.Values.ToArray(); } @@ -169,12 +184,33 @@ private void ReadStandaloneDeployments(Dictionary deployme foreach (var sectionPath in _catalogOptions.DeploymentSections) { var section = _configuration.GetSection(sectionPath); + var children = section.GetChildren().ToArray(); + + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Inspecting AI deployment section '{SectionPath}'. Exists: {SectionExists}. Child count: {ChildCount}. Child keys: [{ChildKeys}].", + sectionPath, + section.Exists(), + children.Length, + string.Join(", ", children.Select(static child => child.Key))); + } + if (!section.Exists()) { continue; } var deploymentsNode = ReadConfigurationNode(section); + + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Resolved AI deployment section '{SectionPath}' as {NodeType}.", + sectionPath, + GetNodeTypeName(deploymentsNode)); + } + switch (deploymentsNode) { case JsonArray deploymentArray: @@ -194,6 +230,14 @@ private void ReadStandaloneDeployments(Dictionary deployme private void ReadStandaloneDeploymentsFromArray(JsonArray deploymentArray, Dictionary deployments, Dictionary names, string sectionPath) { + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Reading {DeploymentCount} deployment entries from array section '{SectionPath}'.", + deploymentArray.Count, + sectionPath); + } + foreach (var deploymentNode in deploymentArray) { if (deploymentNode is not JsonObject deploymentObject) @@ -209,6 +253,14 @@ private void ReadStandaloneDeploymentsFromArray(JsonArray deploymentArray, Dicti private void ReadStandaloneDeploymentsFromObject(JsonObject deploymentObject, Dictionary deployments, Dictionary names, string sectionPath) { + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Reading provider-grouped deployment entries from section '{SectionPath}'. Providers: [{ProviderNames}].", + sectionPath, + string.Join(", ", deploymentObject.Select(static pair => pair.Key))); + } + foreach (var (providerName, providerDeploymentsNode) in deploymentObject) { if (providerDeploymentsNode is not JsonArray providerDeployments) @@ -251,6 +303,17 @@ private static AIDeploymentConfigurationEntry ParseStandaloneDeploymentEntry(Jso private AIDeployment CreateStandaloneDeployment(AIDeploymentConfigurationEntry entry) { + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Parsed AI deployment configuration entry. Provider: {ProviderName}. Name: {DeploymentName}. Model: {ModelName}. Type: {DeploymentType}. Property count: {PropertyCount}.", + entry.ProviderName, + entry.Name, + entry.ModelName, + entry.Type, + entry.Properties?.Count ?? 0); + } + if (string.IsNullOrWhiteSpace(entry.ProviderName)) { _logger.LogWarning("A standalone AI deployment entry is missing a ClientName. Skipping."); @@ -463,6 +526,28 @@ private void AddDeployment( names[deployment.Name] = deployment.ItemId; deployments[deployment.ItemId] = deployment; + + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug( + "Registered configuration-backed AI deployment '{DeploymentName}' from '{SourceDescription}' with item id '{DeploymentId}' and source '{DeploymentSource}'.", + deployment.Name, + sourceDescription, + deployment.ItemId, + deployment.Source); + } + } + + private static string GetNodeTypeName(JsonNode node) + { + return node switch + { + null => "null", + JsonArray => "array", + JsonObject => "object", + JsonValue => "scalar", + _ => node.GetType().Name, + }; } private static List Merge(IReadOnlyCollection primary, IReadOnlyCollection secondary) diff --git a/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs b/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs index 7c90e8c8..cd28c434 100644 --- a/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs +++ b/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs @@ -19,21 +19,6 @@ public DefaultAIDeploymentManager( _deploymentSettings = deploymentSettings; } - protected override ValueTask GetGlobalDefaultSelectorAsync(AIDeploymentType type) - { - var settings = _deploymentSettings.CurrentValue; - - var result = type switch - { - AIDeploymentType.Chat => settings.DefaultChatDeploymentName, - AIDeploymentType.Utility => settings.DefaultUtilityDeploymentName, - AIDeploymentType.Embedding => settings.DefaultEmbeddingDeploymentName, - AIDeploymentType.Image => settings.DefaultImageDeploymentName, - AIDeploymentType.SpeechToText => settings.DefaultSpeechToTextDeploymentName, - AIDeploymentType.TextToSpeech => settings.DefaultTextToSpeechDeploymentName, - _ => null, - }; - - return new ValueTask(result); - } + protected override ValueTask GetDefaultAIDeploymentSettingsAsync() + => ValueTask.FromResult(_deploymentSettings.CurrentValue); } diff --git a/src/Primitives/CrestApps.Core.Azure.AISearch/CrestApps.Core.Azure.AISearch.csproj b/src/Primitives/CrestApps.Core.Azure.AISearch/CrestApps.Core.Azure.AISearch.csproj index adc3a07c..78006b7d 100644 --- a/src/Primitives/CrestApps.Core.Azure.AISearch/CrestApps.Core.Azure.AISearch.csproj +++ b/src/Primitives/CrestApps.Core.Azure.AISearch/CrestApps.Core.Azure.AISearch.csproj @@ -8,7 +8,7 @@ Azure AI Search integration services for search index management, document indexing, vector search, and data source access. - $(PackageTags) AzureAI AzureAISearch Search + $(PackageTags) azure-ai-search vector-search indexing rag diff --git a/src/Primitives/CrestApps.Core.Azure/CrestApps.Core.Azure.csproj b/src/Primitives/CrestApps.Core.Azure/CrestApps.Core.Azure.csproj index 1ac07212..52f0bec2 100644 --- a/src/Primitives/CrestApps.Core.Azure/CrestApps.Core.Azure.csproj +++ b/src/Primitives/CrestApps.Core.Azure/CrestApps.Core.Azure.csproj @@ -7,7 +7,7 @@ Core services project for Azure project. - $(PackageTags) Azure + $(PackageTags) azure cloud utilities diff --git a/src/Primitives/CrestApps.Core.Elasticsearch/CrestApps.Core.Elasticsearch.csproj b/src/Primitives/CrestApps.Core.Elasticsearch/CrestApps.Core.Elasticsearch.csproj index 7cc3313e..0ccb94c8 100644 --- a/src/Primitives/CrestApps.Core.Elasticsearch/CrestApps.Core.Elasticsearch.csproj +++ b/src/Primitives/CrestApps.Core.Elasticsearch/CrestApps.Core.Elasticsearch.csproj @@ -8,7 +8,7 @@ Elasticsearch integration services for search index management, document indexing, vector search, and data source access. - $(PackageTags) Elasticsearch Search + $(PackageTags) elasticsearch vector-search indexing rag diff --git a/src/Primitives/CrestApps.Core.Infrastructure/CrestApps.Core.Infrastructure.csproj b/src/Primitives/CrestApps.Core.Infrastructure/CrestApps.Core.Infrastructure.csproj index 759ada17..f50dd16e 100644 --- a/src/Primitives/CrestApps.Core.Infrastructure/CrestApps.Core.Infrastructure.csproj +++ b/src/Primitives/CrestApps.Core.Infrastructure/CrestApps.Core.Infrastructure.csproj @@ -8,7 +8,7 @@ Shared non-AI infrastructure helpers and constants used by the CrestApps framework. - $(PackageTags) Infrastructure + $(PackageTags) infrastructure indexing search diff --git a/src/Primitives/CrestApps.Core.SignalR/CrestApps.Core.SignalR.csproj b/src/Primitives/CrestApps.Core.SignalR/CrestApps.Core.SignalR.csproj index 53ecc15b..480b4fad 100644 --- a/src/Primitives/CrestApps.Core.SignalR/CrestApps.Core.SignalR.csproj +++ b/src/Primitives/CrestApps.Core.SignalR/CrestApps.Core.SignalR.csproj @@ -8,7 +8,7 @@ SignalR hub route management for CrestApps services. - $(PackageTags) SignalR + $(PackageTags) signalr realtime chat hubs diff --git a/src/Primitives/CrestApps.Core.Templates/CrestApps.Core.Templates.csproj b/src/Primitives/CrestApps.Core.Templates/CrestApps.Core.Templates.csproj index 2c308f76..6970e614 100644 --- a/src/Primitives/CrestApps.Core.Templates/CrestApps.Core.Templates.csproj +++ b/src/Primitives/CrestApps.Core.Templates/CrestApps.Core.Templates.csproj @@ -8,7 +8,7 @@ A standalone template management library for .NET. Supports Liquid-based prompt templates with front matter metadata, file-based discovery, composition via include tags, and extensible metadata parsing. - $(PackageTags) Templating Templates Liquid + $(PackageTags) templating templates liquid prompts diff --git a/src/Primitives/CrestApps.Core/CrestApps.Core.csproj b/src/Primitives/CrestApps.Core/CrestApps.Core.csproj index 1ec3b33f..13f78319 100644 --- a/src/Primitives/CrestApps.Core/CrestApps.Core.csproj +++ b/src/Primitives/CrestApps.Core/CrestApps.Core.csproj @@ -7,7 +7,7 @@ Core service implementations for CrestApps.Core. Framework-independent, usable in any ASP.NET Core application. - $(PackageTags) Core + $(PackageTags) foundation dependency-injection catalogs utilities diff --git a/src/Startup/CrestApps.Core.Aspire.AppHost/CrestApps.Core.Aspire.AppHost.csproj b/src/Startup/CrestApps.Core.Aspire.AppHost/CrestApps.Core.Aspire.AppHost.csproj index 7688441e..da48a902 100644 --- a/src/Startup/CrestApps.Core.Aspire.AppHost/CrestApps.Core.Aspire.AppHost.csproj +++ b/src/Startup/CrestApps.Core.Aspire.AppHost/CrestApps.Core.Aspire.AppHost.csproj @@ -6,6 +6,7 @@ Exe true false + $(PackageTags) aspire apphost sample orchestration 0e4ae8bf-0fbf-43e7-b88a-91a70740841c diff --git a/src/Startup/CrestApps.Core.Mvc.Samples.A2AClient/CrestApps.Core.Mvc.Samples.A2AClient.csproj b/src/Startup/CrestApps.Core.Mvc.Samples.A2AClient/CrestApps.Core.Mvc.Samples.A2AClient.csproj index cc853a6c..64c84e14 100644 --- a/src/Startup/CrestApps.Core.Mvc.Samples.A2AClient/CrestApps.Core.Mvc.Samples.A2AClient.csproj +++ b/src/Startup/CrestApps.Core.Mvc.Samples.A2AClient/CrestApps.Core.Mvc.Samples.A2AClient.csproj @@ -3,6 +3,7 @@ enable false + $(PackageTags) mvc sample a2a client diff --git a/src/Startup/CrestApps.Core.Mvc.Samples.McpClient/CrestApps.Core.Mvc.Samples.McpClient.csproj b/src/Startup/CrestApps.Core.Mvc.Samples.McpClient/CrestApps.Core.Mvc.Samples.McpClient.csproj index aae747d6..4453c7f7 100644 --- a/src/Startup/CrestApps.Core.Mvc.Samples.McpClient/CrestApps.Core.Mvc.Samples.McpClient.csproj +++ b/src/Startup/CrestApps.Core.Mvc.Samples.McpClient/CrestApps.Core.Mvc.Samples.McpClient.csproj @@ -3,6 +3,7 @@ enable false + $(PackageTags) mvc sample mcp client diff --git a/src/Startup/CrestApps.Core.Mvc.Web/CrestApps.Core.Mvc.Web.csproj b/src/Startup/CrestApps.Core.Mvc.Web/CrestApps.Core.Mvc.Web.csproj index 39f49344..aa793d11 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/CrestApps.Core.Mvc.Web.csproj +++ b/src/Startup/CrestApps.Core.Mvc.Web/CrestApps.Core.Mvc.Web.csproj @@ -4,6 +4,7 @@ CrestApps.Core.Mvc.Web CrestApps-Mvc-Web false + $(PackageTags) mvc sample host admin-ui signalr $(DefaultItemExcludes);**\App_Data\** diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Services/YesSqlServiceCollectionExtensions.cs b/src/Startup/CrestApps.Core.Mvc.Web/Services/YesSqlServiceCollectionExtensions.cs index 412ee1fa..95c963dd 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Services/YesSqlServiceCollectionExtensions.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Services/YesSqlServiceCollectionExtensions.cs @@ -142,26 +142,26 @@ public static async Task InitializeYesSqlSchemaAsync(this IServiceProvider servi await using var transaction = await connection.BeginTransactionAsync(); var schemaBuilder = new SchemaBuilder(store.Configuration, transaction); await NormalizeLegacyDocumentTypeNamesAsync(store, connection, transaction, logger); - await TryCreateTableAsync(schemaBuilder.CreateAIProfileIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIProviderConnectionIndexSchemaAsync); + await TryCreateTableAsync(() => schemaBuilder.CreateAIProfileIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIProviderConnectionIndexSchemaAsync()); await TryCreateTableAsync(() => schemaBuilder.CreateMapIndexTableAsync(t => t.Column(nameof(A2AConnectionIndex.ItemId), c => c.WithLength(26)).Column(nameof(A2AConnectionIndex.DisplayText), c => c.WithLength(255)))); await TryCreateTableAsync(() => schemaBuilder.CreateMapIndexTableAsync(t => t.Column(nameof(McpConnectionIndex.ItemId), c => c.WithLength(26)).Column(nameof(McpConnectionIndex.DisplayText), c => c.WithLength(255)).Column(nameof(McpConnectionIndex.Source), c => c.WithLength(50)))); await TryCreateTableAsync(() => schemaBuilder.CreateMapIndexTableAsync(t => t.Column(nameof(McpPromptIndex.ItemId), c => c.WithLength(26)).Column(nameof(McpPromptIndex.Name), c => c.WithLength(255)))); await TryCreateTableAsync(() => schemaBuilder.CreateMapIndexTableAsync(t => t.Column(nameof(McpResourceIndex.ItemId), c => c.WithLength(26)).Column(nameof(McpResourceIndex.DisplayText), c => c.WithLength(255)).Column(nameof(McpResourceIndex.Source), c => c.WithLength(50)))); - await TryCreateTableAsync(schemaBuilder.CreateAIDeploymentIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIProfileTemplateIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIChatSessionIndexSchemaAsync); + await TryCreateTableAsync(() => schemaBuilder.CreateAIDeploymentIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIProfileTemplateIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIChatSessionIndexSchemaAsync()); await TryCreateTableAsync(() => schemaBuilder.CreateAIChatSessionMetricsSchemaAsync(new AIChatSessionMetricsIndexSchemaOptions())); - await TryCreateTableAsync(schemaBuilder.CreateAICompletionUsageIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIChatSessionExtractedDataIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIChatSessionPromptIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIDocumentIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIDocumentChunkIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateSearchIndexProfileIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIDataSourceIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateAIMemoryEntryIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateChatInteractionIndexSchemaAsync); - await TryCreateTableAsync(schemaBuilder.CreateChatInteractionPromptIndexSchemaAsync); + await TryCreateTableAsync(() => schemaBuilder.CreateAICompletionUsageIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIChatSessionExtractedDataIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIChatSessionPromptIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIDocumentIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIDocumentChunkIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateSearchIndexProfileIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIDataSourceIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateAIMemoryEntryIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateChatInteractionIndexSchemaAsync()); + await TryCreateTableAsync(() => schemaBuilder.CreateChatInteractionPromptIndexSchemaAsync()); await TryCreateTableAsync(() => schemaBuilder.CreateMapIndexTableAsync(t => t.Column(nameof(ArticleIndex.ItemId), c => c.WithLength(26)).Column(nameof(ArticleIndex.Title), c => c.WithLength(255)))); await transaction.CommitAsync(); } diff --git a/src/Stores/CrestApps.Core.Data.EntityCore/CrestApps.Core.Data.EntityCore.csproj b/src/Stores/CrestApps.Core.Data.EntityCore/CrestApps.Core.Data.EntityCore.csproj index 168e7dd8..3824dbfc 100644 --- a/src/Stores/CrestApps.Core.Data.EntityCore/CrestApps.Core.Data.EntityCore.csproj +++ b/src/Stores/CrestApps.Core.Data.EntityCore/CrestApps.Core.Data.EntityCore.csproj @@ -9,7 +9,7 @@ $(CrestAppsDescription) Entity Framework Core-based store implementations for CrestApps services. Optional - consumers can provide their own store implementations. - $(PackageTags) EntityFrameworkCore Store + $(PackageTags) efcore entity-framework-core persistence store diff --git a/src/Stores/CrestApps.Core.Data.YesSql/CrestApps.Core.Data.YesSql.csproj b/src/Stores/CrestApps.Core.Data.YesSql/CrestApps.Core.Data.YesSql.csproj index 86671b50..c7256488 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/CrestApps.Core.Data.YesSql.csproj +++ b/src/Stores/CrestApps.Core.Data.YesSql/CrestApps.Core.Data.YesSql.csproj @@ -9,7 +9,7 @@ $(CrestAppsDescription) YesSql-based document store implementations for CrestApps services. Optional - consumers can provide their own store implementations. -$(PackageTags) YesSql Store +$(PackageTags) yessql persistence store catalogs diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIDeploymentIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIDeploymentIndexSchemaBuilderExtensions.cs index 671e0f9e..4625b64d 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIDeploymentIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIDeploymentIndexSchemaBuilderExtensions.cs @@ -4,8 +4,12 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AI; public static class AIDeploymentIndexSchemaBuilderExtensions { - public static Task CreateAIDeploymentIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIDeploymentIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIDeploymentIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIDeploymentIndex.Name), column => column.WithLength(255)).Column(nameof(AIDeploymentIndex.Source), column => column.WithLength(255))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIDeploymentIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIDeploymentIndex.Name), column => column.WithLength(255)) + .Column(nameof(AIDeploymentIndex.Source), column => column.WithLength(255)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileIndexSchemaBuilderExtensions.cs index d7828724..75443a15 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileIndexSchemaBuilderExtensions.cs @@ -4,8 +4,12 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AI; public static class AIProfileIndexSchemaBuilderExtensions { - public static Task CreateAIProfileIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIProfileIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIProfileIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIProfileIndex.Name), column => column.WithLength(255)).Column(nameof(AIProfileIndex.Source), column => column.WithLength(255))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIProfileIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIProfileIndex.Name), column => column.WithLength(255)) + .Column(nameof(AIProfileIndex.Source), column => column.WithLength(255)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileTemplateIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileTemplateIndexSchemaBuilderExtensions.cs index 658be54b..c81049c4 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileTemplateIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProfileTemplateIndexSchemaBuilderExtensions.cs @@ -4,8 +4,12 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AI; public static class AIProfileTemplateIndexSchemaBuilderExtensions { - public static Task CreateAIProfileTemplateIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIProfileTemplateIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIProfileTemplateIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIProfileTemplateIndex.Name), column => column.WithLength(255)).Column(nameof(AIProfileTemplateIndex.Source), column => column.WithLength(255))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIProfileTemplateIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIProfileTemplateIndex.Name), column => column.WithLength(255)) + .Column(nameof(AIProfileTemplateIndex.Source), column => column.WithLength(255)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProviderConnectionIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProviderConnectionIndexSchemaBuilderExtensions.cs index ff16ec65..4c704482 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProviderConnectionIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AI/AIProviderConnectionIndexSchemaBuilderExtensions.cs @@ -4,8 +4,12 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AI; public static class AIProviderConnectionIndexSchemaBuilderExtensions { - public static Task CreateAIProviderConnectionIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIProviderConnectionIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIProviderConnectionIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIProviderConnectionIndex.Name), column => column.WithLength(255)).Column(nameof(AIProviderConnectionIndex.Source), column => column.WithLength(255))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIProviderConnectionIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIProviderConnectionIndex.Name), column => column.WithLength(255)) + .Column(nameof(AIProviderConnectionIndex.Source), column => column.WithLength(255)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionExtractedDataIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionExtractedDataIndexSchemaBuilderExtensions.cs index 7edc0bed..053603f5 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionExtractedDataIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionExtractedDataIndexSchemaBuilderExtensions.cs @@ -4,8 +4,17 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AIChat; public static class AIChatSessionExtractedDataIndexSchemaBuilderExtensions { - public static Task CreateAIChatSessionExtractedDataIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIChatSessionExtractedDataIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIChatSessionExtractedDataIndex.SessionId), column => column.WithLength(44)).Column(nameof(AIChatSessionExtractedDataIndex.ProfileId), column => column.WithLength(26)).Column(nameof(AIChatSessionExtractedDataIndex.SessionStartedUtc)).Column(nameof(AIChatSessionExtractedDataIndex.SessionEndedUtc)).Column(nameof(AIChatSessionExtractedDataIndex.FieldCount)).Column(nameof(AIChatSessionExtractedDataIndex.FieldNames), column => column.WithLength(4000)).Column(nameof(AIChatSessionExtractedDataIndex.ValuesText), column => column.WithLength(4000)).Column(nameof(AIChatSessionExtractedDataIndex.UpdatedUtc))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIChatSessionExtractedDataIndex.SessionId), column => column.WithLength(26)) + .Column(nameof(AIChatSessionExtractedDataIndex.ProfileId), column => column.WithLength(26)) + .Column(nameof(AIChatSessionExtractedDataIndex.SessionStartedUtc)) + .Column(nameof(AIChatSessionExtractedDataIndex.SessionEndedUtc)) + .Column(nameof(AIChatSessionExtractedDataIndex.FieldCount)) + .Column(nameof(AIChatSessionExtractedDataIndex.FieldNames), column => column.WithLength(4000)) + .Column(nameof(AIChatSessionExtractedDataIndex.ValuesText), column => column.WithLength(4000)) + .Column(nameof(AIChatSessionExtractedDataIndex.UpdatedUtc)) + , collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionIndexSchemaBuilderExtensions.cs index fd2af656..839571f8 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionIndexSchemaBuilderExtensions.cs @@ -4,8 +4,15 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AIChat; public static class AIChatSessionIndexSchemaBuilderExtensions { - public static Task CreateAIChatSessionIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIChatSessionIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIChatSessionIndex.ItemId), column => column.WithLength(44)).Column(nameof(AIChatSessionIndex.SessionId), column => column.WithLength(44)).Column(nameof(AIChatSessionIndex.ProfileId), column => column.WithLength(26)).Column(nameof(AIChatSessionIndex.UserId), column => column.WithLength(255)).Column(nameof(AIChatSessionIndex.Status)).Column(nameof(AIChatSessionIndex.LastActivityUtc))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIChatSessionIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIChatSessionIndex.SessionId), column => column.WithLength(26)) + .Column(nameof(AIChatSessionIndex.ProfileId), column => column.WithLength(26)) + .Column(nameof(AIChatSessionIndex.UserId), column => column.WithLength(255)) + .Column(nameof(AIChatSessionIndex.Status)) + .Column(nameof(AIChatSessionIndex.LastActivityUtc)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionMetricsIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionMetricsIndexSchemaBuilderExtensions.cs index f0f756f6..59c01cba 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionMetricsIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionMetricsIndexSchemaBuilderExtensions.cs @@ -4,34 +4,91 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AIChat; public static class AIChatSessionMetricsIndexSchemaBuilderExtensions { - public static async Task CreateAIChatSessionMetricsSchemaAsync(this ISchemaBuilder schemaBuilder, AIChatSessionMetricsIndexSchemaOptions options = null) + public static async Task CreateAIChatSessionMetricsSchemaAsync(this ISchemaBuilder schemaBuilder, AIChatSessionMetricsIndexSchemaOptions options = null, string collection = null) { - options ??= new AIChatSessionMetricsIndexSchemaOptions(); + options = NormalizeOptions(options, collection); await schemaBuilder.CreateAIChatSessionMetricsIndexTableAsync(options); await schemaBuilder.CreateAIChatSessionMetricsNamedIndexesAsync(options); } - public static Task CreateAIChatSessionMetricsIndexTableAsync(this ISchemaBuilder schemaBuilder, AIChatSessionMetricsIndexSchemaOptions options = null) + public static Task CreateAIChatSessionMetricsIndexTableAsync(this ISchemaBuilder schemaBuilder, AIChatSessionMetricsIndexSchemaOptions options = null, string collection = null) { - options ??= new AIChatSessionMetricsIndexSchemaOptions(); - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIChatSessionMetricsIndex.SessionId), column => column.WithLength(options.SessionIdLength)).Column(nameof(AIChatSessionMetricsIndex.ProfileId), column => column.WithLength(options.ProfileIdLength)).Column(nameof(AIChatSessionMetricsIndex.VisitorId), column => column.WithLength(options.VisitorIdLength)).Column(nameof(AIChatSessionMetricsIndex.UserId), column => column.WithLength(options.UserIdLength)).Column(nameof(AIChatSessionMetricsIndex.IsAuthenticated)).Column(nameof(AIChatSessionMetricsIndex.SessionStartedUtc)).Column(nameof(AIChatSessionMetricsIndex.SessionEndedUtc)).Column(nameof(AIChatSessionMetricsIndex.MessageCount)).Column(nameof(AIChatSessionMetricsIndex.HandleTimeSeconds)).Column(nameof(AIChatSessionMetricsIndex.IsResolved)).Column(nameof(AIChatSessionMetricsIndex.HourOfDay)).Column(nameof(AIChatSessionMetricsIndex.DayOfWeek)).Column(nameof(AIChatSessionMetricsIndex.TotalInputTokens), column => column.WithDefault(0)).Column(nameof(AIChatSessionMetricsIndex.TotalOutputTokens), column => column.WithDefault(0)).Column(nameof(AIChatSessionMetricsIndex.AverageResponseLatencyMs), column => column.WithDefault(0)).Column(nameof(AIChatSessionMetricsIndex.CompletionCount), column => column.WithDefault(0)).Column(nameof(AIChatSessionMetricsIndex.UserRating), column => column.Nullable()).Column(nameof(AIChatSessionMetricsIndex.ThumbsUpCount), column => column.WithDefault(0)).Column(nameof(AIChatSessionMetricsIndex.ThumbsDownCount), column => column.WithDefault(0)).Column(nameof(AIChatSessionMetricsIndex.ConversionScore), column => column.Nullable()).Column(nameof(AIChatSessionMetricsIndex.ConversionMaxScore), column => column.Nullable()).Column(nameof(AIChatSessionMetricsIndex.CreatedUtc)), collection: options.CollectionName); + options = NormalizeOptions(options, collection); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIChatSessionMetricsIndex.SessionId), column => column.WithLength(options.SessionIdLength)) + .Column(nameof(AIChatSessionMetricsIndex.ProfileId), column => column.WithLength(options.ProfileIdLength)) + .Column(nameof(AIChatSessionMetricsIndex.VisitorId), column => column.WithLength(options.VisitorIdLength)) + .Column(nameof(AIChatSessionMetricsIndex.UserId), column => column.WithLength(options.UserIdLength)) + .Column(nameof(AIChatSessionMetricsIndex.IsAuthenticated)) + .Column(nameof(AIChatSessionMetricsIndex.SessionStartedUtc)) + .Column(nameof(AIChatSessionMetricsIndex.SessionEndedUtc)) + .Column(nameof(AIChatSessionMetricsIndex.MessageCount)) + .Column(nameof(AIChatSessionMetricsIndex.HandleTimeSeconds)) + .Column(nameof(AIChatSessionMetricsIndex.IsResolved)) + .Column(nameof(AIChatSessionMetricsIndex.HourOfDay)) + .Column(nameof(AIChatSessionMetricsIndex.DayOfWeek)) + .Column(nameof(AIChatSessionMetricsIndex.TotalInputTokens), column => column.WithDefault(0)) + .Column(nameof(AIChatSessionMetricsIndex.TotalOutputTokens), column => column.WithDefault(0)) + .Column(nameof(AIChatSessionMetricsIndex.AverageResponseLatencyMs), column => column.WithDefault(0)) + .Column(nameof(AIChatSessionMetricsIndex.CompletionCount), column => column.WithDefault(0)) + .Column(nameof(AIChatSessionMetricsIndex.UserRating), column => column.Nullable()) + .Column(nameof(AIChatSessionMetricsIndex.ThumbsUpCount), column => column.WithDefault(0)) + .Column(nameof(AIChatSessionMetricsIndex.ThumbsDownCount), column => column.WithDefault(0)) + .Column(nameof(AIChatSessionMetricsIndex.ConversionScore), column => column.Nullable()) + .Column(nameof(AIChatSessionMetricsIndex.ConversionMaxScore), column => column.Nullable()) + .Column(nameof(AIChatSessionMetricsIndex.CreatedUtc)), + collection: options.CollectionName); } - public static Task CreateAIChatSessionMetricsNamedIndexesAsync(this ISchemaBuilder schemaBuilder, AIChatSessionMetricsIndexSchemaOptions options) + public static Task CreateAIChatSessionMetricsNamedIndexesAsync(this ISchemaBuilder schemaBuilder, AIChatSessionMetricsIndexSchemaOptions options = null, string collection = null) { - if (options?.CreateNamedIndexes != true) + options = NormalizeOptions(options, collection); + + if (!options.CreateNamedIndexes) { return Task.CompletedTask; } - return Task.WhenAll(schemaBuilder.AlterIndexTableAsync(table => table.CreateIndex("IDX_AIChatSessionMetrics_DocumentId", "DocumentId", nameof(AIChatSessionMetricsIndex.SessionId), nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.CreatedUtc)), collection: options.CollectionName), schemaBuilder.AlterIndexTableAsync(table => table.CreateIndex("IDX_AIChatSessionMetrics_ProfileDate", "DocumentId", nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.SessionStartedUtc), nameof(AIChatSessionMetricsIndex.SessionEndedUtc), nameof(AIChatSessionMetricsIndex.IsResolved)), collection: options.CollectionName), schemaBuilder.AlterIndexTableAsync(table => table.CreateIndex("IDX_AIChatSessionMetrics_VisitorId", "DocumentId", nameof(AIChatSessionMetricsIndex.VisitorId), nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.SessionStartedUtc)), collection: options.CollectionName), schemaBuilder.AlterIndexTableAsync(table => table.CreateIndex("IDX_AIChatSessionMetrics_TimeOfDay", "DocumentId", nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.HourOfDay), nameof(AIChatSessionMetricsIndex.DayOfWeek), nameof(AIChatSessionMetricsIndex.SessionStartedUtc)), collection: options.CollectionName)); + return Task.WhenAll( + schemaBuilder.AlterIndexTableAsync( + table => table.CreateIndex("IDX_AIChatSessionMetrics_DocumentId", "DocumentId", nameof(AIChatSessionMetricsIndex.SessionId), nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.CreatedUtc)), + collection: options.CollectionName), + schemaBuilder.AlterIndexTableAsync( + table => table.CreateIndex("IDX_AIChatSessionMetrics_ProfileDate", "DocumentId", nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.SessionStartedUtc), nameof(AIChatSessionMetricsIndex.SessionEndedUtc), nameof(AIChatSessionMetricsIndex.IsResolved)), + collection: options.CollectionName), + schemaBuilder.AlterIndexTableAsync( + table => table.CreateIndex("IDX_AIChatSessionMetrics_VisitorId", "DocumentId", nameof(AIChatSessionMetricsIndex.VisitorId), nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.SessionStartedUtc)), + collection: options.CollectionName), + schemaBuilder.AlterIndexTableAsync( + table => table.CreateIndex("IDX_AIChatSessionMetrics_TimeOfDay", "DocumentId", nameof(AIChatSessionMetricsIndex.ProfileId), nameof(AIChatSessionMetricsIndex.HourOfDay), nameof(AIChatSessionMetricsIndex.DayOfWeek), nameof(AIChatSessionMetricsIndex.SessionStartedUtc)), + collection: options.CollectionName)); } - public static Task AddAIChatSessionMetricsCompletionCountColumnAsync(this ISchemaBuilder schemaBuilder, string collectionName = null) + public static Task AddAIChatSessionMetricsCompletionCountColumnAsync(this ISchemaBuilder schemaBuilder, string collection = null) { return schemaBuilder.AlterIndexTableAsync(table => { table.AddColumn(nameof(AIChatSessionMetricsIndex.CompletionCount), column => column.WithDefault(0)); - }, collection: collectionName); + }, collection: collection); + } + + private static AIChatSessionMetricsIndexSchemaOptions NormalizeOptions(AIChatSessionMetricsIndexSchemaOptions options, string collection) + { + options ??= new AIChatSessionMetricsIndexSchemaOptions(); + + if (collection == null) + { + return options; + } + + return new AIChatSessionMetricsIndexSchemaOptions + { + CollectionName = collection, + SessionIdLength = options.SessionIdLength, + ProfileIdLength = options.ProfileIdLength, + VisitorIdLength = options.VisitorIdLength, + UserIdLength = options.UserIdLength, + CreateNamedIndexes = options.CreateNamedIndexes, + }; } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionPromptIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionPromptIndexSchemaBuilderExtensions.cs index 1b6ec143..50211ac4 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionPromptIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AIChatSessionPromptIndexSchemaBuilderExtensions.cs @@ -4,8 +4,12 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AIChat; public static class AIChatSessionPromptIndexSchemaBuilderExtensions { - public static Task CreateAIChatSessionPromptIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIChatSessionPromptIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIChatSessionPromptIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIChatSessionPromptIndex.SessionId), column => column.WithLength(44)).Column(nameof(AIChatSessionPromptIndex.Role), column => column.WithLength(50))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIChatSessionPromptIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIChatSessionPromptIndex.SessionId), column => column.WithLength(26)) + .Column(nameof(AIChatSessionPromptIndex.Role), column => column.WithLength(50)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AICompletionUsageIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AICompletionUsageIndexSchemaBuilderExtensions.cs index f76a471c..afd22356 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AICompletionUsageIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIChat/AICompletionUsageIndexSchemaBuilderExtensions.cs @@ -4,8 +4,30 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AIChat; public static class AICompletionUsageIndexSchemaBuilderExtensions { - public static Task CreateAICompletionUsageIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAICompletionUsageIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AICompletionUsageIndex.ContextType), column => column.WithLength(64)).Column(nameof(AICompletionUsageIndex.SessionId), column => column.WithLength(44)).Column(nameof(AICompletionUsageIndex.ProfileId), column => column.WithLength(26)).Column(nameof(AICompletionUsageIndex.InteractionId), column => column.WithLength(26)).Column(nameof(AICompletionUsageIndex.UserId), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.UserName), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.VisitorId), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.ClientId), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.IsAuthenticated)).Column(nameof(AICompletionUsageIndex.ProviderName), column => column.WithLength(128)).Column(nameof(AICompletionUsageIndex.ClientName), column => column.WithLength(128)).Column(nameof(AICompletionUsageIndex.ConnectionName), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.DeploymentName), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.ModelName), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.ResponseId), column => column.WithLength(255)).Column(nameof(AICompletionUsageIndex.IsStreaming)).Column(nameof(AICompletionUsageIndex.InputTokenCount)).Column(nameof(AICompletionUsageIndex.OutputTokenCount)).Column(nameof(AICompletionUsageIndex.TotalTokenCount)).Column(nameof(AICompletionUsageIndex.ResponseLatencyMs)).Column(nameof(AICompletionUsageIndex.CreatedUtc))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AICompletionUsageIndex.ContextType), column => column.WithLength(64)) + .Column(nameof(AICompletionUsageIndex.SessionId), column => column.WithLength(26)) + .Column(nameof(AICompletionUsageIndex.ProfileId), column => column.WithLength(26)) + .Column(nameof(AICompletionUsageIndex.InteractionId), column => column.WithLength(26)) + .Column(nameof(AICompletionUsageIndex.UserId), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.UserName), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.VisitorId), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.ClientId), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.IsAuthenticated)) + .Column(nameof(AICompletionUsageIndex.ProviderName), column => column.WithLength(128)) + .Column(nameof(AICompletionUsageIndex.ClientName), column => column.WithLength(128)) + .Column(nameof(AICompletionUsageIndex.ConnectionName), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.DeploymentName), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.ModelName), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.ResponseId), column => column.WithLength(255)) + .Column(nameof(AICompletionUsageIndex.IsStreaming)) + .Column(nameof(AICompletionUsageIndex.InputTokenCount)) + .Column(nameof(AICompletionUsageIndex.OutputTokenCount)) + .Column(nameof(AICompletionUsageIndex.TotalTokenCount)) + .Column(nameof(AICompletionUsageIndex.ResponseLatencyMs)) + .Column(nameof(AICompletionUsageIndex.CreatedUtc)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIMemory/AIMemoryEntryIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIMemory/AIMemoryEntryIndexSchemaBuilderExtensions.cs index 524f7712..839f3206 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIMemory/AIMemoryEntryIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/AIMemory/AIMemoryEntryIndexSchemaBuilderExtensions.cs @@ -4,8 +4,12 @@ namespace CrestApps.Core.Data.YesSql.Indexes.AIMemory; public static class AIMemoryEntryIndexSchemaBuilderExtensions { - public static Task CreateAIMemoryEntryIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIMemoryEntryIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIMemoryEntryIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIMemoryEntryIndex.UserId), column => column.WithLength(255)).Column(nameof(AIMemoryEntryIndex.Name), column => column.WithLength(255))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIMemoryEntryIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIMemoryEntryIndex.UserId), column => column.WithLength(255)) + .Column(nameof(AIMemoryEntryIndex.Name), column => column.WithLength(255)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionIndexSchemaBuilderExtensions.cs index 346d5347..f1a577b3 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionIndexSchemaBuilderExtensions.cs @@ -4,8 +4,13 @@ namespace CrestApps.Core.Data.YesSql.Indexes.ChatInteractions; public static class ChatInteractionIndexSchemaBuilderExtensions { - public static Task CreateChatInteractionIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateChatInteractionIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(ChatInteractionIndex.ItemId), column => column.WithLength(26)).Column(nameof(ChatInteractionIndex.UserId), column => column.WithLength(255)).Column(nameof(ChatInteractionIndex.Title), column => column.WithLength(255)).Column(nameof(ChatInteractionIndex.CreatedUtc))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(ChatInteractionIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(ChatInteractionIndex.UserId), column => column.WithLength(255)) + .Column(nameof(ChatInteractionIndex.Title), column => column.WithLength(255)) + .Column(nameof(ChatInteractionIndex.CreatedUtc)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionPromptIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionPromptIndexSchemaBuilderExtensions.cs index 804b61a4..d92960b4 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionPromptIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/ChatInteractions/ChatInteractionPromptIndexSchemaBuilderExtensions.cs @@ -4,8 +4,13 @@ namespace CrestApps.Core.Data.YesSql.Indexes.ChatInteractions; public static class ChatInteractionPromptIndexSchemaBuilderExtensions { - public static Task CreateChatInteractionPromptIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateChatInteractionPromptIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(ChatInteractionPromptIndex.ItemId), column => column.WithLength(26)).Column(nameof(ChatInteractionPromptIndex.ChatInteractionId), column => column.WithLength(26)).Column(nameof(ChatInteractionPromptIndex.Role), column => column.WithLength(50)).Column(nameof(ChatInteractionPromptIndex.CreatedUtc))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(ChatInteractionPromptIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(ChatInteractionPromptIndex.ChatInteractionId), column => column.WithLength(26)) + .Column(nameof(ChatInteractionPromptIndex.Role), column => column.WithLength(50)) + .Column(nameof(ChatInteractionPromptIndex.CreatedUtc)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/DataSources/AIDataSourceIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/DataSources/AIDataSourceIndexSchemaBuilderExtensions.cs index cad5b9b3..585a5ab9 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/DataSources/AIDataSourceIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/DataSources/AIDataSourceIndexSchemaBuilderExtensions.cs @@ -4,8 +4,12 @@ namespace CrestApps.Core.Data.YesSql.Indexes.DataSources; public static class AIDataSourceIndexSchemaBuilderExtensions { - public static Task CreateAIDataSourceIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIDataSourceIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIDataSourceIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIDataSourceIndex.DisplayText), column => column.WithLength(255)).Column(nameof(AIDataSourceIndex.SourceIndexProfileName), column => column.WithLength(255))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIDataSourceIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIDataSourceIndex.DisplayText), column => column.WithLength(255)) + .Column(nameof(AIDataSourceIndex.SourceIndexProfileName), column => column.WithLength(255)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentChunkIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentChunkIndexSchemaBuilderExtensions.cs index 01fb0bdb..f5e84a1e 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentChunkIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentChunkIndexSchemaBuilderExtensions.cs @@ -4,8 +4,14 @@ namespace CrestApps.Core.Data.YesSql.Indexes.Indexing; public static class AIDocumentChunkIndexSchemaBuilderExtensions { - public static Task CreateAIDocumentChunkIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIDocumentChunkIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIDocumentChunkIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIDocumentChunkIndex.AIDocumentId), column => column.WithLength(26)).Column(nameof(AIDocumentChunkIndex.ReferenceId), column => column.WithLength(26)).Column(nameof(AIDocumentChunkIndex.ReferenceType), column => column.WithLength(50)).Column(nameof(AIDocumentChunkIndex.Index))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIDocumentChunkIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIDocumentChunkIndex.AIDocumentId), column => column.WithLength(26)) + .Column(nameof(AIDocumentChunkIndex.ReferenceId), column => column.WithLength(26)) + .Column(nameof(AIDocumentChunkIndex.ReferenceType), column => column.WithLength(50)) + .Column(nameof(AIDocumentChunkIndex.Index)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentIndexSchemaBuilderExtensions.cs index 58a7f71a..231262b1 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/AIDocumentIndexSchemaBuilderExtensions.cs @@ -4,8 +4,13 @@ namespace CrestApps.Core.Data.YesSql.Indexes.Indexing; public static class AIDocumentIndexSchemaBuilderExtensions { - public static Task CreateAIDocumentIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateAIDocumentIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(AIDocumentIndex.ItemId), column => column.WithLength(26)).Column(nameof(AIDocumentIndex.ReferenceId), column => column.WithLength(26)).Column(nameof(AIDocumentIndex.ReferenceType), column => column.WithLength(50)).Column(nameof(AIDocumentIndex.FileName), column => column.WithLength(255))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AIDocumentIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(AIDocumentIndex.ReferenceId), column => column.WithLength(26)) + .Column(nameof(AIDocumentIndex.ReferenceType), column => column.WithLength(50)) + .Column(nameof(AIDocumentIndex.FileName), column => column.WithLength(255)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/SearchIndexProfileIndexSchemaBuilderExtensions.cs b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/SearchIndexProfileIndexSchemaBuilderExtensions.cs index 5f106124..73728de0 100644 --- a/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/SearchIndexProfileIndexSchemaBuilderExtensions.cs +++ b/src/Stores/CrestApps.Core.Data.YesSql/Indexes/Indexing/SearchIndexProfileIndexSchemaBuilderExtensions.cs @@ -4,8 +4,13 @@ namespace CrestApps.Core.Data.YesSql.Indexes.Indexing; public static class SearchIndexProfileIndexSchemaBuilderExtensions { - public static Task CreateSearchIndexProfileIndexSchemaAsync(this ISchemaBuilder schemaBuilder) + public static Task CreateSearchIndexProfileIndexSchemaAsync(this ISchemaBuilder schemaBuilder, string collection = null) { - return schemaBuilder.CreateMapIndexTableAsync(table => table.Column(nameof(SearchIndexProfileIndex.ItemId), column => column.WithLength(26)).Column(nameof(SearchIndexProfileIndex.Name), column => column.WithLength(255)).Column(nameof(SearchIndexProfileIndex.ProviderName), column => column.WithLength(50)).Column(nameof(SearchIndexProfileIndex.Type), column => column.WithLength(50))); + return schemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(SearchIndexProfileIndex.ItemId), column => column.WithLength(26)) + .Column(nameof(SearchIndexProfileIndex.Name), column => column.WithLength(255)) + .Column(nameof(SearchIndexProfileIndex.ProviderName), column => column.WithLength(50)) + .Column(nameof(SearchIndexProfileIndex.Type), column => column.WithLength(50)), + collection: collection); } -} \ No newline at end of file +} diff --git a/src/Utilities/CrestApps.Core.Support/CrestApps.Core.Support.csproj b/src/Utilities/CrestApps.Core.Support/CrestApps.Core.Support.csproj index 7d86c436..040e1e34 100644 --- a/src/Utilities/CrestApps.Core.Support/CrestApps.Core.Support.csproj +++ b/src/Utilities/CrestApps.Core.Support/CrestApps.Core.Support.csproj @@ -8,7 +8,7 @@ Offer a comprehensive set of C# utility methods for common tasks involving strings, numbers, types, enums, date-time operations, and more - $(PackageTags) Support + $(PackageTags) utilities helpers extensions diff --git a/tests/CrestApps.Core.Tests/CrestApps.Core.Tests.csproj b/tests/CrestApps.Core.Tests/CrestApps.Core.Tests.csproj index 824732fe..5e75d66a 100644 --- a/tests/CrestApps.Core.Tests/CrestApps.Core.Tests.csproj +++ b/tests/CrestApps.Core.Tests/CrestApps.Core.Tests.csproj @@ -4,6 +4,7 @@ enable false true + $(PackageTags) tests xunit unit-tests integration-tests $(NoWarn);CA1852