diff --git a/.models b/.models
index 7880248a..6f7747fc 100644
--- a/.models
+++ b/.models
@@ -1,4 +1,6 @@
-gemma:2b
-llama3:8b
+gemma2:2b
+llama3.1:8b
+phi3:mini
+
+#Place any new model here and it will be downloaded during start command
-#Place any new model here and it will be downloaded during start command
\ No newline at end of file
diff --git a/Docs/Images/temp/example.json b/Docs/Images/temp/example.json
new file mode 100644
index 00000000..07aed352
--- /dev/null
+++ b/Docs/Images/temp/example.json
@@ -0,0 +1,59 @@
+{
+ "id": "d2f191c7-f08b-4285-b0d6-bb99a045ebde",
+ "name": "agent_one",
+ "description": "This is the first RAG agent",
+ "model": "gemma:2b",
+ "context":
+ {
+ "instruction": "You are shop assistant in a GeekITStuff store. You have to help the customer in finding the right product",
+ "source":
+ {
+ "type": "api", //can be api/sql/text/file as we know it now
+ "details":
+ {
+ "url": "https://api.example.com/@filter@",
+ "method": "GET",
+ "query": "",
+ "payload": ""
+ //In future we will also have to add authentication details
+ }
+ },
+ "steps": ["ANSWER"],
+ //"steps": ["REDIRECT_ac243657-5ab1-4727-b4be-1ea5ae2e76d3", "FETCH_DATA_WITH_FILTER", "ANSWER", "REDIRECT_b29211e9-9ee8-45f4-bdbb-054cb835d0d6"],
+ "relations": //connections that this agent can make to other agents
+ [
+ {
+ "id": "ac243657-5ab1-4727-b4be-1ea5ae2e76d3",
+ "agentPurpose": "category detection",
+ },
+ {
+ "id": "b29211e9-9ee8-45f4-bdbb-054cb835d0d6",
+ "agentPurpose": "output prettifier",
+ }
+ ]
+ }
+}
+
+{
+ "id": "ac243657-5ab1-4727-b4be-1ea5ae2e76d3",
+ "name": "agent_two",
+ "description": "This is the second RAG agent",
+ "model": "gemma:2b",
+ "context":
+ {
+ "instruction": "Identify correct category for question: Available are PC, LAPTOP, ACCESSORIES - Its important to respond with single word",
+ "steps": ["ANSWER"],
+ }
+}
+
+{
+ "id": "b29211e9-9ee8-45f4-bdbb-054cb835d0d6",
+ "name": "agent_three",
+ "description": "This is the third RAG agent",
+ "model": "gemma:2b",
+ "context":
+ {
+ "instruction": "Your role is to prettify the response for the user to understand better",
+ "steps": ["ANSWER"],
+ }
+}
\ No newline at end of file
diff --git a/Docs/Images/temp/response.json b/Docs/Images/temp/response.json
new file mode 100644
index 00000000..3b3be44a
--- /dev/null
+++ b/Docs/Images/temp/response.json
@@ -0,0 +1,13 @@
+{
+ "id": "a0353d9d-2dfb-4663-91a4-987a20c9aba1",
+ "name": "agent_one",
+ "model": "gemma:2b",
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are shop assistant in a GeekITStuff store. You have to help the customer in finding the right product"
+ }
+ ],
+ "type": 2,
+ "stream": false
+ }
\ No newline at end of file
diff --git a/Docs/Images/temp/result.json b/Docs/Images/temp/result.json
new file mode 100644
index 00000000..5b74daf7
--- /dev/null
+++ b/Docs/Images/temp/result.json
@@ -0,0 +1,28 @@
+{
+ "Id": "32d8d3f5-4cb5-4f9b-bb51-ab674ae212f5",
+ "Name": "agent_one",
+ "Model": "gemma:2b",
+ "Messages": [
+ {
+ "Role": "system",
+ "Content": "You are shop assistant in a GeekITStuff store. You have to help the customer in finding the right product"
+ },
+ {
+ "Role": "user",
+ "Content": "I am looking for something budget friendly but with options for extensions"
+ },
+ {
+ "Role": "system",
+ "Content": "Here is data from internal data source, This is what you should use to answer questions: [{\"id\":1,\"image\":\"pc1.jpg\",\"name\":\"Gaming PC Ultra\",\"brand\":\"PowerTech\",\"processor\":\"Intel Core i9-11900K\",\"type\":\"PC\",\"ram\":\"32GB DDR4\",\"storage\":\"1TB SSD + 2TB HDD\",\"gpu\":\"NVIDIA GeForce RTX 3090\",\"price\":2999.99,\"availability\":\"In Stock\",\"description\":\"The Gaming PC Ultra from PowerTech features an Intel Core i9 processor and NVIDIA RTX 3090, making it perfect for high-end gaming and demanding applications. With 32GB of RAM and ample storage, this PC can handle any task with ease.\"},{\"id\":2,\"image\":\"pc2.png\",\"name\":\"Workstation Pro\",\"brand\":\"TechMaster\",\"processor\":\"AMD Ryzen 9 5950X\",\"type\":\"PC\",\"ram\":\"64GB DDR4\",\"storage\":\"2TB SSD\",\"gpu\":\"NVIDIA Quadro RTX 5000\",\"price\":3499.99,\"availability\":\"In Stock\",\"description\":\"TechMaster's Workstation Pro is designed for professionals who require top-tier performance. With an AMD Ryzen 9 CPU, 64GB RAM, and NVIDIA Quadro RTX GPU, this workstation excels in 3D rendering, video editing, and other intensive tasks.\"},{\"id\":3,\"image\":\"pc3.jpg\",\"name\":\"Budget Gamer\",\"brand\":\"EconoPC\",\"processor\":\"Intel Core i5-10400F\",\"type\":\"PC\",\"ram\":\"16GB DDR4\",\"storage\":\"512GB SSD\",\"gpu\":\"NVIDIA GeForce GTX 1660 Super\",\"price\":799.99,\"availability\":\"Out of Stock\",\"description\":\"The Budget Gamer by EconoPC provides excellent value for entry-level gaming. Featuring an Intel Core i5 processor and NVIDIA GTX 1660 Super GPU, this PC delivers smooth performance for popular games at an affordable price.\"},{\"id\":4,\"image\":\"pc4.jpg\",\"name\":\"All-Purpose PC\",\"brand\":\"ValueComp\",\"processor\":\"AMD Ryzen 5 3600\",\"type\":\"PC\",\"ram\":\"16GB DDR4\",\"storage\":\"1TB HDD\",\"gpu\":\"AMD Radeon RX 5700\",\"price\":999.99,\"availability\":\"In Stock\",\"description\":\"ValueComp's All-Purpose PC is a versatile system suitable for gaming, work, and everyday use. It features an AMD Ryzen 5 processor, 16GB RAM, and a Radeon RX 5700 GPU, ensuring balanced performance for a variety of tasks.\"},{\"id\":5,\"image\":\"pc5.jpg\",\"name\":\"Compact Office PC\",\"brand\":\"OfficeMate\",\"processor\":\"Intel Core i3-10100\",\"type\":\"PC\",\"ram\":\"8GB DDR4\",\"storage\":\"256GB SSD\",\"gpu\":\"Integrated Graphics\",\"price\":499.99,\"availability\":\"In Stock\",\"description\":\"The Compact Office PC by OfficeMate is ideal for small spaces and routine office tasks. Equipped with an Intel Core i3 processor and 8GB of RAM, it offers reliable performance for word processing, spreadsheets, and internet browsing.\"},{\"id\":6,\"image\":\"pc6.jpg\",\"name\":\"Performance Beast\",\"brand\":\"ExtremeTech\",\"processor\":\"Intel Core i7-12700K\",\"type\":\"PC\",\"ram\":\"32GB DDR5\",\"storage\":\"2TB SSD\",\"gpu\":\"NVIDIA GeForce RTX 3080\",\"price\":2599.99,\"availability\":\"In Stock\",\"description\":\"ExtremeTech's Performance Beast is designed for the most demanding users. With an Intel Core i7 processor, 32GB DDR5 RAM, and NVIDIA RTX 3080 GPU, this PC is ideal for gaming, 3D rendering, and other high-performance applications.\"},{\"id\":7,\"image\":\"laptop1.jpg\",\"name\":\"UltraBook Pro\",\"brand\":\"PowerTech\",\"processor\":\"Intel Core i7-1165G7\",\"type\":\"Laptop\",\"ram\":\"16GB LPDDR4x\",\"storage\":\"512GB SSD\",\"gpu\":\"Intel Iris Xe Graphics\",\"price\":1299.99,\"availability\":\"In Stock\",\"description\":\"The UltraBook Pro by PowerTech offers a sleek design with powerful performance, featuring an Intel Core i7 processor and Intel Iris Xe Graphics. With 16GB of RAM and a 512GB SSD, this laptop is perfect for professionals on the go.\"},{\"id\":8,\"image\":\"laptop2.jpg\",\"name\":\"Gaming Laptop X\",\"brand\":\"TechMaster\",\"processor\":\"AMD Ryzen 7 5800H\",\"type\":\"Laptop\",\"ram\":\"32GB DDR4\",\"storage\":\"1TB SSD\",\"gpu\":\"NVIDIA GeForce RTX 3070\",\"price\":1999.99,\"availability\":\"In Stock\",\"description\":\"TechMaster's Gaming Laptop X is designed for gamers who need high performance on the go. Featuring an AMD Ryzen 7 CPU and NVIDIA RTX 3070 GPU, this laptop delivers smooth gaming experiences and fast load times.\"},{\"id\":9,\"image\":\"laptop3.jpg\",\"name\":\"Budget Laptop\",\"brand\":\"EconoPC\",\"processor\":\"Intel Core i3-1115G4\",\"type\":\"Laptop\",\"ram\":\"8GB DDR4\",\"storage\":\"256GB SSD\",\"gpu\":\"Integrated Graphics\",\"price\":499.99,\"availability\":\"Out of Stock\",\"description\":\"The Budget Laptop by EconoPC offers essential features at an affordable price. With an Intel Core i3 processor and 8GB of RAM, it's suitable for students and casual users who need reliable performance for everyday tasks.\"},{\"id\":10,\"image\":\"laptop4.jpg\",\"name\":\"All-Purpose Laptop\",\"brand\":\"ValueComp\",\"processor\":\"AMD Ryzen 5 4500U\",\"type\":\"Laptop\",\"ram\":\"16GB DDR4\",\"storage\":\"512GB SSD\",\"gpu\":\"AMD Radeon Graphics\",\"price\":899.99,\"availability\":\"In Stock\",\"description\":\"ValueComp's All-Purpose Laptop is versatile and powerful, equipped with an AMD Ryzen 5 processor and Radeon Graphics. It's ideal for a variety of tasks, including work, entertainment, and light gaming.\"},{\"id\":11,\"image\":\"laptop5.jpg\",\"name\":\"Compact Office Laptop\",\"brand\":\"OfficeMate\",\"processor\":\"Intel Core i5-1135G7\",\"type\":\"Laptop\",\"ram\":\"8GB LPDDR4x\",\"storage\":\"512GB SSD\",\"gpu\":\"Intel Iris Xe Graphics\",\"price\":799.99,\"availability\":\"In Stock\",\"description\":\"The Compact Office Laptop by OfficeMate is perfect for professionals who need a portable and efficient device. With an Intel Core i5 processor and 8GB of RAM, it offers solid performance for office applications and multitasking.\"},{\"id\":12,\"image\":\"laptop6.jpg\",\"name\":\"Creative Laptop\",\"brand\":\"CreativeTech\",\"processor\":\"Apple M1\",\"type\":\"Laptop\",\"ram\":\"16GB Unified Memory\",\"storage\":\"1TB SSD\",\"gpu\":\"Apple M1 GPU\",\"price\":1499.99,\"availability\":\"In Stock\",\"description\":\"CreativeTech's Creative Laptop is perfect for designers and content creators. Featuring the Apple M1 chip, 16GB of unified memory, and a 1TB SSD, this laptop offers exceptional performance and efficiency for creative tasks.\"}]"
+ },
+ {
+ "Role": "assistant",
+ "Content": "## Budget Gamer: Your Gateway to High-Performance Gaming on a Shoestring\n\nThe **Budget Gamer** from EconoPC is the PC that proves you can have the ultimate gaming experience without breaking the bank. It's packed with powerful features and sleek design, making it the perfect companion for both casual gamers and hardcore enthusiasts.\n\n**Here's what the Budget Gamer offers:**\n\n* **Unmatched value for its price:** This PC delivers high performance and exceptional features without the hefty price tag.\n* **Next-gen specs:** It boasts a brand-new Ryzen 7 5800U processor, 16GB of fast DDR5 memory, and a NVIDIA GeForce RTX 3060 graphics card – enough to run most popular games at high settings and even some demanding titles in 1080p.\n* **Ultra-quiet operation:** Enjoy a peaceful gaming environment with the built-in fans that ensure minimal noise.\n* **Sleek and stylish design:** The Budget Gamer boasts an elegant chassis and a unique mesh front panel, giving it a modern touch.\n* **Warranty and support:** EconoPC offers a comprehensive warranty and dedicated support resources, ensuring you have everything you need for complete peace of mind.\n\n**The Budget Gamer is perfect for:**\n\n* Gamers on a tight budget who still want to enjoy high-quality gaming.\n* First-time PC builders who want a powerful system without breaking the bank.\n* Gamers who demand high performance and a quiet and comfortable gaming environment.\n\n**Don't miss out on the ultimate gaming experience – get your Budget Gamer today!**"
+ }
+ ],
+ "Type": 2,
+ "Stream": false,
+ "Properties": {
+ "FETCH_DATA*": ""
+ }
+ }
\ No newline at end of file
diff --git a/Docs/Images/temp/test.json b/Docs/Images/temp/test.json
new file mode 100644
index 00000000..69acafbc
--- /dev/null
+++ b/Docs/Images/temp/test.json
@@ -0,0 +1,40 @@
+[
+ {
+ "id": "d2f191c7-f08b-4285-b0d6-bb99a045ebde",
+ "name": "agent_one",
+ "description": "This is the first RAG agent",
+ "model": "llama3.1:8b",
+ "context":
+ {
+ "instruction": "You are shop assistant in a GeekITStuff store. You have to help the customer in finding the right product",
+ "source":
+ {
+ "type": 1,
+ "details":
+ {
+ "url": "http://localhost:5098/items",
+ "method": "Get",
+ "query": "",
+ "payload": ""
+ }
+ },
+ "steps": ["FETCH_DATA*","ANSWER", "REDIRECT+b29211e9-9ee8-45f4-bdbb-054cb835d0d6+AS_Output+REPLACE"],
+ "relations":
+ [
+ "ac243657-5ab1-4727-b4be-1ea5ae2e76d3",
+ "b29211e9-9ee8-45f4-bdbb-054cb835d0d6"
+ ]
+ }
+},
+{
+ "id": "b29211e9-9ee8-45f4-bdbb-054cb835d0d6",
+ "name": "agent_three",
+ "description": "This is the third RAG agent",
+ "model": "llama3.1:8b",
+ "context":
+ {
+ "instruction": "Adjust previous response to be better for marketting purposes. Dont include any introduction, just pure content",
+ "steps": ["ANSWER"]
+ }
+}
+]
\ No newline at end of file
diff --git a/Frontend/MainFE/.idea/.idea.MainFE/.idea/inspectionProfiles/Project_Default.xml b/Frontend/MainFE/.idea/.idea.MainFE/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..39b37c89
--- /dev/null
+++ b/Frontend/MainFE/.idea/.idea.MainFE/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Frontend/MainFE/Components/Elements/ChatComponent.razor b/Frontend/MainFE/Components/Elements/ChatComponent.razor
new file mode 100644
index 00000000..2a495ba0
--- /dev/null
+++ b/Frontend/MainFE/Components/Elements/ChatComponent.razor
@@ -0,0 +1,114 @@
+@using MainFE.Components.Models
+@using Markdig
+@using Microsoft.FluentUI.AspNetCore.Components
+@using Message = MainFE.Components.Models.Message
+@inject HttpClient Http
+
+
+
M.A.I.N (Modular Artificial Intelligence Network) is an advanced, modular AI platform designed to integrate various AI capabilities seamlessly. Initially featuring chat-based solutions powered by open-source large language models (LLMs), M.A.I.N is set to evolve with additional functionalities, providing a comprehensive AI toolkit for diverse applications. The ultimate goal is to build an AI that is aware of what is happening within the system and can react to those changes. This interconnectedness and contextual awareness is what makes it a "Network."
-
-
-
Chat Solutions: Leveraging open-source LLMs to deliver sophisticated conversational AI experiences.
-
Vision Capabilities:Upcoming feature enabling the system to process and interpret visual data.
-
Image Processing: Advanced image handling and analysis tools.
-
Retrieval-Augmented Generation (RAG): Integrating retrieval mechanisms to enhance response generation with up-to-date information.
-
Modular Architecture: Flexible and scalable design allowing easy integration of new AI modules.
-
Open-Source Foundation: Built on robust, community-driven AI technologies.
Experience the capabilities of our cutting-edge technology through our IT shop demo, featuring a sophisticated two-agent architecture built on the open-source Gemma2 (2B) models. This demo showcases the seamless integration of our custom infrastructure in creating dynamic and engaging user interactions.
+
+
Technical Flow
+
Our system operates with a streamlined flow involving two specialized agents:
+
+
User: - Looking for good PC/Laptop writing initial message
+
Agent 1: Intelligent Assistant - This agent interfaces with external APIs to dynamically retrieve and aggregate detailed information about products, such as PCs and laptops. It pulls in specifications, availability, and other relevant data.
+
Agent 2: Response Enhancer - After receiving the initial product information, this agent processes and refines the data, transforming it into polished, marketing-optimized responses that enhance user engagement and improve presentation.
+
+
+
Its worth to mention that Agents are prepared to fetch data from lots of other sources as well, such as txt files, json, sql and nosql databases
+
Our custom infrastructure ensures smooth data flow and interaction between these agents, allowing for a flexible and scalable system that can be adapted to various applications, including e-commerce platforms, customer support systems, and content generation.
+
+
+
+
+
+
+
+
+
+
@selectedItem.Name Details
+
+
+
+
+ Brand: @selectedItem.Brand
+
+
+ Type: @selectedItem.Type
+
+
+ Processor: @selectedItem.Processor
+
+
+ RAM: @selectedItem.Ram
+
+
+ Storage: @selectedItem.Storage
+
+
+ GPU: @selectedItem.Gpu
+
+
Price: $@selectedItem.Price
+
+ Availability: @selectedItem.Availability
+
+
+ Description:
+
+
@selectedItem.Description
+
+
+
+
+}
+
+@code {
+ ChatDto _selectedChat = new();
+ string _ask = string.Empty;
+ bool _loading = false;
+ bool _globalLoading = true;
+ AgentDto _selectedAgent;
+ string _designatedAgent = "agent_one";
+
+ HardwareItemDto selectedItem = new();
+ private List _items = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ Http.Timeout = TimeSpan.FromMinutes(10);
+ await InitRagAsync();
+ await LoadProductsAsync();
+ }
+ finally
+ {
+ _globalLoading = false;
+ }
+ }
+
+ private async Task LoadProductsAsync()
+ {
+ var response = await Http.GetAsync($"{ExtensionMethods.GetDemoApiUrl()}/items/");
+ if (response.IsSuccessStatusCode)
+ {
+ _items = await response.Content.ReadFromJsonAsync>() ?? [];
+ }
+ }
+
+ private void ShowDetailsModal(HardwareItemDto item)
+ {
+ selectedItem = item;
+ }
+
+
+ private async Task InitRagAsync()
+ {
+ var filePath = ExtensionMethods.GetWorkingEnvironment() == "Docker" ? "wwwroot/initial_rag_demo_docker.json" : "wwwroot/initial_rag_demo.json";
+ var agents = JsonSerializer.Deserialize>(
+ await File.ReadAllTextAsync(filePath), new JsonSerializerOptions()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ });
+
+ _selectedAgent = agents?.FirstOrDefault(x => x.Name == _designatedAgent)!;
+ foreach (var agent in agents!)
+ {
+ await Http.PostAsJsonAsync($"{ExtensionMethods.GetApiUrl()}/api/agents", agent);
+ }
+
+ _selectedChat = (await Http.GetFromJsonAsync($"{ExtensionMethods.GetApiUrl()}/api/agents/{_selectedAgent.Id}/chat"))!;
+ }
+
+ private async Task Cleanup(string selectedAgentId)
+ {
+ await Http.PutAsync($"{ExtensionMethods.GetApiUrl()}/api/agents/{selectedAgentId}/chat/reset", default);
+ _selectedChat.Properties.Clear();
+ _selectedChat.Messages = _selectedChat.Messages.Take(1).ToList();
+ }
+}
diff --git a/Frontend/MainFE/ExtensionMethods.cs b/Frontend/MainFE/ExtensionMethods.cs
index f50e5d4b..88b0c755 100644
--- a/Frontend/MainFE/ExtensionMethods.cs
+++ b/Frontend/MainFE/ExtensionMethods.cs
@@ -45,5 +45,10 @@ public static string GetApiUrl()
{
return Environment.GetEnvironmentVariable("API_URL") ?? throw new InvalidOperationException("API_URL environment variable is not set");
}
+
+ public static string GetDemoApiUrl()
+ {
+ return Environment.GetEnvironmentVariable("DEMO_API_URL") ?? throw new InvalidOperationException("DEMO_API_URL environment variable is not set");
+ }
}
\ No newline at end of file
diff --git a/Frontend/MainFE/Properties/launchSettings.json b/Frontend/MainFE/Properties/launchSettings.json
index 0715fa45..19120832 100644
--- a/Frontend/MainFE/Properties/launchSettings.json
+++ b/Frontend/MainFE/Properties/launchSettings.json
@@ -16,6 +16,7 @@
"applicationUrl": "http://localhost:5028",
"environmentVariables": {
"API_URL": "http://localhost:5243",
+ "DEMO_API_URL": "http://localhost:5098",
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
diff --git a/Frontend/MainFE/appsettings.Docker.json b/Frontend/MainFE/appsettings.Docker.json
new file mode 100644
index 00000000..0c208ae9
--- /dev/null
+++ b/Frontend/MainFE/appsettings.Docker.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/Frontend/MainFE/wwwroot/app.css b/Frontend/MainFE/wwwroot/app.css
index 6c000a6a..816370a2 100644
--- a/Frontend/MainFE/wwwroot/app.css
+++ b/Frontend/MainFE/wwwroot/app.css
@@ -35,8 +35,32 @@ code {
color: #a9beff !important;
}
+.navl {
+ display: inline;
+ padding: .5rem 1rem;
+ color: #0d6efd;
+ text-decoration: none;
+ transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .navl {
+ transition: none
+ }
+}
+
+.navl:focus, .nav-link:hover {
+ color: #0a58ca
+}
+
+.navl.disabled {
+ color: #6c757d;
+ pointer-events: none;
+ cursor: default
+}
+
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
- box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
+ box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
@@ -65,9 +89,9 @@ h1:focus {
color: white;
}
- .blazor-error-boundary::after {
- content: "An error has occurred."
- }
+.blazor-error-boundary::after {
+ content: "An error has occurred."
+}
.darker-border-checkbox.form-check-input {
border-color: #929292;
diff --git a/Frontend/MainFE/wwwroot/css/agents.css b/Frontend/MainFE/wwwroot/css/agents.css
new file mode 100644
index 00000000..dcdbebb2
--- /dev/null
+++ b/Frontend/MainFE/wwwroot/css/agents.css
@@ -0,0 +1,171 @@
+body {
+ background-color: #121212;
+ color: #ffffff;
+ font-family: Arial, sans-serif;
+}
+
+.remove-btn {
+ position: absolute;
+ top: 1px;
+ right: 1px;
+ background: transparent;
+ border: none;
+ color: #fff6a3;
+ cursor: pointer;
+}
+
+.remove-btn:hover {
+ color: #ea7373;
+}
+
+.main-container {
+ padding: 0 20px;
+}
+
+.agents-grid {
+ padding: 20px;
+ justify-content: center;
+ height: 45rem;
+}
+
+.agent-tile {
+ background-color: #2b2b2b;
+ border: 1px solid #3c3c3c;
+ border-radius: 8px;
+ padding: 20px !important;
+ margin: 10px !important;
+ text-align: center;
+ cursor: pointer;
+ transition: transform 0.2s, background-color 0.2s;
+}
+
+.agent-tile:hover {
+ transform: scale(1.05);
+ background-color: #383838;
+}
+
+.agent-content {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.agent-emoji {
+ font-size: 2em;
+ margin-bottom: 10px;
+}
+
+.back-button {
+ background-color: transparent;
+ border: none;
+ color: #00dcaa; /* Primary blue color */
+ font-size: 3rem;
+ cursor: pointer;
+ padding: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: color 0.3s ease;
+}
+
+.back-button:hover {
+ color: #5ff158; /* Darker blue color for hover effect */
+}
+
+.back-button i {
+ margin-right: 8px; /* Space between the icon and text (if any) */
+}
+
+.cleanup-button {
+ background-color: #4CAF50; /* Green background */
+ border: none; /* Remove borders */
+ color: white; /* White text */
+ padding: 10px 10px; /* Some padding */
+ text-align: center; /* Centered text */
+ text-decoration: none; /* Remove underline */
+ display: inline-block; /* Get the element to behave like an inline-block */
+ font-size: 13px; /* Increase font size */
+ cursor: pointer; /* Pointer/hand icon */
+ border-radius: 8px; /* Rounded corners */
+ transition-duration: 0.4s; /* Smooth transition */
+}
+
+.cleanup-button:hover {
+ background-color: white; /* White background on hover */
+ color: black; /* Black text on hover */
+ border: 2px solid #4CAF50; /* Green border on hover */
+}
+
+.cleanup-button i {
+ margin-right: 8px; /* Space between icon and text */
+}
+
+.badge-agent-model {
+ top: 10px;
+ right: 10px;
+ background-color: #8df18d; /* Primary blue color */
+ color: #4c4c4c;
+ padding: 5px 10px;
+ border-radius: 12px;
+ font-size: 12px;
+ font-weight: bold;
+}
+
+.create-form-container {
+ max-width: 600px;
+ margin: auto;
+ padding: 20px;
+ background-color: #2b2b2b;
+ border: 1px solid #3c3c3c;
+ border-radius: 8px;
+}
+
+.create-form-container label {
+ color: #cccccc;
+ margin-bottom: -10px;
+}
+
+.create-form-container .form-group {
+ margin-bottom: 15px;
+}
+
+.create-form-container input,
+.create-form-container textarea {
+ width: 100%;
+ padding: 10px;
+ margin-top: 5px;
+ border: 1px solid #333;
+ border-radius: 4px;
+ background-color: #121212;
+ color: #ffffff;
+}
+
+.create-form-container input:focus,
+.create-form-container textarea:focus {
+ width: 100%;
+ padding: 10px;
+ margin-top: 5px;
+ border: 1px solid #af6bff;
+ border-radius: 4px;
+ background-color: #121212;
+ color: #ffffff;
+}
+
+
+.create-button {
+ background-color: #7aeda3;
+ color: #3a3232;
+ border: none;
+ cursor: pointer;
+ padding: 10px 20px;
+ border-radius: 4px;
+ width: 15rem;
+ transition: background-color 0.2s;
+ display: block;
+ margin: auto;
+}
+
+.create-button:hover {
+ background-color: #005a9e;
+}
diff --git a/Frontend/MainFE/wwwroot/css/chat.css b/Frontend/MainFE/wwwroot/css/chat.css
index 52b2d4f7..e0f52f77 100644
--- a/Frontend/MainFE/wwwroot/css/chat.css
+++ b/Frontend/MainFE/wwwroot/css/chat.css
@@ -12,7 +12,6 @@
.containerbox {
display: flex;
height: 90vh;
- width: 90vw !important;
flex-direction: column;
}
@@ -88,7 +87,7 @@
}
.input-container {
- max-height: 7%;
+ height: 3rem;
display: flex;
padding: 5px;
position: relative;
@@ -106,14 +105,70 @@
margin: 0;
}
+ h1 {
+ color: green;
+ }
+
+/* To hide the checkbox */
+#checkboxInput {
+ display: none;
+}
+
+.containerToggle {
+ display: flex;
+ align-items: center;
+}
+
+.text {
+ color: white; /* Make the text white */
+ margin-right: 10px; /* Space between text and label */
+}
+
+.toggleSwitch {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ width: 50px;
+ height: 30px;
+ background-color: rgb(82, 82, 82);
+ border-radius: 20px;
+ cursor: pointer;
+ transition-duration: .2s;
+}
+
+.toggleSwitch::after {
+ content: "";
+ position: absolute;
+ height: 10px;
+ width: 10px;
+ left: 5px;
+ background-color: transparent;
+ border-radius: 50%;
+ transition-duration: .2s;
+ box-shadow: 5px 2px 7px rgba(8, 8, 8, 0.26);
+ border: 5px solid white;
+}
+
+#checkboxInput:checked+.toggleSwitch::after {
+ transform: translateX(200%);
+ transition-duration: .2s;
+ background-color: white;
+}
+/* Switch background change */
+#checkboxInput:checked+.toggleSwitch {
+ background-color: rgb(148, 118, 255);
+ transition-duration: .2s;
+}
+
.user-message {
align-self: flex-end;
- background-color: #012139;
+ background-color: #0d1820;
}
.bot-message {
align-self: flex-start;
- background-color: #121312;
+ background-color: #0c0c0c;
}
.chat-item {
diff --git a/Frontend/MainFE/wwwroot/css/home.css b/Frontend/MainFE/wwwroot/css/home.css
index 5f9375d3..1d23464e 100644
--- a/Frontend/MainFE/wwwroot/css/home.css
+++ b/Frontend/MainFE/wwwroot/css/home.css
@@ -23,6 +23,53 @@ body {
margin-bottom: 20px;
}
+
+@media (max-width: 767px) {
+ .social-media-buttons {
+ gap: 5px !important;
+ opacity: 0;
+ margin-left: 0 !important;
+ animation: popup 1s ease-in-out forwards;
+ animation-delay: 0.5s;
+ }
+}
+
+.social-media-buttons {
+ display: flex;
+ gap: 20px;
+ position: relative;
+ opacity: 0;
+ margin-left: 25%;
+ animation: popup 1s ease-in-out forwards;
+ animation-delay: 0.5s;
+}
+
+.button {
+ text-decoration: none;
+ color: white;
+ padding: 15px 30px;
+ border-radius: 10px;
+ border-color: wheat;
+ border-width: 2px;
+ border-style: solid;
+ transition: transform 0.3s ease;
+}
+
+.button:hover {
+ transform: scale(1.1);
+}
+
+@keyframes popup {
+ 0% {
+ transform: translateY(50px);
+ opacity: 0;
+ }
+ 100% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
.fancy-text {
font-family: "Tiny5", sans-serif;
font-weight: 400;
diff --git a/Frontend/MainFE/wwwroot/css/rag.css b/Frontend/MainFE/wwwroot/css/rag.css
new file mode 100644
index 00000000..96e149aa
--- /dev/null
+++ b/Frontend/MainFE/wwwroot/css/rag.css
@@ -0,0 +1,360 @@
+/* Body and Layout */
+body {
+ background-color: #f0f0f0; /* Light grey background */
+ color: #333; /* Darker text for contrast */
+ font-family: 'Arial', sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+/* Header */
+header {
+ background-color: #ffffff; /* White background for the header */
+ color: #333; /* Dark text color */
+ padding: 15px;
+ text-align: center;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ font-family: 'Tiny5', monospace; /* Coding-style font for the title */
+}
+
+nav {
+ display: none !important;
+}
+
+header h1 {
+ margin: 0;
+ font-size: 2em;
+}
+
+/* Toggle Switch for Dark Mode */
+.theme-switch-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 10px;
+}
+
+.theme-switch {
+ display: inline-block;
+ height: 34px;
+ position: relative;
+ width: 60px;
+}
+
+.theme-switch input {
+ display: none;
+}
+
+.slider {
+ background-color: #ccc;
+ bottom: 0;
+ cursor: pointer;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+ transition: .4s;
+}
+
+.slider:before {
+ background-color: white;
+ bottom: 4px;
+ content: "";
+ height: 26px;
+ left: 4px;
+ position: absolute;
+ transition: .4s;
+ width: 26px;
+}
+
+input:checked + .slider {
+ background-color: #66bb6a;
+}
+
+input:checked + .slider:before {
+ transform: translateX(26px);
+}
+
+.slider.round {
+ border-radius: 34px;
+}
+
+.slider.round:before {
+ border-radius: 50%;
+}
+
+/* Cards */
+.card-container {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ margin: 30px;
+}
+
+.card {
+ background-color: #ffffff; /* White background for cards */
+ border-radius: 10px;
+ padding: 20px;
+ margin: 15px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ transition: transform 0.2s;
+ max-width: 300px;
+ text-align: center;
+}
+
+.card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
+}
+
+.card-content {
+ text-align: center;
+}
+
+.card-image {
+ max-width: 100%;
+ max-height: 150px;
+ margin-bottom: 15px;
+ border-radius: 5px;
+ border: 1px solid #ddd; /* Light grey border around the images */
+}
+
+/* Buttons */
+.btn-secondary {
+ background-color: #0078D4;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ padding: 10px 20px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.btn-secondary:hover {
+ background-color: #005A9E;
+}
+
+/* Modal Styling */
+.custom-modal-dialog {
+ max-width: 800px;
+ height: 25rem;
+}
+
+.custom-modal {
+ background-color: #f0f0f0;
+ color: #333;
+}
+
+.custom-modal-header {
+ background-color: #0078D4;
+ color: #fff;
+ border-bottom: 1px solid #005A9E;
+ border-radius: 5px 5px 0 0;
+}
+
+.custom-modal-body {
+ padding: 20px;
+}
+
+/* Miscellaneous */
+.close {
+ color: #333;
+ opacity: 0.8;
+ cursor: pointer;
+}
+
+.close:hover {
+ opacity: 1;
+}
+
+/* Footer */
+footer {
+ background-color: #ffffff;
+ color: #333;
+ text-align: center;
+ padding: 10px 0;
+ box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.1);
+ margin-top: 20px;
+}
+
+
+.logo-container {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+}
+
+.center-logo {
+ display: block;
+ max-height: 70px;
+}
+
+.ask-assistant-button {
+ position: absolute;
+ left: 20px;
+ transform: translateY(-50%);
+ padding: 10px 20px;
+ background-color: #0078D4;
+ color: white;
+ border: none;
+ border-radius: 25px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.information-button {
+ position: absolute;
+ right: 20px;
+ transform: translateY(-50%);
+ padding: 10px 20px;
+ background-color: #0078D4;
+ color: white;
+ border: none;
+ border-radius: 25px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.information-button:hover {
+ background-color: #005A9E;
+}
+
+.ask-assistant-button:hover {
+ background-color: #005A9E;
+}
+.cleanup-button {
+ background-color: #4CAF50; /* Green background */
+ border: none; /* Remove borders */
+ color: white; /* White text */
+ padding: 5px 10px; /* Some padding */
+ text-align: center; /* Centered text */
+ text-decoration: none; /* Remove underline */
+ display: inline-block; /* Get the element to behave like an inline-block */
+ font-size: 13px; /* Increase font size */
+ cursor: pointer; /* Pointer/hand icon */
+ border-radius: 8px; /* Rounded corners */
+ transition-duration: 0.4s; /* Smooth transition */
+ margin-left: 10px;
+}
+
+.cleanup-button:hover {
+ background-color: white; /* White background on hover */
+ color: black; /* Black text on hover */
+ border: 2px solid #4CAF50; /* Green border on hover */
+}
+
+/* Media Queries */
+@media (max-width: 768px) {
+ .card-container {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .card {
+ max-width: 90%;
+ margin: 10px 0;
+ }
+}
+
+
+.collapsible {
+ text-align: left;
+}
+
+.inp {
+ background: #06495e !important;
+ color: aliceblue !important;
+ border-radius: 10px;
+ border-color: #1c1f23;
+ padding: 10px;
+ margin-right: 10px;
+ flex-grow: 1;
+}
+
+
+@media (max-width: 768px) {
+ .messages-container {
+ height: 40vh;
+ }
+}
+
+.messages-container {
+ flex-grow: 1;
+ overflow-y: auto;
+ padding: 10px;
+ max-height: 90vh;
+ min-height: 20rem;
+ display: flex;
+ border-color: #939393;
+ border-radius: 15px;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
+ flex-direction: column;
+ background-color: #dcdcdc !important;
+}
+
+.input-container {
+ max-height: 7%;
+ display: flex;
+ padding: 5px;
+ position: relative;
+}
+
+.message-card {
+ margin-bottom: 15px;
+ width: 80%;
+ height: fit-content;
+ padding: 10px;
+ color: #fff;
+}
+
+.message-card p {
+ margin: 0;
+}
+
+.user-message {
+ align-self: flex-end;
+ background-color: #1c99fa !important;
+}
+
+.bot-message {
+ align-self: flex-start;
+ background-color: #5a37ea !important;
+}
+
+@media (min-width: 768px) {
+ .containerbox {
+ flex-direction: row;
+ }
+}
+
+@media (min-width: 768px) {
+ .input-container {
+ flex-direction: row;
+ }
+}
+
+@media (max-width: 767px) {
+ .input-container {
+ height: 20vh;
+ padding: 10px;
+ }
+
+ .inp {
+ height: 5vh;
+ }
+
+ .containerbox {
+ margin: 0 auto;
+ }
+}
+
+.message-role-bot {
+ color: #676767 !important;
+ align-self: flex-start;
+}
+
+.message-role-user {
+ color: #676767 !important;
+ align-self: flex-end;
+}
+
diff --git a/Frontend/MainFE/wwwroot/favicon.png b/Frontend/MainFE/wwwroot/favicon.png
index 8422b596..1dbdf565 100644
Binary files a/Frontend/MainFE/wwwroot/favicon.png and b/Frontend/MainFE/wwwroot/favicon.png differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/laptop1.jpg b/Frontend/MainFE/wwwroot/images/demo/laptop1.jpg
new file mode 100644
index 00000000..2be30849
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/laptop1.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/laptop2.jpg b/Frontend/MainFE/wwwroot/images/demo/laptop2.jpg
new file mode 100644
index 00000000..721dfdc0
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/laptop2.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/laptop3.jpg b/Frontend/MainFE/wwwroot/images/demo/laptop3.jpg
new file mode 100644
index 00000000..d110a4bf
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/laptop3.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/laptop4.jpg b/Frontend/MainFE/wwwroot/images/demo/laptop4.jpg
new file mode 100644
index 00000000..eab73fe0
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/laptop4.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/laptop5.jpg b/Frontend/MainFE/wwwroot/images/demo/laptop5.jpg
new file mode 100644
index 00000000..c4f45dad
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/laptop5.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/laptop6.jpg b/Frontend/MainFE/wwwroot/images/demo/laptop6.jpg
new file mode 100644
index 00000000..62f5130d
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/laptop6.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/logo.png b/Frontend/MainFE/wwwroot/images/demo/logo.png
new file mode 100644
index 00000000..860a6461
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/logo.png differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/pc1.jpg b/Frontend/MainFE/wwwroot/images/demo/pc1.jpg
new file mode 100644
index 00000000..80bd0925
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/pc1.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/pc2.png b/Frontend/MainFE/wwwroot/images/demo/pc2.png
new file mode 100644
index 00000000..7fbe25f3
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/pc2.png differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/pc3.jpg b/Frontend/MainFE/wwwroot/images/demo/pc3.jpg
new file mode 100644
index 00000000..d3f7276e
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/pc3.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/pc4.jpg b/Frontend/MainFE/wwwroot/images/demo/pc4.jpg
new file mode 100644
index 00000000..060b61ae
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/pc4.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/pc5.jpg b/Frontend/MainFE/wwwroot/images/demo/pc5.jpg
new file mode 100644
index 00000000..339c3e9b
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/pc5.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/demo/pc6.jpg b/Frontend/MainFE/wwwroot/images/demo/pc6.jpg
new file mode 100644
index 00000000..a1a150e3
Binary files /dev/null and b/Frontend/MainFE/wwwroot/images/demo/pc6.jpg differ
diff --git a/Frontend/MainFE/wwwroot/images/robot.png b/Frontend/MainFE/wwwroot/images/robot.png
index 37a96768..968c5dec 100644
Binary files a/Frontend/MainFE/wwwroot/images/robot.png and b/Frontend/MainFE/wwwroot/images/robot.png differ
diff --git a/Frontend/MainFE/wwwroot/initial_rag_demo.json b/Frontend/MainFE/wwwroot/initial_rag_demo.json
new file mode 100644
index 00000000..18f46759
--- /dev/null
+++ b/Frontend/MainFE/wwwroot/initial_rag_demo.json
@@ -0,0 +1,40 @@
+[
+ {
+ "id": "d2f191c7-f08b-4285-b0d6-bb99a045ebde",
+ "name": "agent_one",
+ "description": "This is the first RAG agent",
+ "model": "gemma2:2b",
+ "context":
+ {
+ "instruction": "You are shop assistant in a GeekITStuff store. You have to help the customer in finding the right product",
+ "source":
+ {
+ "type": 1,
+ "details":
+ {
+ "url": "http://localhost:5098/items",
+ "method": "Get",
+ "query": "",
+ "payload": ""
+ }
+ },
+ "steps": ["FETCH_DATA*","ANSWER", "REDIRECT+f29211e9-9xe8-45f4-bdbb-054cb835d0d6+AS_Output+REPLACE"],
+ "relations":
+ [
+ "ac243657-5ab1-4727-b4be-1ea5ae2e76d3",
+ "f29211e9-9xe8-45f4-bdbb-054cb835d0d6"
+ ]
+ }
+},
+{
+ "id": "f29211e9-9xe8-45f4-bdbb-054cb835d0d6",
+ "name": "agent_three",
+ "description": "This is the third RAG agent",
+ "model": "gemma2:2b",
+ "context":
+ {
+ "instruction": "Adjust previous response to be better for marketing purposes. Dont include any introduction, just pure content",
+ "steps": ["ANSWER"]
+ }
+}
+]
\ No newline at end of file
diff --git a/Frontend/MainFE/wwwroot/initial_rag_demo_docker.json b/Frontend/MainFE/wwwroot/initial_rag_demo_docker.json
new file mode 100644
index 00000000..8d46ef46
--- /dev/null
+++ b/Frontend/MainFE/wwwroot/initial_rag_demo_docker.json
@@ -0,0 +1,40 @@
+[
+ {
+ "id": "d2f191c7-f08b-4285-b0d6-bb99a045ebde",
+ "name": "agent_one",
+ "description": "This is the first RAG agent",
+ "model": "gemma2:2b",
+ "context":
+ {
+ "instruction": "You are shop assistant in a GeekITStuff store. You have to help the customer in finding the right product",
+ "source":
+ {
+ "type": 1,
+ "details":
+ {
+ "url": "http://demo:8080/items",
+ "method": "Get",
+ "query": "",
+ "payload": ""
+ }
+ },
+ "steps": ["FETCH_DATA*","ANSWER", "REDIRECT+f29211e9-9xe8-45f4-bdbb-054cb835d0d6+AS_Output+REPLACE"],
+ "relations":
+ [
+ "ac243657-5ab1-4727-b4be-1ea5ae2e76d3",
+ "f29211e9-9xe8-45f4-bdbb-054cb835d0d6"
+ ]
+ }
+},
+{
+ "id": "f29211e9-9xe8-45f4-bdbb-054cb835d0d6",
+ "name": "agent_three",
+ "description": "This is the third RAG agent",
+ "model": "gemma2:2b",
+ "context":
+ {
+ "instruction": "Adjust previous response to be better for marketing purposes. Dont include any introduction, just pure content",
+ "steps": ["ANSWER"]
+ }
+}
+]
\ No newline at end of file
diff --git a/MaIN.sln b/MaIN.sln
index dc7c237b..fd019bab 100644
--- a/MaIN.sln
+++ b/MaIN.sln
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaIN.Services", "src\MaIN.S
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaIN.Domain", "src\MaIN.Domain\MaIN.Domain.csproj", "{4DEF4AA6-DFD8-4626-B8BE-2F773B12FE0C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaIN.RAG.Demo", "src\MaIN.RAG.Demo\MaIN.RAG.Demo.csproj", "{2351B015-EA33-42F3-A844-CECABF140E9A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -30,5 +32,9 @@ Global
{4DEF4AA6-DFD8-4626-B8BE-2F773B12FE0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DEF4AA6-DFD8-4626-B8BE-2F773B12FE0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DEF4AA6-DFD8-4626-B8BE-2F773B12FE0C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2351B015-EA33-42F3-A844-CECABF140E9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2351B015-EA33-42F3-A844-CECABF140E9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2351B015-EA33-42F3-A844-CECABF140E9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2351B015-EA33-42F3-A844-CECABF140E9A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/docker-compose.yml b/docker-compose.yml
index d47f7359..3dede400 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,9 +9,7 @@ services:
- mongo-data:/data/db
backend:
- build:
- context: ./src
- dockerfile: ./Dockerfile
+ image: inza124/main-backend:latest
container_name: backend
restart: always
ports:
@@ -23,9 +21,7 @@ services:
LocalHost: http://host.docker.internal
blazor:
- build:
- context: ./Frontend/MainFE
- dockerfile: ./Dockerfile
+ image: inza124/main-blazor:latest
container_name: blazor
restart: always
ports:
@@ -33,8 +29,21 @@ services:
depends_on:
- backend
environment:
- ASPNETCORE_ENVIRONMENT: Development
+ ASPNETCORE_ENVIRONMENT: Docker
API_URL: http://backend:8080
+ DEMO_API_URL: http://demo:8080
+
+ demo:
+ image: inza124/main-demo
+ container_name: demo
+ restart: always
+ ports:
+ - "5002:8080"
+ depends_on:
+ - blazor
+ environment:
+ ASPNETCORE_ENVIRONMENT: Docker
+ LocalHost: http://host.docker.internal
volumes:
mongo-data:
diff --git a/src/Dockerfile.demo b/src/Dockerfile.demo
new file mode 100644
index 00000000..4167d4ba
--- /dev/null
+++ b/src/Dockerfile.demo
@@ -0,0 +1,14 @@
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+WORKDIR /app
+
+COPY MaIN.RAG.Demo/*.csproj ./MaIN.RAG.Demo/
+
+RUN dotnet restore ./MaIN.RAG.Demo/MaIN.RAG.Demo.csproj
+
+COPY . ./
+RUN dotnet publish ./MaIN.RAG.Demo/MaIN.RAG.Demo.csproj -c Release -o /app/out
+
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
+WORKDIR /app
+COPY --from=build /app/out .
+ENTRYPOINT ["dotnet", "MaIN.RAG.Demo.dll"]
\ No newline at end of file
diff --git a/src/MaIN.Domain/Chat.cs b/src/MaIN.Domain/Chat.cs
deleted file mode 100644
index 7f72878b..00000000
--- a/src/MaIN.Domain/Chat.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace MaIN.Models;
-
-public class Chat
-{
- public string Id { get; set; }
- public string Name { get; set; }
- public string Model { get; set; }
- public List Messages { get; set; }
- public bool Stream { get; set; } = false;
-}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/Agent.cs b/src/MaIN.Domain/Entities/Agents/Agent.cs
new file mode 100644
index 00000000..9fe815fa
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/Agent.cs
@@ -0,0 +1,12 @@
+namespace MaIN.Domain.Entities.Agents;
+
+public class Agent
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Model { get; set; }
+ public string? Description { get; set; }
+ public bool Started { get; set; }
+ public AgentContext Context { get; set; }
+ public string? ChatId { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentContext.cs b/src/MaIN.Domain/Entities/Agents/AgentContext.cs
new file mode 100644
index 00000000..3bc9b216
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentContext.cs
@@ -0,0 +1,10 @@
+namespace MaIN.Domain.Entities.Agents;
+
+public class AgentContext
+{
+ public string Instruction { get; set; }
+ public AgentSource.AgentSource? Source { get; set; }
+ public ILookup Steps { get; set; }
+ public List? Relations { get; set; }
+
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentRelations.cs b/src/MaIN.Domain/Entities/Agents/AgentRelations.cs
new file mode 100644
index 00000000..c38972b7
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentRelations.cs
@@ -0,0 +1,6 @@
+namespace MaIN.Domain.Entities.Agents;
+
+public class AgentRelation
+{
+ public string Id { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentAPISourceDetails.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentAPISourceDetails.cs
new file mode 100644
index 00000000..061f585f
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentAPISourceDetails.cs
@@ -0,0 +1,9 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public class AgentApiSourceDetails : AgentSourceDetailsBase
+{
+ public string Url { get; set; }
+ public string Method { get; set; }
+ public string? Payload { get; set; }
+ public string? Query { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentFileSourceDetails.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentFileSourceDetails.cs
new file mode 100644
index 00000000..e47fbb35
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentFileSourceDetails.cs
@@ -0,0 +1,6 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public class AgentFileSourceDetails : AgentSourceDetailsBase
+{
+ public string Path { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentNoSqlSourceDetails.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentNoSqlSourceDetails.cs
new file mode 100644
index 00000000..6192138c
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentNoSqlSourceDetails.cs
@@ -0,0 +1,9 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public class AgentNoSqlSourceDetails
+{
+ public string ConnectionString { get; set; }
+ public string DbName { get; set; }
+ public string Collection { get; set; }
+ public string Query { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSource.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSource.cs
new file mode 100644
index 00000000..a5b03b49
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSource.cs
@@ -0,0 +1,7 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public class AgentSource
+{
+ public object? Details { get; set; }
+ public AgentSourceType Type { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSourceDetailsBase.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSourceDetailsBase.cs
new file mode 100644
index 00000000..dd4a0354
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSourceDetailsBase.cs
@@ -0,0 +1,6 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public class AgentSourceDetailsBase
+{
+
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSourceType.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSourceType.cs
new file mode 100644
index 00000000..281041d0
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSourceType.cs
@@ -0,0 +1,10 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public enum AgentSourceType
+{
+ API = 1,
+ SQL = 2,
+ NoSQL = 3,
+ File = 4,
+ Text = 5,
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSqlSourceDetails.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSqlSourceDetails.cs
new file mode 100644
index 00000000..313643d7
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentSqlSourceDetails.cs
@@ -0,0 +1,8 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public class AgentSqlSourceDetails
+{
+ public string ConnectionString { get; set; }
+ public string Table { get; set; }
+ public string Query { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/AgentSource/AgentTextSourceDetails.cs b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentTextSourceDetails.cs
new file mode 100644
index 00000000..fb050ab3
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/AgentSource/AgentTextSourceDetails.cs
@@ -0,0 +1,6 @@
+namespace MaIN.Domain.Entities.Agents.AgentSource;
+
+public class AgentTextSourceDetails : AgentSourceDetailsBase
+{
+ public string Text { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/Commands/AnswerCommand.cs b/src/MaIN.Domain/Entities/Agents/Commands/AnswerCommand.cs
new file mode 100644
index 00000000..61df6c6e
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/Commands/AnswerCommand.cs
@@ -0,0 +1,8 @@
+using MaIN.Domain.Entities.Agents.Commands.Base;
+
+namespace MaIN.Domain.Entities.Agents.Commands;
+
+public class AnswerCommand : BaseCommand
+{
+
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/Commands/Base/BaseCommand.cs b/src/MaIN.Domain/Entities/Agents/Commands/Base/BaseCommand.cs
new file mode 100644
index 00000000..fe32e56a
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/Commands/Base/BaseCommand.cs
@@ -0,0 +1,6 @@
+namespace MaIN.Domain.Entities.Agents.Commands.Base;
+
+public class BaseCommand
+{
+ public Chat? Chat { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/Commands/FetchCommand.cs b/src/MaIN.Domain/Entities/Agents/Commands/FetchCommand.cs
new file mode 100644
index 00000000..0300b4e1
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/Commands/FetchCommand.cs
@@ -0,0 +1,9 @@
+using MaIN.Domain.Entities.Agents.Commands.Base;
+
+namespace MaIN.Domain.Entities.Agents.Commands;
+
+public class FetchCommand : BaseCommand
+{
+ public string? Filter { get; set; }
+ public AgentContext Context { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/Commands/RedirectCommand.cs b/src/MaIN.Domain/Entities/Agents/Commands/RedirectCommand.cs
new file mode 100644
index 00000000..336d28ec
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/Commands/RedirectCommand.cs
@@ -0,0 +1,16 @@
+using MaIN.Domain.Entities.Agents.Commands.Base;
+
+namespace MaIN.Domain.Entities.Agents.Commands;
+
+public class RedirectCommand : BaseCommand
+{
+ public Message Message { get; set; }
+ public string RelatedAgentId { get; set; } = null!;
+ public OutputTypeOfRedirect SaveAs { get; set; }
+}
+
+public enum OutputTypeOfRedirect
+{
+ AS_Filter,
+ AS_Output
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Agents/Commands/StartCommand.cs b/src/MaIN.Domain/Entities/Agents/Commands/StartCommand.cs
new file mode 100644
index 00000000..538b3b43
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Agents/Commands/StartCommand.cs
@@ -0,0 +1,8 @@
+using MaIN.Domain.Entities.Agents.Commands.Base;
+
+namespace MaIN.Domain.Entities.Agents.Commands;
+
+public class StartCommand : BaseCommand
+{
+ public string InitialPrompt { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/Chat.cs b/src/MaIN.Domain/Entities/Chat.cs
new file mode 100644
index 00000000..a74e9bd6
--- /dev/null
+++ b/src/MaIN.Domain/Entities/Chat.cs
@@ -0,0 +1,12 @@
+namespace MaIN.Domain.Entities;
+
+public class Chat
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Model { get; set; }
+ public List? Messages { get; set; }
+ public ChatType Type { get; set; }
+ public bool Stream { get; set; } = false;
+ public Dictionary Properties { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Entities/ChatType.cs b/src/MaIN.Domain/Entities/ChatType.cs
new file mode 100644
index 00000000..9d82f090
--- /dev/null
+++ b/src/MaIN.Domain/Entities/ChatType.cs
@@ -0,0 +1,7 @@
+namespace MaIN.Domain.Entities;
+
+public enum ChatType
+{
+ Conversation = 1,
+ Rag = 2,
+}
\ No newline at end of file
diff --git a/src/MaIN.Domain/Message.cs b/src/MaIN.Domain/Entities/Message.cs
similarity index 62%
rename from src/MaIN.Domain/Message.cs
rename to src/MaIN.Domain/Entities/Message.cs
index 2ddd9c32..5d2d5908 100644
--- a/src/MaIN.Domain/Message.cs
+++ b/src/MaIN.Domain/Entities/Message.cs
@@ -1,6 +1,4 @@
-using System.Text.Json.Serialization;
-
-namespace MaIN.Models;
+namespace MaIN.Domain.Entities;
public class Message
{
diff --git a/src/MaIN.Domain/MaIN.Domain.csproj b/src/MaIN.Domain/MaIN.Domain.csproj
index 45444911..3a635329 100644
--- a/src/MaIN.Domain/MaIN.Domain.csproj
+++ b/src/MaIN.Domain/MaIN.Domain.csproj
@@ -6,8 +6,4 @@
enable
-
-
-
-
diff --git a/src/MaIN.Infrastructure/Bootstrapper.cs b/src/MaIN.Infrastructure/Bootstrapper.cs
index b728b3f4..280b9ecf 100644
--- a/src/MaIN.Infrastructure/Bootstrapper.cs
+++ b/src/MaIN.Infrastructure/Bootstrapper.cs
@@ -1,6 +1,6 @@
using MaIN.Infrastructure.Configuration;
-using MaIN.Infrastructure.Providers.cs;
-using MaIN.Infrastructure.Providers.cs.Abstract;
+using MaIN.Infrastructure.Repositories;
+using MaIN.Infrastructure.Repositories.Abstract;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Driver;
@@ -23,11 +23,18 @@ public static IServiceCollection ConfigureInfrastructure(this IServiceCollection
new MongoClient(configuration.GetSection("MongoDbSettings:ConnectionString").Value));
}
- services.AddScoped(sp =>
+ services.AddSingleton(sp =>
{
var mongoClient = sp.GetRequiredService();
var database = mongoClient.GetDatabase(configuration.GetSection("MongoDbSettings:DatabaseName").Value);
- return new ChatProvider(database, configuration.GetSection("MongoDbSettings:CollectionName").Value!);
+ return new ChatRepository(database, "Chats");
+ });
+
+ services.AddSingleton(sp =>
+ {
+ var mongoClient = sp.GetRequiredService();
+ var database = mongoClient.GetDatabase(configuration.GetSection("MongoDbSettings:DatabaseName").Value);
+ return new AgentRepository(database, "Agents");
});
return services;
diff --git a/src/MaIN.Infrastructure/Models/AgentContextDocument.cs b/src/MaIN.Infrastructure/Models/AgentContextDocument.cs
new file mode 100644
index 00000000..fa4a3853
--- /dev/null
+++ b/src/MaIN.Infrastructure/Models/AgentContextDocument.cs
@@ -0,0 +1,11 @@
+using MaIN.Models.Rag;
+
+namespace MaIN.Infrastructure.Models;
+
+public class AgentContextDocument
+{
+ public string Instruction { get; set; }
+ public AgentSourceDocument? Source { get; set; }
+ public List Steps { get; set; }
+ public List? Relations { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Models/AgentDocument.cs b/src/MaIN.Infrastructure/Models/AgentDocument.cs
new file mode 100644
index 00000000..fd08b8de
--- /dev/null
+++ b/src/MaIN.Infrastructure/Models/AgentDocument.cs
@@ -0,0 +1,15 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace MaIN.Infrastructure.Models;
+
+public class AgentDocument
+{
+ [BsonId]
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Model { get; set; }
+ public string? Description { get; set; }
+ public bool Started { get; set; }
+ public AgentContextDocument Context { get; set; }
+ public string ChatId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Models/AgentSourceDocument.cs b/src/MaIN.Infrastructure/Models/AgentSourceDocument.cs
new file mode 100644
index 00000000..726dc527
--- /dev/null
+++ b/src/MaIN.Infrastructure/Models/AgentSourceDocument.cs
@@ -0,0 +1,7 @@
+namespace MaIN.Models.Rag;
+
+public class AgentSourceDocument
+{
+ public string? DetailsSerialized { get; set; }
+ public AgentSourceTypeDocument Type { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Models/AgentSourceTypeDocument.cs b/src/MaIN.Infrastructure/Models/AgentSourceTypeDocument.cs
new file mode 100644
index 00000000..49bb1d99
--- /dev/null
+++ b/src/MaIN.Infrastructure/Models/AgentSourceTypeDocument.cs
@@ -0,0 +1,10 @@
+namespace MaIN.Models.Rag;
+
+public enum AgentSourceTypeDocument
+{
+ API = 1,
+ SQL = 2,
+ NoSQL = 3,
+ File = 4,
+ Text = 5,
+}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Models/ChatDocument.cs b/src/MaIN.Infrastructure/Models/ChatDocument.cs
index 557b95b8..3737a76d 100644
--- a/src/MaIN.Infrastructure/Models/ChatDocument.cs
+++ b/src/MaIN.Infrastructure/Models/ChatDocument.cs
@@ -9,5 +9,7 @@ public class ChatDocument
public string Name { get; set; }
public string Model { get; set; }
public List Messages { get; set; }
+ public ChatTypeDocument Type { get; set; }
+ public Dictionary Properties { get; set; }
public bool Stream { get; set; } = false;
}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Models/ChatTypeDocument.cs b/src/MaIN.Infrastructure/Models/ChatTypeDocument.cs
new file mode 100644
index 00000000..fd490051
--- /dev/null
+++ b/src/MaIN.Infrastructure/Models/ChatTypeDocument.cs
@@ -0,0 +1,7 @@
+namespace MaIN.Infrastructure.Models;
+
+public enum ChatTypeDocument
+{
+ Conversation = 1,
+ Rag = 2,
+}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Providers.cs/ChatProvider.cs b/src/MaIN.Infrastructure/Providers.cs/ChatProvider.cs
deleted file mode 100644
index 8d00e377..00000000
--- a/src/MaIN.Infrastructure/Providers.cs/ChatProvider.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using MaIN.Infrastructure.Models;
-using MaIN.Infrastructure.Providers.cs.Abstract;
-using MongoDB.Driver;
-
-namespace MaIN.Infrastructure.Providers.cs;
-
-public class ChatProvider(IMongoDatabase database, string collectionName) : IChatProvider
-{
- private readonly IMongoCollection _chats = database.GetCollection(collectionName);
-
- public async Task> GetAllChats()
- {
- return await _chats.Find(chat => true).ToListAsync();
- }
-
- public async Task GetChatById(string id)
- {
- return await _chats.Find(chat => chat.Id == id).FirstOrDefaultAsync();
- }
-
- public async Task AddChat(ChatDocument chat)
- {
- await _chats.InsertOneAsync(chat);
- }
-
- public async Task UpdateChat(string id, ChatDocument chat)
- {
- await _chats.ReplaceOneAsync(x => x.Id == id, chat);
- }
-
- public async Task DeleteChat(string id)
- {
- await _chats.DeleteOneAsync(x => x.Id == id);
- }
-}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Repositories/Abstract/IAgentRepository.cs b/src/MaIN.Infrastructure/Repositories/Abstract/IAgentRepository.cs
new file mode 100644
index 00000000..f6bff715
--- /dev/null
+++ b/src/MaIN.Infrastructure/Repositories/Abstract/IAgentRepository.cs
@@ -0,0 +1,13 @@
+using MaIN.Infrastructure.Models;
+
+namespace MaIN.Infrastructure.Repositories.Abstract;
+
+public interface IAgentRepository
+{
+ Task> GetAllAgents();
+ Task GetAgentById(string id);
+ Task AddAgent(AgentDocument? agent);
+ Task UpdateAgent(string id, AgentDocument? agent);
+ Task DeleteAgent(string id);
+ Task Exists(string id);
+}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Providers.cs/Abstract/IChatProvider.cs b/src/MaIN.Infrastructure/Repositories/Abstract/IChatRepository.cs
similarity index 74%
rename from src/MaIN.Infrastructure/Providers.cs/Abstract/IChatProvider.cs
rename to src/MaIN.Infrastructure/Repositories/Abstract/IChatRepository.cs
index b4205359..0b9cd966 100644
--- a/src/MaIN.Infrastructure/Providers.cs/Abstract/IChatProvider.cs
+++ b/src/MaIN.Infrastructure/Repositories/Abstract/IChatRepository.cs
@@ -1,8 +1,8 @@
using MaIN.Infrastructure.Models;
-namespace MaIN.Infrastructure.Providers.cs.Abstract;
+namespace MaIN.Infrastructure.Repositories.Abstract;
-public interface IChatProvider
+public interface IChatRepository
{
Task> GetAllChats();
Task GetChatById(string id);
diff --git a/src/MaIN.Infrastructure/Repositories/AgentRepository.cs b/src/MaIN.Infrastructure/Repositories/AgentRepository.cs
new file mode 100644
index 00000000..b4d9302b
--- /dev/null
+++ b/src/MaIN.Infrastructure/Repositories/AgentRepository.cs
@@ -0,0 +1,29 @@
+using MaIN.Infrastructure.Models;
+using MaIN.Infrastructure.Repositories.Abstract;
+using MongoDB.Driver;
+
+namespace MaIN.Infrastructure.Repositories;
+
+public class AgentRepository(IMongoDatabase database, string collectionName) : IAgentRepository
+{
+ private readonly IMongoCollection _agents = database.GetCollection(collectionName)!;
+
+ public async Task> GetAllAgents() =>
+ await _agents.Find(chat => true).ToListAsync();
+
+ public async Task GetAgentById(string id) =>
+ await _agents!.Find(agent => agent.Id == id).FirstOrDefaultAsync();
+
+ public async Task AddAgent(AgentDocument? agent) =>
+ await _agents.InsertOneAsync(agent);
+
+ public async Task UpdateAgent(string id, AgentDocument? agent) =>
+ await _agents.ReplaceOneAsync(x => x.Id == id, agent);
+
+ public async Task DeleteAgent(string id) =>
+ await _agents.DeleteOneAsync(x => x.Id == id);
+
+ public async Task Exists(string id) =>
+ (await _agents.CountDocumentsAsync(x => x!.Id == id)) > 0;
+
+}
\ No newline at end of file
diff --git a/src/MaIN.Infrastructure/Repositories/ChatRepository.cs b/src/MaIN.Infrastructure/Repositories/ChatRepository.cs
new file mode 100644
index 00000000..aaa3ddd3
--- /dev/null
+++ b/src/MaIN.Infrastructure/Repositories/ChatRepository.cs
@@ -0,0 +1,25 @@
+using MaIN.Infrastructure.Models;
+using MaIN.Infrastructure.Repositories.Abstract;
+using MongoDB.Driver;
+
+namespace MaIN.Infrastructure.Repositories;
+
+public class ChatRepository(IMongoDatabase database, string collectionName) : IChatRepository
+{
+ private readonly IMongoCollection _chats = database.GetCollection(collectionName);
+
+ public async Task> GetAllChats() =>
+ await _chats.Find(chat => true).ToListAsync();
+
+ public async Task GetChatById(string id) =>
+ await _chats.Find(chat => chat.Id == id).FirstOrDefaultAsync();
+
+ public async Task AddChat(ChatDocument chat) =>
+ await _chats.InsertOneAsync(chat);
+
+ public async Task UpdateChat(string id, ChatDocument chat) =>
+ await _chats.ReplaceOneAsync(x => x.Id == id, chat);
+
+ public async Task DeleteChat(string id) =>
+ await _chats.DeleteOneAsync(x => x.Id == id);
+}
\ No newline at end of file
diff --git a/src/MaIN.RAG.Demo/MaIN.RAG.Demo.csproj b/src/MaIN.RAG.Demo/MaIN.RAG.Demo.csproj
new file mode 100644
index 00000000..37dc7448
--- /dev/null
+++ b/src/MaIN.RAG.Demo/MaIN.RAG.Demo.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
diff --git a/src/MaIN.RAG.Demo/MaIN.RAG.Demo.http b/src/MaIN.RAG.Demo/MaIN.RAG.Demo.http
new file mode 100644
index 00000000..fdce86c5
--- /dev/null
+++ b/src/MaIN.RAG.Demo/MaIN.RAG.Demo.http
@@ -0,0 +1,6 @@
+@MaIN.RAG.Demo_HostAddress = http://localhost:5098
+
+GET {{MaIN.RAG.Demo_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/src/MaIN.RAG.Demo/Program.cs b/src/MaIN.RAG.Demo/Program.cs
new file mode 100644
index 00000000..5972f42d
--- /dev/null
+++ b/src/MaIN.RAG.Demo/Program.cs
@@ -0,0 +1,51 @@
+using System.Text.Json;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+
+var serializeOptions = new JsonSerializerOptions
+{
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+};
+
+
+app.MapGet("/items/", () => Results.Ok(
+ JsonSerializer.Deserialize>(
+ File.ReadAllText("json/items.json"), serializeOptions)))
+ .WithName("GetHardwareItems")
+ .WithOpenApi();
+
+app.Run();
+
+
+public class Hardware
+{
+ public int Id { get; set; }
+ public string Image { get; set; }
+ public string Name { get; set; }
+ public string Brand { get; set; }
+ public string Processor { get; set; }
+ public string Type { get; set; }
+ public string Ram { get; set; }
+ public string Storage { get; set; }
+ public string Gpu { get; set; }
+ public double Price { get; set; }
+ public string Availability { get; set; }
+ public string Description { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.RAG.Demo/Properties/launchSettings.json b/src/MaIN.RAG.Demo/Properties/launchSettings.json
new file mode 100644
index 00000000..75df4c08
--- /dev/null
+++ b/src/MaIN.RAG.Demo/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:54552",
+ "sslPort": 44303
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5098",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7205;http://localhost:5098",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/MaIN.RAG.Demo/appsettings.Development.json b/src/MaIN.RAG.Demo/appsettings.Development.json
new file mode 100644
index 00000000..0c208ae9
--- /dev/null
+++ b/src/MaIN.RAG.Demo/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/MaIN.RAG.Demo/appsettings.json b/src/MaIN.RAG.Demo/appsettings.json
new file mode 100644
index 00000000..10f68b8c
--- /dev/null
+++ b/src/MaIN.RAG.Demo/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/MaIN.RAG.Demo/json/items.json b/src/MaIN.RAG.Demo/json/items.json
new file mode 100644
index 00000000..a221d646
--- /dev/null
+++ b/src/MaIN.RAG.Demo/json/items.json
@@ -0,0 +1,170 @@
+[
+ {
+ "id": 1,
+ "image": "pc1.jpg",
+ "type": "PC",
+ "name": "Gaming PC Ultra",
+ "brand": "PowerTech",
+ "processor": "Intel Core i9-11900K",
+ "ram": "32GB DDR4",
+ "storage": "1TB SSD + 2TB HDD",
+ "gpu": "NVIDIA GeForce RTX 3090",
+ "price": 2999.99,
+ "availability": "In Stock",
+ "description": "The Gaming PC Ultra from PowerTech features an Intel Core i9 processor and NVIDIA RTX 3090, making it perfect for high-end gaming and demanding applications. With 32GB of RAM and ample storage, this PC can handle any task with ease."
+ },
+ {
+ "id": 2,
+ "image": "pc2.png",
+ "type": "PC",
+ "name": "Workstation Pro",
+ "brand": "TechMaster",
+ "processor": "AMD Ryzen 9 5950X",
+ "ram": "64GB DDR4",
+ "storage": "2TB SSD",
+ "gpu": "NVIDIA Quadro RTX 5000",
+ "price": 3499.99,
+ "availability": "In Stock",
+ "description": "TechMaster's Workstation Pro is designed for professionals who require top-tier performance. With an AMD Ryzen 9 CPU, 64GB RAM, and NVIDIA Quadro RTX GPU, this workstation excels in 3D rendering, video editing, and other intensive tasks."
+ },
+ {
+ "id": 3,
+ "image": "pc3.jpg",
+ "type": "PC",
+ "name": "Budget Gamer",
+ "brand": "EconoPC",
+ "processor": "Intel Core i5-10400F",
+ "ram": "16GB DDR4",
+ "storage": "512GB SSD",
+ "gpu": "NVIDIA GeForce GTX 1660 Super",
+ "price": 799.99,
+ "availability": "Out of Stock",
+ "description": "The Budget Gamer by EconoPC provides excellent value for entry-level gaming. Featuring an Intel Core i5 processor and NVIDIA GTX 1660 Super GPU, this PC delivers smooth performance for popular games at an affordable price."
+ },
+ {
+ "id": 4,
+ "image": "pc4.jpg",
+ "type": "PC",
+ "name": "All-Purpose PC",
+ "brand": "ValueComp",
+ "processor": "AMD Ryzen 5 3600",
+ "ram": "16GB DDR4",
+ "storage": "1TB HDD",
+ "gpu": "AMD Radeon RX 5700",
+ "price": 999.99,
+ "availability": "In Stock",
+ "description": "ValueComp's All-Purpose PC is a versatile system suitable for gaming, work, and everyday use. It features an AMD Ryzen 5 processor, 16GB RAM, and a Radeon RX 5700 GPU, ensuring balanced performance for a variety of tasks."
+ },
+ {
+ "id": 5,
+ "image": "pc5.jpg",
+ "type": "PC",
+ "name": "Compact Office PC",
+ "brand": "OfficeMate",
+ "processor": "Intel Core i3-10100",
+ "ram": "8GB DDR4",
+ "storage": "256GB SSD",
+ "gpu": "Integrated Graphics",
+ "price": 499.99,
+ "availability": "In Stock",
+ "description": "The Compact Office PC by OfficeMate is ideal for small spaces and routine office tasks. Equipped with an Intel Core i3 processor and 8GB of RAM, it offers reliable performance for word processing, spreadsheets, and internet browsing."
+ },
+ {
+ "id": 6,
+ "image": "pc6.jpg",
+ "type": "PC",
+ "name": "Performance Beast",
+ "brand": "ExtremeTech",
+ "processor": "Intel Core i7-12700K",
+ "ram": "32GB DDR5",
+ "storage": "2TB SSD",
+ "gpu": "NVIDIA GeForce RTX 3080",
+ "price": 2599.99,
+ "availability": "In Stock",
+ "description": "ExtremeTech's Performance Beast is designed for the most demanding users. With an Intel Core i7 processor, 32GB DDR5 RAM, and NVIDIA RTX 3080 GPU, this PC is ideal for gaming, 3D rendering, and other high-performance applications."
+ },
+ {
+ "id": 7,
+ "image": "laptop1.jpg",
+ "type": "Laptop",
+ "name": "UltraBook Pro",
+ "brand": "PowerTech",
+ "processor": "Intel Core i7-1165G7",
+ "ram": "16GB LPDDR4x",
+ "storage": "512GB SSD",
+ "gpu": "Intel Iris Xe Graphics",
+ "price": 1299.99,
+ "availability": "In Stock",
+ "description": "The UltraBook Pro by PowerTech offers a sleek design with powerful performance, featuring an Intel Core i7 processor and Intel Iris Xe Graphics. With 16GB of RAM and a 512GB SSD, this laptop is perfect for professionals on the go."
+ },
+ {
+ "id": 8,
+ "image": "laptop2.jpg",
+ "type": "Laptop",
+ "name": "Gaming Laptop X",
+ "brand": "TechMaster",
+ "processor": "AMD Ryzen 7 5800H",
+ "ram": "32GB DDR4",
+ "storage": "1TB SSD",
+ "gpu": "NVIDIA GeForce RTX 3070",
+ "price": 1999.99,
+ "availability": "In Stock",
+ "description": "TechMaster's Gaming Laptop X is designed for gamers who need high performance on the go. Featuring an AMD Ryzen 7 CPU and NVIDIA RTX 3070 GPU, this laptop delivers smooth gaming experiences and fast load times."
+ },
+ {
+ "id": 9,
+ "image": "laptop3.jpg",
+ "type": "Laptop",
+ "name": "Budget Laptop",
+ "brand": "EconoPC",
+ "processor": "Intel Core i3-1115G4",
+ "ram": "8GB DDR4",
+ "storage": "256GB SSD",
+ "gpu": "Integrated Graphics",
+ "price": 499.99,
+ "availability": "Out of Stock",
+ "description": "The Budget Laptop by EconoPC offers essential features at an affordable price. With an Intel Core i3 processor and 8GB of RAM, it's suitable for students and casual users who need reliable performance for everyday tasks."
+ },
+ {
+ "id": 10,
+ "image": "laptop4.jpg",
+ "type": "Laptop",
+ "name": "All-Purpose Laptop",
+ "brand": "ValueComp",
+ "processor": "AMD Ryzen 5 4500U",
+ "ram": "16GB DDR4",
+ "storage": "512GB SSD",
+ "gpu": "AMD Radeon Graphics",
+ "price": 899.99,
+ "availability": "In Stock",
+ "description": "ValueComp's All-Purpose Laptop is versatile and powerful, equipped with an AMD Ryzen 5 processor and Radeon Graphics. It's ideal for a variety of tasks, including work, entertainment, and light gaming."
+ },
+ {
+ "id": 11,
+ "image": "laptop5.jpg",
+ "type": "Laptop",
+ "name": "Compact Office Laptop",
+ "brand": "OfficeMate",
+ "processor": "Intel Core i5-1135G7",
+ "ram": "8GB LPDDR4x",
+ "storage": "512GB SSD",
+ "gpu": "Intel Iris Xe Graphics",
+ "price": 799.99,
+ "availability": "In Stock",
+ "description": "The Compact Office Laptop by OfficeMate is perfect for professionals who need a portable and efficient device. With an Intel Core i5 processor and 8GB of RAM, it offers solid performance for office applications and multitasking."
+ },
+ {
+ "id": 12,
+ "image": "laptop6.jpg",
+ "type": "Laptop",
+ "name": "Creative Laptop",
+ "brand": "CreativeTech",
+ "processor": "Apple M1",
+ "ram": "16GB Unified Memory",
+ "storage": "1TB SSD",
+ "gpu": "Apple M1 GPU",
+ "price": 1499.99,
+ "availability": "In Stock",
+ "description": "CreativeTech's Creative Laptop is perfect for designers and content creators. Featuring the Apple M1 chip, 16GB of unified memory, and a 1TB SSD, this laptop offers exceptional performance and efficiency for creative tasks."
+ }
+]
diff --git a/src/MaIN.Services/Bootstrapper.cs b/src/MaIN.Services/Bootstrapper.cs
index e378ef01..8966f844 100644
--- a/src/MaIN.Services/Bootstrapper.cs
+++ b/src/MaIN.Services/Bootstrapper.cs
@@ -8,8 +8,11 @@ public static class Bootstrapper
{
public static IServiceCollection ConfigureApplication(this IServiceCollection serviceCollection)
{
- serviceCollection.AddScoped();
- serviceCollection.AddScoped();
+ //TODO solve this with separate registration for actions purposes
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
return serviceCollection;
}
diff --git a/src/MaIN.Services/MaIN.Services.csproj b/src/MaIN.Services/MaIN.Services.csproj
index 25e44223..baa31608 100644
--- a/src/MaIN.Services/MaIN.Services.csproj
+++ b/src/MaIN.Services/MaIN.Services.csproj
@@ -14,6 +14,7 @@
+
diff --git a/src/MaIN.Services/Mappers/AgentMapper.cs b/src/MaIN.Services/Mappers/AgentMapper.cs
new file mode 100644
index 00000000..6a23a0a7
--- /dev/null
+++ b/src/MaIN.Services/Mappers/AgentMapper.cs
@@ -0,0 +1,119 @@
+using System.Text.Json;
+using MaIN.Domain.Entities.Agents;
+using MaIN.Domain.Entities.Agents.AgentSource;
+using MaIN.Infrastructure.Models;
+using MaIN.Models.Rag;
+using MaIN.Services.Steps;
+
+namespace MaIN.Services.Mappers;
+
+public static class AgentMapper
+{
+ public static AgentDto ToDto(this Agent? agent)
+ => new()
+ {
+ Id = agent.Id,
+ Name = agent.Name,
+ Model = agent.Model,
+ Started = agent.Started,
+ Description = agent.Description,
+ Context = agent.Context.ToDto()
+ };
+
+ public static AgentContextDto ToDto(this AgentContext agentContext)
+ => new()
+ {
+ Instruction = agentContext.Instruction,
+ Relations = agentContext.Relations,
+ Steps = new List(),
+ Source = new AgentSourceDto()
+ {
+ Details = agentContext.Source.Details,
+ Type = Enum.Parse(agentContext.Source.Type.ToString())
+ }
+ };
+
+ public static Agent ToDomain(this AgentDto agent)
+ => new()
+ {
+ Id = agent.Id,
+ Name = agent.Name,
+ Model = agent.Model,
+ Started = agent.Started,
+ Description = agent.Description,
+ Context = agent.Context.ToDomain()
+ };
+
+ public static AgentContext ToDomain(this AgentContextDto agentContextDto)
+ => new()
+ {
+ Instruction = agentContextDto.Instruction,
+ Relations = agentContextDto?.Relations,
+ Source = agentContextDto?.Source is not null ? new AgentSource()
+ {
+ Details = agentContextDto?.Source?.Details,
+ Type = Enum.Parse(agentContextDto?.Source?.Type.ToString()!)
+ } : null,
+ Steps = agentContextDto!.Steps.ToLookup(x => x, y => Actions.Steps[y.Split('+').First()])
+ };
+
+ private static AgentSourceDetailsBase MapDetailsToType(object? details, AgentSourceTypeDto? sourceDetailsType)
+ {
+ return sourceDetailsType switch
+ {
+ AgentSourceTypeDto.Text => (AgentTextSourceDetails)details!,
+ AgentSourceTypeDto.File => (AgentFileSourceDetails)details!,
+ AgentSourceTypeDto.API => (AgentApiSourceDetails)details!,
+ //TBD add all types
+ _ => new()
+ };
+ }
+
+ public static AgentContextDocument ToDocument(this AgentContext context)
+ => new()
+ {
+ Instruction = context.Instruction,
+ Relations = context.Relations?.ToList(),
+ Steps = context.Steps.Select(x => x.Key).ToList(),
+ Source = context.Source is not null ? new AgentSourceDocument()
+ {
+ DetailsSerialized = JsonSerializer.Serialize(context.Source.Details),
+ Type = Enum.Parse(context.Source.Type.ToString())
+ } : null
+ };
+
+ public static AgentDocument? ToDocument(this Agent agent)
+ => new()
+ {
+ Id = agent.Id,
+ Name = agent.Name,
+ Model = agent.Model,
+ Started = agent.Started,
+ Description = agent.Description,
+ Context = agent.Context.ToDocument()
+ };
+
+ public static Agent? ToDomain(this AgentDocument? agent)
+ => new()
+ {
+ Id = agent.Id,
+ Name = agent.Name,
+ Model = agent.Model,
+ Started = agent.Started,
+ Description = agent.Description,
+ Context = agent.Context.ToDomain()
+ };
+
+ public static AgentContext ToDomain(this AgentContextDocument agentContextDocument)
+ => new()
+ {
+ Instruction = agentContextDocument.Instruction,
+ Relations = agentContextDocument?.Relations,
+ Source = new AgentSource
+ {
+ Details = agentContextDocument?.Source?.DetailsSerialized,
+ Type = Enum.Parse(agentContextDocument?.Source?.Type.ToString() ?? AgentSourceType.Text.ToString())
+ },
+ Steps = agentContextDocument!.Steps.ToLookup(x => x, y => Actions.Steps[y.Split('+').First()])
+ };
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Mappers/ChatMapper.cs b/src/MaIN.Services/Mappers/ChatMapper.cs
index f94bde47..30c87090 100644
--- a/src/MaIN.Services/Mappers/ChatMapper.cs
+++ b/src/MaIN.Services/Mappers/ChatMapper.cs
@@ -1,3 +1,4 @@
+using MaIN.Domain.Entities;
using MaIN.Infrastructure.Models;
using MaIN.Models;
using MaIN.Services.Models;
@@ -13,7 +14,9 @@ public static ChatDto ToDto(this Chat chat)
Name = chat.Name,
Model = chat.Model,
Messages = chat.Messages.Select(m => m.ToDto()).ToList(),
- Stream = chat.Stream
+ Stream = chat.Stream,
+ Type = Enum.Parse(chat.Type.ToString()),
+ Properties = chat.Properties
};
public static MessageDto ToDto(this Message message)
@@ -23,14 +26,16 @@ public static MessageDto ToDto(this Message message)
Role = message.Role
};
- public static Chat ToDomain(this ChatDto chat)
+ public static Chat? ToDomain(this ChatDto chat)
=> new Chat()
{
Id = chat.Id,
Name = chat.Name,
Model = chat.Model,
- Messages = chat.Messages.Select(m => m.ToDomain()).ToList(),
- Stream = chat.Stream
+ Messages = chat.Messages?.Select(m => m.ToDomain()).ToList(),
+ Stream = chat.Stream,
+ Type = Enum.Parse(chat.Type.ToString()),
+ Properties = chat.Properties
};
public static Message ToDomain(this MessageDto message)
@@ -47,14 +52,16 @@ public static MessageDocument ToDocument(this Message message)
Role = message.Role
};
- public static ChatDocument ToDocument(this Chat chat)
+ public static ChatDocument ToDocument(this Chat? chat)
=> new ChatDocument()
{
Id = chat.Id,
Name = chat.Name,
Model = chat.Model,
Messages = chat.Messages.Select(m => m.ToDocument()).ToList(),
- Stream = chat.Stream
+ Properties = chat.Properties,
+ Stream = chat.Stream,
+ Type = Enum.Parse(chat.Type.ToString())
};
public static Chat ToDomain(this ChatDocument chat)
@@ -64,7 +71,9 @@ public static Chat ToDomain(this ChatDocument chat)
Name = chat.Name,
Model = chat.Model,
Messages = chat.Messages.Select(m => m.ToDomain()).ToList(),
- Stream = chat.Stream
+ Stream = chat.Stream,
+ Properties = chat.Properties,
+ Type = Enum.Parse(chat.Type.ToString())
};
public static Message ToDomain(this MessageDocument message)
diff --git a/src/MaIN.Services/Models/ChatDto.cs b/src/MaIN.Services/Models/ChatDto.cs
index cb62e2b0..cc794c4e 100644
--- a/src/MaIN.Services/Models/ChatDto.cs
+++ b/src/MaIN.Services/Models/ChatDto.cs
@@ -11,8 +11,13 @@ public class ChatDto
[JsonPropertyName("model")]
public string Model { get; set; }
[JsonPropertyName("messages")]
- public List Messages { get; set; }
+ public List? Messages { get; set; }
+ [JsonPropertyName("type")]
+ public ChatTypeDto Type { get; set; }
[JsonPropertyName("stream")]
public bool Stream { get; set; } = false;
+
+ [JsonPropertyName("properties")]
+ public Dictionary Properties { get; set; }
}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/ChatTypeDto.cs b/src/MaIN.Services/Models/ChatTypeDto.cs
new file mode 100644
index 00000000..37d98918
--- /dev/null
+++ b/src/MaIN.Services/Models/ChatTypeDto.cs
@@ -0,0 +1,7 @@
+namespace MaIN.Services.Models;
+
+public enum ChatTypeDto
+{
+ Conversation = 1,
+ Rag = 2,
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/Rag/AgentContextDto.cs b/src/MaIN.Services/Models/Rag/AgentContextDto.cs
new file mode 100644
index 00000000..0fe64a6b
--- /dev/null
+++ b/src/MaIN.Services/Models/Rag/AgentContextDto.cs
@@ -0,0 +1,10 @@
+namespace MaIN.Models.Rag;
+
+public class AgentContextDto
+{
+ public string Instruction { get; set; }
+ public AgentSourceDto Source { get; set; }
+ public List Steps { get; set; }
+ public List? Relations { get; set; }
+
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/Rag/AgentDto.cs b/src/MaIN.Services/Models/Rag/AgentDto.cs
new file mode 100644
index 00000000..7f255fb4
--- /dev/null
+++ b/src/MaIN.Services/Models/Rag/AgentDto.cs
@@ -0,0 +1,11 @@
+namespace MaIN.Models.Rag;
+
+public class AgentDto
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Model { get; set; }
+ public string? Description { get; set; }
+ public bool Started { get; set; }
+ public AgentContextDto Context { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/Rag/AgentRelationDto.cs b/src/MaIN.Services/Models/Rag/AgentRelationDto.cs
new file mode 100644
index 00000000..2c75cb4c
--- /dev/null
+++ b/src/MaIN.Services/Models/Rag/AgentRelationDto.cs
@@ -0,0 +1,7 @@
+namespace MaIN.Models.Rag;
+
+public class AgentRelationDto
+{
+ public string Id { get; set; }
+ public string AgentPurpose { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/Rag/AgentSource/AgentApiSourceDetailsDto.cs b/src/MaIN.Services/Models/Rag/AgentSource/AgentApiSourceDetailsDto.cs
new file mode 100644
index 00000000..4e18d58c
--- /dev/null
+++ b/src/MaIN.Services/Models/Rag/AgentSource/AgentApiSourceDetailsDto.cs
@@ -0,0 +1,11 @@
+using MaIN.Domain.Entities.Agents.AgentSource;
+
+namespace MaIN.Models.Rag;
+
+public class AgentApiSourceDetailsDto : AgentSourceDetailsBase
+{
+ public string Url { get; set; }
+ public string Method { get; set; }
+ public string? Payload { get; set; }
+ public string? Query { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/Rag/AgentSource/AgentFileSourceDetailsDto.cs b/src/MaIN.Services/Models/Rag/AgentSource/AgentFileSourceDetailsDto.cs
new file mode 100644
index 00000000..8f672c1b
--- /dev/null
+++ b/src/MaIN.Services/Models/Rag/AgentSource/AgentFileSourceDetailsDto.cs
@@ -0,0 +1,8 @@
+using MaIN.Domain.Entities.Agents.AgentSource;
+
+namespace MaIN.Models.Rag;
+
+public class AgentFileSourceDetailsDto : AgentSourceDetailsBase
+{
+ public string Path { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/Rag/AgentSource/AgentSourceDto.cs b/src/MaIN.Services/Models/Rag/AgentSource/AgentSourceDto.cs
new file mode 100644
index 00000000..e6414bbb
--- /dev/null
+++ b/src/MaIN.Services/Models/Rag/AgentSource/AgentSourceDto.cs
@@ -0,0 +1,7 @@
+namespace MaIN.Models.Rag;
+
+public class AgentSourceDto
+{
+ public object? Details { get; set; }
+ public AgentSourceTypeDto Type { get; set; }
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Models/Rag/AgentSource/AgentSourceTypeDto.cs b/src/MaIN.Services/Models/Rag/AgentSource/AgentSourceTypeDto.cs
new file mode 100644
index 00000000..09eb5062
--- /dev/null
+++ b/src/MaIN.Services/Models/Rag/AgentSource/AgentSourceTypeDto.cs
@@ -0,0 +1,10 @@
+namespace MaIN.Models.Rag;
+
+public enum AgentSourceTypeDto
+{
+ API = 1,
+ SQL = 2,
+ NoSQL = 3,
+ File = 4,
+ Text = 5,
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Services/Abstract/IAgentService.cs b/src/MaIN.Services/Services/Abstract/IAgentService.cs
new file mode 100644
index 00000000..134176f5
--- /dev/null
+++ b/src/MaIN.Services/Services/Abstract/IAgentService.cs
@@ -0,0 +1,16 @@
+using MaIN.Domain.Entities;
+using MaIN.Domain.Entities.Agents;
+
+namespace MaIN.Services.Services.Abstract;
+
+public interface IAgentService
+{
+ Task Process(Chat? chat, string agentId, bool translatePrompt = false);
+ Task CreateAgent(Agent agent);
+ Task GetChatByAgent(string agentId);
+ Task Restart(string agentId);
+ Task> GetAgents();
+ Task GetAgentById(string id);
+ Task DeleteAgent(string id);
+ Task AgentExists(string id);
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Services/Abstract/IChatService.cs b/src/MaIN.Services/Services/Abstract/IChatService.cs
index 8600acc3..8f15186d 100644
--- a/src/MaIN.Services/Services/Abstract/IChatService.cs
+++ b/src/MaIN.Services/Services/Abstract/IChatService.cs
@@ -1,3 +1,4 @@
+using MaIN.Domain.Entities;
using MaIN.Models;
using MaIN.Services.Models;
using MaIN.Services.Models.Ollama;
@@ -6,10 +7,9 @@ namespace MaIN.Services.Services.Abstract;
public interface IChatService
{
- Task Create(Chat chat);
- Task Completions(Chat chat);
+ Task Create(Chat? chat);
+ Task Completions(Chat? chat, bool translatePrompt = false);
Task Delete(string id);
Task GetById(string id);
- Task> GetCurrentModels();
Task> GetAll();
}
\ No newline at end of file
diff --git a/src/MaIN.Services/Services/Abstract/IOllamaService.cs b/src/MaIN.Services/Services/Abstract/IOllamaService.cs
new file mode 100644
index 00000000..90435fa7
--- /dev/null
+++ b/src/MaIN.Services/Services/Abstract/IOllamaService.cs
@@ -0,0 +1,12 @@
+using MaIN.Domain.Entities;
+using MaIN.Models;
+using MaIN.Services.Models;
+using MaIN.Services.Models.Ollama;
+
+namespace MaIN.Services.Services.Abstract;
+
+public interface IOllamaService
+{
+ Task Send(Chat? chat);
+ Task> GetCurrentModels();
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Services/AgentService.cs b/src/MaIN.Services/Services/AgentService.cs
new file mode 100644
index 00000000..867a1e81
--- /dev/null
+++ b/src/MaIN.Services/Services/AgentService.cs
@@ -0,0 +1,210 @@
+using Amazon.Runtime.Internal.Transform;
+using MaIN.Domain.Entities;
+using MaIN.Domain.Entities.Agents;
+using MaIN.Domain.Entities.Agents.Commands;
+using MaIN.Infrastructure.Models;
+using MaIN.Infrastructure.Repositories.Abstract;
+using MaIN.Models;
+using MaIN.Models.Rag;
+using MaIN.Services.Mappers;
+using MaIN.Services.Models.Ollama;
+using MaIN.Services.Services.Abstract;
+using MaIN.Services.Steps;
+using MongoDB.Driver.Core.Events;
+
+namespace MaIN.Services.Services;
+
+public class AgentService(
+ IAgentRepository agentRepository,
+ IChatRepository chatRepository) : IAgentService
+{
+ public async Task Process(Chat? chat, string agentId, bool translatePrompt = false)
+ {
+ // Fetch the agent details from the repository
+ var agent = await agentRepository.GetAgentById(agentId);
+
+ // Ensure the agent and its context are valid
+ if (agent == null)
+ {
+ throw new ArgumentException("Agent not found.");
+ }
+
+ var context = agent.Context;
+ if (context == null)
+ {
+ throw new ArgumentException("Agent context not found.");
+ }
+
+ chat = await ProcessSteps(context, chat);
+
+ // Return the processed chat
+ return chat;
+ }
+
+ private static async Task ProcessSteps(AgentContextDocument context, Chat? chat)
+ {
+ // Process each step in the defined order
+ foreach (var step in context.Steps)
+ {
+ // provide arguments
+ var stepParts = step.Split('+');
+ var stepName = stepParts[0];
+ var shouldReplaceLastMessage = step.Contains("REPLACE");
+
+ // Create the appropriate command based on the step name
+ switch (stepName)
+ {
+ case "REDIRECT":
+ var redirectCommand = new RedirectCommand
+ {
+ Message = chat?.Messages?.Last()!,
+ RelatedAgentId = stepParts[1],
+ SaveAs = Enum.Parse(stepParts[2])
+ };
+
+ var message = await Actions.CallAsync("REDIRECT", redirectCommand) as Message;
+ if (redirectCommand.SaveAs == OutputTypeOfRedirect.AS_Filter)
+ {
+ chat?.Properties.TryAdd("data_filter", message!.Content);
+ }
+ else
+ {
+ if (shouldReplaceLastMessage)
+ {
+ chat?.Messages?.RemoveAt(chat.Messages.Count - 1);
+ }
+
+ chat?.Messages?.Add(message!);
+ }
+
+ break;
+
+ case "FETCH_DATA":
+ var filterExist =
+ chat!.Properties.TryGetValue("data_filter",
+ out var filter); //TODO define a way to create multiple filters
+ var fetchCommand = new FetchCommand
+ {
+ Chat = chat,
+ Filter = filterExist ? filter : string.Empty,
+ Context = context.ToDomain()
+ };
+ var fetchCommandResponse =
+ (await Actions.CallAsync("FETCH_DATA_WITH_FILTER", fetchCommand) as Message)!;
+ chat.Messages?.Add(fetchCommandResponse);
+ break;
+
+ case "FETCH_DATA*":
+ if (chat!.Properties.ContainsKey("FETCH_DATA*"))
+ {
+ break;
+ }
+
+ var filterExists =
+ chat!.Properties.TryGetValue("data_filter",
+ out var filterData); //TODO define a way to create multiple filters
+ var fetchCommandOnce = new FetchCommand
+ {
+ Context = context.ToDomain(),
+ Chat = chat,
+ Filter = filterExists ? filterData : string.Empty
+ };
+ var response = (await Actions.CallAsync("FETCH_DATA", fetchCommandOnce) as Message)!;
+ chat.Messages?.Add(response);
+
+ chat.Properties.Add("FETCH_DATA*", string.Empty);
+ break;
+
+ case "ANSWER":
+ var answerCommand = new AnswerCommand
+ {
+ Chat = chat
+ };
+ var answerResponse = (await Actions.CallAsync("ANSWER", answerCommand) as Message)!;
+
+ if (shouldReplaceLastMessage)
+ {
+ chat?.Messages?.RemoveAt(chat.Messages.Count - 1);
+ }
+
+ chat?.Messages?.Add(answerResponse);
+ break;
+
+ default:
+ throw new InvalidOperationException($"Unknown step: {stepName}");
+ }
+ }
+
+ return chat;
+ }
+
+ public async Task CreateAgent(Agent agent)
+ {
+ var chat = new Chat()
+ {
+ Id = Guid.NewGuid().ToString(),
+ Model = agent.Model,
+ Name = agent.Name,
+ Stream = false,
+ Messages = new List(),
+ Type = ChatType.Rag,
+ };
+
+ var startCommand = new StartCommand()
+ {
+ Chat = chat,
+ InitialPrompt = agent.Context.Instruction,
+ };
+
+ var result = await Actions.CallAsync("START", startCommand) as Message;
+ result!.Role = "system";
+ agent.Started = true;
+ //chat.Messages.Add(result!);
+ var agentDocument = agent.ToDocument();
+ agentDocument!.ChatId = chat.Id;
+ await chatRepository.AddChat(chat!.ToDocument());
+ await agentRepository.AddAgent(agentDocument);
+ return agent;
+ }
+
+ public async Task GetChatByAgent(string agentId)
+ {
+ var agent = await agentRepository.GetAgentById(agentId);
+ var chat = await chatRepository.GetChatById(agent.ChatId);
+ return chat.ToDomain();
+ }
+
+ public async Task Restart(string agentId)
+ {
+ var agent = await agentRepository.GetAgentById(agentId);
+ var chat = await chatRepository.GetChatById(agent?.ChatId!);
+ chat.Messages = chat.Messages.Take(1).ToList(); //Takes only system message and initial prompt
+ await chatRepository.UpdateChat(chat.Id, chat);
+
+ return chat.ToDomain();
+ }
+
+ public async Task> GetAgents()
+ {
+ var result = await agentRepository.GetAllAgents();
+ return result
+ .Select(x => x.ToDomain())
+ .ToList()!;
+ }
+
+ public async Task GetAgentById(string id)
+ {
+ var result = await agentRepository.GetAgentById(id);
+ return result?.ToDomain();
+ }
+
+ public async Task DeleteAgent(string id)
+ {
+ var chat = await GetChatByAgent(id);
+ await chatRepository.DeleteChat(chat.Id);
+ await agentRepository.DeleteAgent(id);
+ }
+
+ public Task AgentExists(string id) =>
+ agentRepository.Exists(id);
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Services/ChatService.cs b/src/MaIN.Services/Services/ChatService.cs
index 988d7277..cd5f9196 100644
--- a/src/MaIN.Services/Services/ChatService.cs
+++ b/src/MaIN.Services/Services/ChatService.cs
@@ -1,6 +1,7 @@
using System.Text.Json;
+using MaIN.Domain.Entities;
using MaIN.Infrastructure.Models;
-using MaIN.Infrastructure.Providers.cs.Abstract;
+using MaIN.Infrastructure.Repositories.Abstract;
using MaIN.Models;
using MaIN.Services.Mappers;
using MaIN.Services.Models;
@@ -11,50 +12,41 @@ namespace MaIN.Services.Services;
public class ChatService(
ITranslatorService translatorService,
- IChatProvider chatProvider,
+ IChatRepository chatProvider,
+ IOllamaService ollamaService,
IHttpClientFactory httpClientFactory) : IChatService
{
- public async Task Create(Chat chat)
- => await chatProvider.AddChat(chat.ToDocument());
+ public async Task Create(Chat? chat)
+ {
+ chat.Type = ChatType.Conversation;
+ await chatProvider.AddChat(chat.ToDocument());
+ }
- public async Task Completions(Chat chat)
+ public async Task Completions(Chat? chat, bool translate = false)
{
var lng = await translatorService.DetectLanguage(chat.Messages.Last().Content);
var originalMessages = chat.Messages;
- var translatedMessages = await Task.WhenAll(chat.Messages.Select(async m => new Message()
- {
- Role = m.Role,
- Content = await translatorService.Translate(m.Content, "en")
- }));
- chat.Messages = translatedMessages.ToList();
- using var client = httpClientFactory.CreateClient();
- var response = await client.PostAsync($"{GetLocalhost()}:11434/api/chat",
- new StringContent(JsonSerializer.Serialize(new ChatOllama()
+ if (translate)
+ {
+ var translatedMessages = await Task.WhenAll(chat.Messages.Select(async m => new Message()
{
- Messages = chat.Messages.Select(x => new MessageDto()
- {
- Content = x.Content,
- Role = x.Role
- }).ToList(),
- Model = chat.Model,
- Stream = chat.Stream
- }), System.Text.Encoding.UTF8, "application/json"));
+ Role = m.Role,
+ Content = await translatorService.Translate(m.Content, "en")
+ }));
+ chat.Messages = translatedMessages.ToList();
+ }
- if (!response.IsSuccessStatusCode)
+ var result = await ollamaService.Send(chat);
+
+ if (translate)
{
- throw new Exception($"Failed to create completion for chat {chat.Id} with message " +
- $"{chat.Messages.Last().Content}, status code {response.StatusCode}");
+ result!.Message.Content = (await translatorService.Translate(result.Message.Content, lng));
}
-
- // Read the response from Ollama
- var responseBody = await response.Content.ReadAsStringAsync();
- var result = JsonSerializer.Deserialize(responseBody);
- result!.Message.Content = (await translatorService.Translate(result.Message.Content, lng));
-
+
originalMessages.Add(new Message()
{
- Content = result.Message.Content,
+ Content = result!.Message.Content,
Role = result.Message.Role
});
chat.Messages = originalMessages;
@@ -73,26 +65,9 @@ public async Task GetById(string id)
return chatDocument.ToDomain();
}
- public async Task> GetCurrentModels()
- {
- using var client = httpClientFactory.CreateClient();
- var response = await client.GetAsync($"{GetLocalhost()}:11434/api/tags");
-
- if (!response.IsSuccessStatusCode)
- {
- throw new Exception($"Failed to fetch models from Ollama, status code {response.StatusCode}");
- }
-
- var result = JsonSerializer.Deserialize(
- await response.Content.ReadAsStringAsync());
-
- return result!.Models.Select(x => x.Name).ToList();
- }
-
public async Task> GetAll()
=> (await chatProvider.GetAllChats())
.Select(x => x.ToDomain()).ToList();
- private static string GetLocalhost() =>
- Environment.GetEnvironmentVariable("LocalHost") ?? "http://localhost";
+
}
\ No newline at end of file
diff --git a/src/MaIN.Services/Services/OllamaService.cs b/src/MaIN.Services/Services/OllamaService.cs
new file mode 100644
index 00000000..bf149d11
--- /dev/null
+++ b/src/MaIN.Services/Services/OllamaService.cs
@@ -0,0 +1,56 @@
+using System.Text.Json;
+using MaIN.Domain.Entities;
+using MaIN.Models;
+using MaIN.Services.Models;
+using MaIN.Services.Models.Ollama;
+using MaIN.Services.Services.Abstract;
+
+namespace MaIN.Services.Services;
+
+public class OllamaService(IHttpClientFactory httpClientFactory) : IOllamaService
+{
+ public async Task Send(Chat? chat)
+ {
+ using var client = httpClientFactory.CreateClient();
+ var response = await client.PostAsync($"{GetLocalhost()}:11434/api/chat",
+ new StringContent(JsonSerializer.Serialize(new ChatOllama()
+ {
+ Messages = chat.Messages.Select(x => new MessageDto()
+ {
+ Content = x.Content,
+ Role = x.Role
+ }).ToList(),
+ Model = chat.Model,
+ Stream = chat.Stream,
+ }), System.Text.Encoding.UTF8, "application/json"));
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new Exception($"Failed to create completion for chat {chat.Id} with message " +
+ $"{chat.Messages.Last().Content}, status code {response.StatusCode}");
+ }
+
+ var responseBody = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(responseBody);
+ return result;
+ }
+
+ public async Task> GetCurrentModels()
+ {
+ using var client = httpClientFactory.CreateClient();
+ var response = await client.GetAsync($"{GetLocalhost()}:11434/api/tags");
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new Exception($"Failed to fetch models from Ollama, status code {response.StatusCode}");
+ }
+
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(stringResponse);
+
+ return result!.Models.Select(x => x.Name).ToList();
+ }
+
+ private static string GetLocalhost() =>
+ Environment.GetEnvironmentVariable("LocalHost") ?? "http://localhost";
+}
\ No newline at end of file
diff --git a/src/MaIN.Services/Steps/Actions.cs b/src/MaIN.Services/Steps/Actions.cs
new file mode 100644
index 00000000..8d40ff37
--- /dev/null
+++ b/src/MaIN.Services/Steps/Actions.cs
@@ -0,0 +1,233 @@
+using System.Data.SqlClient;
+using System.Text;
+using System.Text.Json;
+using MaIN.Domain.Entities;
+using MaIN.Domain.Entities.Agents.AgentSource;
+using MaIN.Domain.Entities.Agents.Commands;
+using MaIN.Infrastructure.Repositories.Abstract;
+using MaIN.Services.Mappers;
+using MaIN.Services.Services.Abstract;
+using Microsoft.Extensions.DependencyInjection;
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+namespace MaIN.Services.Steps;
+
+public static class Actions
+{
+ public static Dictionary Steps { get; private set; }
+
+ public static void InitializeAgents(this IServiceProvider serviceProvider)
+ {
+ var ollamaService = serviceProvider.GetRequiredService();
+ var agentService = serviceProvider.GetRequiredService();
+ var httpClientFactory = serviceProvider.GetRequiredService();
+
+ Steps = new Dictionary
+ {
+ {
+ "START", new Func>(async startCommand =>
+ {
+ var message = new Message()
+ {
+ Content = startCommand.InitialPrompt,
+ Role = "system"
+ };
+ startCommand.Chat?.Messages?.Add(message);
+
+ var result = await ollamaService.Send(startCommand.Chat);
+ return result?.Message.ToDomain();
+ })
+ },
+
+ {
+ "REDIRECT", new Func>(async redirectCommand =>
+ {
+ var chat = await agentService.GetChatByAgent(redirectCommand.RelatedAgentId);
+ chat.Messages?.Add(new Message()
+ {
+ Role = "system",
+ Content = redirectCommand.Message.Content //TODO: workaround to fake user input and make agent respond
+ });
+ var result = await agentService.Process(chat, redirectCommand.RelatedAgentId);
+ return result!.Messages?.Last();
+ })
+ },
+
+ {
+ "FETCH_DATA", new Func>(async fetchCommand =>
+ {
+ //TBD
+ var data = fetchCommand.Context.Source.Type switch
+ {
+ AgentSourceType.File => await File.ReadAllTextAsync(
+ ((AgentFileSourceDetails)fetchCommand.Context.Source.Details!).Path),
+ AgentSourceType.Text => ((AgentTextSourceDetails)fetchCommand.Context.Source.Details!).Text,
+ AgentSourceType.API => await FetchApiData(fetchCommand.Context.Source.Details,
+ fetchCommand.Filter, httpClientFactory),
+ AgentSourceType.SQL => await FetchSqlData(fetchCommand.Context.Source.Details,
+ fetchCommand.Filter),
+ AgentSourceType.NoSQL => await FetchNoSqlData(fetchCommand.Context.Source.Details,
+ fetchCommand.Filter),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ var dataMsg = new Message()
+ {
+ Content =
+ $"Here is data from internal data source, This is what you should use to answer questions: {data}",
+ Role = "system"
+ };
+
+ return dataMsg;
+ })
+ },
+
+ { //TODO better handling for duplication
+ "FETCH_DATA*", new Func>(async fetchCommand =>
+ {
+ //TBD
+ var data = fetchCommand.Context.Source.Type switch
+ {
+ AgentSourceType.File => await File.ReadAllTextAsync(
+ JsonSerializer.Deserialize(fetchCommand.Context.Source.Details?.ToString()!)!.Path),
+ AgentSourceType.Text => fetchCommand.Context.Source.Details!.ToString(),
+ AgentSourceType.API => await FetchApiData(fetchCommand.Context.Source.Details,
+ fetchCommand.Filter, httpClientFactory),
+ AgentSourceType.SQL => await FetchSqlData(fetchCommand.Context.Source.Details,
+ fetchCommand.Filter),
+ AgentSourceType.NoSQL => await FetchNoSqlData(fetchCommand.Context.Source.Details,
+ fetchCommand.Filter),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ var dataMsg = new Message()
+ {
+ Content =
+ $"Here is data from internal data source, This is what you should use to answer questions: {data}, Dont mention anything about this to a user, this is for internal purpose",
+ Role = "system"
+ };
+
+ return dataMsg;
+ })
+ },
+
+ {
+ "ANSWER", new Func>(async answerCommand =>
+ {
+ var result = await ollamaService.Send(answerCommand.Chat);
+ return result!.Message.ToDomain();
+ })
+ },
+ };
+ }
+
+ private static async Task FetchNoSqlData(object? sourceDetails, string? fetchCommandFilter)
+ {
+ var noSqlDetails = JsonSerializer.Deserialize(sourceDetails.ToString());
+ noSqlDetails!.ConnectionString = noSqlDetails.ConnectionString.Replace("@filter@", fetchCommandFilter);
+ noSqlDetails.Query = noSqlDetails.Query.Replace("@filter@", fetchCommandFilter);
+ noSqlDetails.Collection = noSqlDetails.Collection.Replace("@filter@", fetchCommandFilter);
+ var clientSettings = MongoClientSettings.FromConnectionString(noSqlDetails!.ConnectionString);
+ var client = new MongoClient(clientSettings);
+
+ var database = client.GetDatabase(noSqlDetails.DbName);
+ var collection = database.GetCollection(noSqlDetails.Collection);
+ var bsonQuery = BsonDocument.Parse(noSqlDetails.Query);
+ var filter = new BsonDocumentFilterDefinition(bsonQuery);
+
+ var documents = collection.Find(filter).ToList();
+ var data = new List>();
+
+ var row = new Dictionary();
+ foreach (var document in documents)
+ {
+ foreach (var element in document.Elements)
+ {
+ row[element.Name] = BsonTypeMapper.MapToDotNetValue(element.Value);
+ }
+
+ data.Add(row);
+ }
+
+ var jsonResult = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
+ return jsonResult;
+ }
+
+ private static async Task FetchSqlData(object? sourceDetails, string? fetchCommandFilter)
+ {
+ var sqlDetails = JsonSerializer.Deserialize(sourceDetails.ToString());
+ sqlDetails!.ConnectionString = sqlDetails.ConnectionString.Replace("@filter@", fetchCommandFilter);
+ sqlDetails.Query = sqlDetails.Query.Replace("@filter@", fetchCommandFilter);
+ await using SqlConnection connection = new SqlConnection(sqlDetails!.ConnectionString);
+ connection.Open();
+
+ var command = new SqlCommand(sqlDetails.Query, connection);
+ var reader = await command.ExecuteReaderAsync();
+ var data = new List>();
+ if (reader.HasRows)
+ {
+ var columns = reader.GetColumnSchema();
+ while (reader.Read())
+ {
+ Dictionary row = new Dictionary();
+
+ foreach (var column in columns)
+ {
+ row[column.ColumnName] = reader[column.ColumnName];
+ }
+
+ data.Add(row);
+ }
+ }
+
+ await reader.CloseAsync();
+ var jsonResult = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
+ return jsonResult;
+ }
+
+ private static async Task FetchApiData(object? details, string? filter,
+ IHttpClientFactory httpClientFactory)
+ {
+ var apiDetails = JsonSerializer.Deserialize(details.ToString(), new JsonSerializerOptions()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ });
+ var httpClient = httpClientFactory.CreateClient();
+ apiDetails!.Payload = apiDetails.Payload?.Replace("@filter@", filter);
+ apiDetails.Query = apiDetails.Query?.Replace("@filter@", filter);
+ apiDetails.Url = apiDetails.Url.Replace("@filter@", filter);
+ var result = await httpClient.SendAsync(
+ new HttpRequestMessage(HttpMethod.Parse(apiDetails?.Method), apiDetails?.Url + apiDetails?.Query)
+ {
+ Content = apiDetails?.Payload != null
+ ? new StringContent(JsonSerializer.Serialize(apiDetails.Payload), Encoding.UTF8, "application/json")
+ : null
+ });
+
+ return await result.Content.ReadAsStringAsync();
+ }
+
+ public static async Task