From 6a179c1b5130e872b70f632256408fd6299f40c8 Mon Sep 17 00:00:00 2001 From: TamilRamGanesan-SF5080 Date: Sat, 27 Sep 2025 00:07:37 +0530 Subject: [PATCH 1/3] 982586: added AI integration and bot integration with Chat UI --- .../ai-integrations/gemini-integration.md | 86 ++++++ .../ai-integrations/openai-integration.md | 90 ++++++ .../integration-with-bot-framework.md | 245 ++++++++++++++++ .../integration-with-dialogflow.md | 219 ++++++++++++++ .../ai-integrations/gemini-integration.md | 86 ++++++ .../ai-integrations/openai-integration.md | 89 ++++++ .../integration-with-bot-framework.md | 275 ++++++++++++++++++ .../integration-with-dialogflow.md | 216 ++++++++++++++ .../chat-ui/images/dialogflow.png | Bin 0 -> 33992 bytes ej2-asp-core-mvc/chat-ui/images/gemini.png | Bin 0 -> 27343 bytes ej2-asp-core-mvc/chat-ui/images/openai.png | Bin 0 -> 20433 bytes .../ai-integrations/gemini-ai/geminicore.cs | 2 +- .../ai-integrations/gemini-ai/razor | 2 +- .../ai-integrations/gemini-ai/tagHelper | 2 +- .../Asp.net-MVC/gemini/gemini.cs | 62 ++++ .../ai-integrations/Asp.net-MVC/gemini/razor | 87 ++++++ .../Asp.net-MVC/openai/openai.cs | 75 +++++ .../ai-integrations/Asp.net-MVC/openai/razor | 87 ++++++ .../Asp.net-core/gemini/gemini.cs | 62 ++++ .../Asp.net-core/gemini/tagHelper | 102 +++++++ .../Asp.net-core/openai/openai.cs | 73 +++++ .../Asp.net-core/openai/tagHelper | 99 +++++++ ej2-asp-core-toc.html | 20 ++ ej2-asp-mvc-toc.html | 20 ++ 24 files changed, 1996 insertions(+), 3 deletions(-) create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/gemini-integration.md create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/openai-integration.md create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-bot-framework.md create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-dialogflow.md create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/gemini-integration.md create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/openai-integration.md create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-bot-framework.md create mode 100644 ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-dialogflow.md create mode 100644 ej2-asp-core-mvc/chat-ui/images/dialogflow.png create mode 100644 ej2-asp-core-mvc/chat-ui/images/gemini.png create mode 100644 ej2-asp-core-mvc/chat-ui/images/openai.png create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/gemini.cs create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/razor create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/openai.cs create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/razor create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/gemini.cs create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/openai.cs create mode 100644 ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/gemini-integration.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/gemini-integration.md new file mode 100644 index 0000000000..6ad8c188d9 --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/gemini-integration.md @@ -0,0 +1,86 @@ +--- +layout: post +title: Gemini AI in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Gemini AI in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Gemini AI +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Integration of Gemini AI With Chat UI component + +The Syncfusion Chat UI supports integration with [Gemini](https://ai.google.dev/gemini-api/docs), enabling advanced conversational AI features in your MVC applications. + +## Getting Started With the Chat UI Component + +Before integrating Gemini AI, ensure that the Syncfusion Chat UI control is correctly rendered in your MVC application: + +[ MVC Getting Started Guide](../getting-started) + +## Prerequisites + +* Google account to generate API key on accessing `Gemini AI` + +* Syncfusion Chat UI for MVC `Syncfusion.EJ2.MVC5` Install ASP.NET MVC package in the application. + +## Install Dependencies + +Install the Syncfusion ASP.NET MVC package in the application using the Package Manager Console. + +```bash + +NuGet\Install-Package Syncfusion.EJ2.MVC5 + +``` + +Install the Open AI package in the application using the Package Manager Console. + +```bash + +NuGet\Install-Package Mscc.GenerativeAI + +``` + +## Generate API Key + +1. Go to [Google AI Studio](https://aistudio.google.com/app/apikey) and sign in with your Google account. If you don’t have one, create a new account. + +2. Once logged in, click on `Get API Key` from the left-hand menu or the top-right corner of the dashboard. + +3. Click the `Create API Key` button. You’ll be prompted to either select an existing Google Cloud project or create a new one. Choose the appropriate option and proceed. + +4. After selecting or creating a project, your API key will be generated and displayed. Copy the key and store it securely, as it will only be shown once. + +> `Security Note`: Never commit the API key to version control. Use environment variables or a secret manager for production. + +## Integration Gemini AI with Chat UI + +You can add the below respective files in your application: + +* Add your generated `API Key` at the line + +```bash + +const geminiApiKey = 'Place your API key here'; + +``` + +{% tabs %} +{% highlight razor tabtitle="CSHTML" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/razor %} +{% endhighlight %} +{% highlight c# tabtitle="gemini.cs" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/gemini.cs %} +{% endhighlight %} +{% endtabs %} + +![gemini](../../images/gemini.png) + +## Run and Test + +Run the application in the browser using the following command. + +Build and run the app (Ctrl + F5). + +Open `https://localhost:44321` to interact with your Gemini AI for dynamic response. \ No newline at end of file diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/openai-integration.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/openai-integration.md new file mode 100644 index 0000000000..7b03076dde --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/ai-integrations/openai-integration.md @@ -0,0 +1,90 @@ +--- +layout: post +title: Azure Open AI in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Integration of Azure Open AI in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Azure Open AI +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Integration of Azure Open AI With Chat UI component + +The Syncfusion AI AssistView supports integration with [Azure Open AI](https://microsoft.github.io/PartnerResources/skilling/ai-ml-academy/resources/openai), enabling advanced conversational AI features in your MVC applications. + +## Getting Started With the Chat UI Component + +Before integrating Azure Open AI, ensure that the Syncfusion Chat UI control is correctly rendered in your MVC application: +[ MVC Getting Started Guide](../getting-started) + +## Prerequisites + +* An Azure account with access to `Azure Open AI` services and a generated API key. + +* Syncfusion Chat UI for MVC `Syncfusion.EJ2.MVC5` Install ASP.NET MVC package in the application. + +## Install Dependencies + +Install the Syncfusion ASP.NET MVC package in the application using the Package Manager Console. + +```bash + +NuGet\Install-Package Syncfusion.EJ2.MVC5 + +``` + +Install the Open AI and Azure Open AI package in the application using Package Manager Console. + +```bash + +NuGet\Install-Package OpenAI +NuGet\Install-Package Azure.AI.OpenAI +NuGet\Install-Package Azure.Core + +``` + + +## Generate API Key + +1. Log in to the [Azure Portal](https://portal.azure.com/#home) and navigate to your Azure Open AI resource. + +2. Under Resource Management, select Keys and Endpoint to retrieve your API key and endpoint URL. + +3. Copy the API key, endpoint, and deployment name (e.g., gpt-4o-mini). Ensure the API version (e.g., 2024-07-01-preview) matches your resource configuration. + +4. Store these values securely, as they will be used in your application. + +> `Security Note`: Never expose your API key in client-side code for production applications. Use a server-side proxy or environment variables to manage sensitive information securely + +## Integration Azure Open AI with Chat UI + +You can add the below respective files in your application: + +* Update the following configuration values with your Azure Open AI details: + +```bash + +string endpoint = "Your_Azure_OpenAI_Endpoint"; +string apiKey = "Your_Azure_OpenAI_API_Key"; +string deploymentName = "Your_Deployment_Name"; + +``` + +{% tabs %} +{% highlight razor tabtitle="CSHTML" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/razor %} +{% endhighlight %} +{% highlight c# tabtitle="openai.cs" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/openai.cs %} +{% endhighlight %} +{% endtabs %} + +![Azure Open AI](../../images/openai.png) + +## Run and Test + +Run the application in the browser using the following command. + +Build and run the app (Ctrl + F5). + +Open `https://localhost:44321` to interact with your Azure Open AI for dynamic response. \ No newline at end of file diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-bot-framework.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-bot-framework.md new file mode 100644 index 0000000000..88006b3400 --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-bot-framework.md @@ -0,0 +1,245 @@ +--- +layout: post +title: Microsoft Bot in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Integration of Microsoft Bot in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Integration of Microsoft Bot +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Microsoft Bot Framework With ASP.NET MVC Chat UI component + +The Syncfusion ASP.NET MVC Chat UI supports integration with a Microsoft Bot Framework bot hosted on Azure, enabling a custom chat interface for seamless user interaction. The process involves setting up a secure backend token server, configuring the bot in Azure, and integrating the Syncfusion Chat UI in an ASP.NET MVC application. + +## Getting Started With the Chat UI Component + +Before integrating Microsoft Bot Framework, ensure that the Syncfusion Chat UI component is correctly rendered in your ASP.NET MVC app: + +[ASP.NET MVC Getting Started Guide](../getting-started) + +## Prerequisites + +* `Microsoft Azure Account`: Required to create and host the bot. +* `Visual Studio`: With ASP.NET MVC development tools. +* `Syncfusion EJ2 ASP.NET MVC`: Install Syncfusion.EJ2.MVC5 in your project. +* `Deployed Azure Bot`: A bot should be created and published using the Bot Framework, accessible via an Azure App Service. Refer to [Microsoft's Bot Creation Guide](https://learn.microsoft.com/en-us/azure/bot-service/). + +## Install Dependencies + +* Install backend dependencies for bot communication using NuGet: + +```bash + +Install-Package Microsoft.Bot.Connector.DirectLine +Install-Package Newtonsoft.Json + +``` + +* Install the Syncfusion EJ2 ASP.NET MVC package: + +```bash + +Install-Package Syncfusion.EJ2.MVC5 + +``` + +## Configure the Azure Bot + +1. In the [Azure Portal](https://portal.azure.com/#home), navigate to your bot resource. + +2. Enable the Direct Line channel: + * Go to `Channels` > `Direct Line` > `Default-Site`. + * Copy one of the displayed secret keys. + +3. Verify the Messaging endpoint in the Configuration section (e.g., https://your-bot-service.azurewebsites.net/api/messages). + +> `Security Note`: Never expose the Direct Line secret key in frontend code. Use a backend token server to handle it securely. + +## Set Up Token Server + +Create a Web API controller in your ASP.NET MVC project to handle Direct Line token generation. Add `Controllers/TokenController.cs`: + +{% tabs %} +{% highlight cs tabtitle="TokenController.cs" %} + +using System; +using System.Configuration; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web.Http; +using Newtonsoft.Json; + +namespace YourNamespace.Controllers +{ + public class TokenController : ApiController + { + private static readonly HttpClient _httpClient = new HttpClient(); + [HttpPost] + [Route("api/token/directline/token")] + public async Task GenerateToken() + { + var directLineSecret = ConfigurationManager.AppSettings["DirectLineSecret"]; + if (string.IsNullOrEmpty(directLineSecret)) + { + return BadRequest("Direct Line secret is not configured."); + } + try + { + var request = new HttpRequestMessage(HttpMethod.Post, "https://directline.botframework.com/v3/directline/tokens/generate"); + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", directLineSecret); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync(); + dynamic tokenResponse = JsonConvert.DeserializeObject(responseContent); + return Ok(new { token = tokenResponse.token }); + } + catch (HttpRequestException ex) + { + return InternalServerError(new Exception("Failed to generate Direct Line token.", ex)); + } + } + } +} + +{% endhighlight %} +{% endtabs %} + +Add the Direct Line secret to `Web.config`: + +{% tabs %} +{% highlight js tabtitle=".env" %} + + + + +{% endhighlight %} +{% endtabs %} + +>`Security Note`: Store the Direct Line secret in a secure configuration, such as Azure Key Vault, for production environments. + +## Integrate ChatUI in ASP.NET MVC + +Use the Chat UI `messageSend` event to handle message exchanges. This event is triggered before a message is sent, allowing you to forward it to the bot via the Direct Line API. Use the `addMessage` method to programmatically add the bot's reply to the Chat UI. + +Create `Views/Home/Index.cshtml` to integrate the Syncfusion Chat UI with the Direct Line API: + +{% tabs %} +{% highlight Html tabtitle="Index.cshtml" %} + +@using Syncfusion.EJ2.InteractiveChat + +@{ + var currentUserModel = new ChatUIUser { Id = "user1", User = "You" }; + var botUserModel = new ChatUIUser { Id = "bot", User = "Bot" }; +} + +
+ @Html.EJS().ChatUI("chatUI").User(currentUserModel).MessageSend("onMessageSend").Created("onCreated").Render() +
+ + + + + +{% endhighlight %} +{% endtabs %} + +>Ensure Syncfusion scripts and styles are included in `_Layout.cshtml` as per the getting started guide. Also, include the Bot Framework Web Chat script for Direct Line functionality. Register the Syncfusion script manager in `_Layout.cshtml`. + +## Configure Web.config for CORS (if needed) + +To enable CORS for API requests, add to `Web.config` under ``: + +{% tabs %} +{% highlight js tabtitle="Web.config.js" %} + + + + + + + + + +{% endhighlight %} +{% endtabs %} + +## Run and Test + +### Start the Application: + +Run the project in Visual Studio or use IIS Express. +Open your app in the browser (e.g., `http://localhost:port`) to interact with your Microsoft Bot Framework chatbot. + +## Troubleshooting + +* `Token Server Error (500)`: Ensure the `DirectLineSecret` in `Web.config` is correct and the token endpoint is accessible. +* `CORS Error`: Verify the CORS configuration in `Web.config` allows requests from your frontend URL. +* `Bot is Not Responding`: + - Test the bot in the Azure Portal using the `Test in Web Chat` feature to ensure it’s running correctly. + - Check the bot’s `Messaging endpoint` in the Configuration section and ensure it is correct and accessible. +* `Connection Fails on Load`: Verify the token controller is running and accessible. Check the browser console for network errors. +* `Token Expiration`: Direct Line tokens are short-lived. The Direct Line client typically handles token refresh, but if issues persist, restart the Direct Line connection. \ No newline at end of file diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-dialogflow.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-dialogflow.md new file mode 100644 index 0000000000..978274bffd --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.MVC/bot-integrations/integration-with-dialogflow.md @@ -0,0 +1,219 @@ +--- +layout: post +title: Integration of Dialogflow in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Integration of Dialogflow in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Integration of Dialogflow +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Integration of Google Dialogflow With ASP.NET MVC Chat UI component + +The Syncfusion Chat UI supports integration with [Google Dialogflow](https://cloud.google.com/dialogflow/docs), enabling advanced conversational AI features in your ASP.NET MVC applications. + +## Getting Started With the ChatUI Component + +Before integrating Dialogflow, ensure that the Syncfusion Chat UI component is correctly rendered in your ASP.NET MVC app: + +[ASP.NET MVC Getting Started Guide](../getting-started) + +## Prerequisites + +* Google account to access `Dialogflow` and `Google Cloud Console`. +* Visual Studio with ASP.NET MVC development tools. +* Syncfusion EJ2 ASP.NET MVC installed in your project. +* Dialogflow Service Account with the `Dialogflow API Client` role and its JSON key file. + +## Install Dependencies + +* Install backend dependencies for Dialogflow and server setup using NuGet: + +```bash + +Install-Package Google.Cloud.Dialogflow.V2 +Install-Package Newtonsoft.Json + +``` +* Install the Syncfusion EJ2 ASP.NET MVC package in your project: + +```bash + +Install-Package Syncfusion.EJ2.MVC5 + +``` + +## Set Up the Dialogflow Agent + +1. In the Dialogflow console, create an [agent](https://cloud.google.com/agent-assist/docs), set a name (e.g., `MyChatBot`), and configure the default language (e.g., English - `en`). + +2. Add intents with training phrases and responses (e.g., greetings, FAQs). Test using the Dialogflow simulator. + +3. In the Google Cloud Console, go to `APIs & Services` > `Credentials`, create a Service Account with the Dialogflow API Client role, and download the JSON key file. + +> `Security Note`: Never commit the JSON key file to version control. Use environment variables or a secret manager (e.g., Google Cloud Secret Manager) for production. + +## Configure ASP.NET MVC Backend + +Create `service-acct.json` with your Dialogflow service account credentials in your project root: + +{% tabs %} +{% highlight js tabtitle="service-acct.json" %} + +{ + "type": "service_account", + "project_id": "your-dialogflow-project-id", + "private_key_id": "abc123xyz...", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEv...", + "client_email": "dialogflow-agent@your-dialogflow-project-id.iam.gserviceaccount.com", + ... +} + +{% endhighlight %} +{% endtabs %} + +Set up a Web API controller in `Controllers/ChatController.cs` to handle Dialogflow requests: + +{% tabs %} +{% highlight cs tabtitle="ChatController.cs" %} + +using Google.Cloud.Dialogflow.V2; +using Google.Apis.Auth.OAuth2; +using System; +using System.Configuration; +using System.Threading.Tasks; +using System.Web.Http; + +namespace YourNamespace.Controllers +{ + public class ChatController : ApiController + { + private readonly SessionsClient _sessionsClient; + private readonly string _projectId; + public ChatController() + { + var credential = GoogleCredential.FromFile("service-acct.json"); + _sessionsClient = SessionsClient.Create(credential.ToChannelCredentials()); + _projectId = ConfigurationManager.AppSettings["DialogflowProjectId"]; // Or hardcode from JSON + } + [HttpPost] + [Route("api/chat/message")] + public async Task SendMessage([FromBody] MessageRequest request) + { + var sessionId = request.SessionId ?? "default-session"; + var session = SessionName.FromProjectSession(_projectId, sessionId); + var queryInput = new QueryInput + { + Text = new TextInput + { + Text = request.Text, + LanguageCode = "en-US" + } + }; + try + { + var response = await _sessionsClient.DetectIntentAsync(new DetectIntentRequest { Session = session.ToString(), QueryInput = queryInput }); + var reply = response.QueryResult.FulfillmentText; + return Ok(new { reply }); + } + catch (Exception ex) + { + return InternalServerError(new Exception("Error connecting to Dialogflow.", ex)); + } + } + } + public class MessageRequest + { + public string Text { get; set; } + public string SessionId { get; set; } + } +} + +{% endhighlight %} +{% endtabs %} + +> Use a unique `sessionId` (e.g., Guid) for each user to maintain conversation context. Add the projectId to Web.config if needed: + +## Integrate ChatUI in ASP.NET MVC + +Use the Chat UI `messageSend` event to exchange messages. This event is triggered before a message is sent, allowing you to forward it to the backend. + +### Forward Message to backend: + +In the `messageSend` event handler, send a POST request to your backend API endpoint (`/api/chat/message`). The backend forwards the message to Dialogflow and returns the response. + +### Displaying Bot response: + +Use the `addMessage` method to programmatically add the bot's reply to the Chat UI. + +Create `Views/Home/Index.cshtml` to integrate the Syncfusion Chat UI with the Dialogflow backend: + +{% tabs %} +{% highlight Html tabtitle="Index.cshtml" %} + +@using Syncfusion.EJ2.InteractiveChat + +@{ + var currentUserModel = new ChatUIUser { Id = "user1", User = "Albert" }; + var botUserModel = new ChatUIUser { Id = "user2", User = "Bot", AvatarUrl = "https://ej2.syncfusion.com/demos/src/chat-ui/images/bot.png" }; +} + +
+ @Html.EJS().ChatUI("chatUI").HeaderText("Bot").HeaderIconCss("e-header-icon").User(currentUserModel).MessageSend("onMessageSend").Render() +
+ + + + +{% endhighlight %} +{% endtabs %} + +> Ensure to include Syncfusion scripts and styles in `_Layout.cshtml` as per the getting started guide. Also, register the Syncfusion script manager in `_Layout.cshtml`. + +## Run and Test + +### Start the Application: + +Run the project in Visual Studio or use IIS Express. + +Open your app in the browser and chat with your Dialogflow-powered bot. + +![ChatUI with Dialogflow](../../images/dialogflow.png) + +## Troubleshooting: + +* `Permission Denied`: Ensure the service account has the `Dialogflow API Client` role in the Google Cloud Console. +* `CORS Error`: If using separate origins, configure CORS in Web.config (e.g., add custom headers under ). +* `No Response`: Test intents in the Dialogflow Console simulator to ensure they are configured correctly. +* `Quota Exceeded`: Check Dialogflow API quotas in the Google Cloud Console. +* `Network Issues`: Confirm the application is running and the frontend is pointing to the correct API URL. +* `Invalid Credentials`: Verify the service account JSON or configuration settings are correctly set up. diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/gemini-integration.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/gemini-integration.md new file mode 100644 index 0000000000..33bed56777 --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/gemini-integration.md @@ -0,0 +1,86 @@ +--- +layout: post +title: Gemini AI in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Integration of Gemini AI in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Gemini AI +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Integration of Gemini AI With Chat UI component + +The Syncfusion Chat UI supports integration with [Gemini](https://ai.google.dev/gemini-api/docs), enabling advanced conversational AI features in your Core applications. + +## Getting Started With the Chat UI Component + +Before integrating Gemini AI, ensure that the Syncfusion Chat UI control is correctly rendered in your Core application: + +[ core Getting Started Guide](../getting-started) + +## Prerequisites + +* Google account to generate API key on accessing `Gemini AI` + +* Syncfusion Chat UI for Core `Syncfusion.EJ2.AspNet.Core` Install ASP.NET Core package in the application. + +## Install Dependencies + +Install the Syncfusion ASP.NET Core package in the application using the Package Manager Console. + +```bash + +NuGet\Install-Package Syncfusion.EJ2.AspNet.Core + +``` + +Install the Open AI package in the application using the Package Manager Console. + +```bash + +NuGet\Install-Package Mscc.GenerativeAI + +``` + +## Generate API Key + +1. Go to [Google AI Studio](https://aistudio.google.com/app/apikey) and sign in with your Google account. If you don’t have one, create a new account. + +2. Once logged in, click on `Get API Key` from the left-hand menu or the top-right corner of the dashboard. + +3. Click the `Create API Key` button. You’ll be prompted to either select an existing Google Cloud project or create a new one. Choose the appropriate option and proceed. + +4. After selecting or creating a project, your API key will be generated and displayed. Copy the key and store it securely, as it will only be shown once. + +> `Security Note`: Never commit the API key to version control. Use environment variables or a secret manager for production. + +## Integration Gemini AI with Chat UI + +You can add the below respective files in your application: + +* Add your generated `API Key` at the line + +```bash + +const geminiApiKey = 'Place your API key here'; + +``` + +{% tabs %} +{% highlight tagHelper tabtitle="CSHTML" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper %} +{% endhighlight %} +{% highlight c# tabtitle="gemini.cs" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/gemini.cs %} +{% endhighlight %} +{% endtabs %} + +![Open AI](../../images/gemini.png) + +## Run and Test + +Run the application in the browser using the following command. + +Build and run the app (Ctrl + F5). + +Open `https://localhost:44321` to interact with your Gemini AI for dynamic response. \ No newline at end of file diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/openai-integration.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/openai-integration.md new file mode 100644 index 0000000000..11ec9b6d5f --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/ai-integrations/openai-integration.md @@ -0,0 +1,89 @@ +--- +layout: post +title: Azure Open AI in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Integration of Azure Open AI in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Azure Open AI +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Integration of Azure Open AI With Chat UI component + +The Syncfusion AI AssistView supports integration with [Azure Open AI](https://microsoft.github.io/PartnerResources/skilling/ai-ml-academy/resources/openai), enabling advanced conversational AI features in your Core applications. + +## Getting Started With the Chat UI Component + +Before integrating Azure Open AI, ensure that the Syncfusion Chat UI control is correctly rendered in your core application: +[ Asp Core Getting Started Guide](../getting-started) + +## Prerequisites + +* An Azure account with access to `Azure Open AI` services and a generated API key. + +* Syncfusion Chat UI for Core `Syncfusion.EJ2.AspNet.Core` Install ASP.NET Core package in the application. + +## Install Dependencies + +Install the Syncfusion ASP.NET Core package in the application using the Package Manager Console. + +```bash + +NuGet\Install-Package Syncfusion.EJ2.AspNet.Core +``` + +Install the Open AI and Azure Open AI package in the application using Package Manager Console. + +```bash + +NuGet\Install-Package OpenAI +NuGet\Install-Package Azure.AI.OpenAI +NuGet\Install-Package Azure.Core + +``` + + +## Configure Azure Open AI + +1. Log in to the [Azure Portal](https://portal.azure.com/#home) and navigate to your Azure Open AI resource. + +2. Under Resource Management, select Keys and Endpoint to retrieve your API key and endpoint URL. + +3. Copy the API key, endpoint, and deployment name (e.g., gpt-4o-mini). Ensure the API version (e.g., 2024-07-01-preview) matches your resource configuration. + +4. Store these values securely, as they will be used in your application. + +> `Security Note`: Never expose your API key in client-side code for production applications. Use a server-side proxy or environment variables to manage sensitive information securely. + +## Integration Azure Open AI with Chat UI + +You can add the below respective files in your application: + +* Update the following configuration values with your Azure Open AI details: + +```bash + +string endpoint = "Your_Azure_OpenAI_Endpoint"; +string apiKey = "Your_Azure_OpenAI_API_Key"; +string deploymentName = "Your_Deployment_Name"; + +``` + +{% tabs %} +{% highlight tagHelper tabtitle="CSHTML" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper %} +{% endhighlight %} +{% highlight c# tabtitle="openai.cs" %} +{% include code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/openai.cs %} +{% endhighlight %} +{% endtabs %} + +![Azure Open AI](../../images/openai.png) + +## Run and Test + +Run the application in the browser using the following command. + +Build and run the app (Ctrl + F5). + +Open `https://localhost:44321` to interact with your Azure Open AI for dynamic response. \ No newline at end of file diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-bot-framework.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-bot-framework.md new file mode 100644 index 0000000000..11910f2113 --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-bot-framework.md @@ -0,0 +1,275 @@ +--- +layout: post +title: Microsoft Bot in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Integration of Microsoft Bot in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Integration of Microsoft Bot +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Microsoft Bot Framework With ASP.NET Core Chat UI component + +The Syncfusion ASP.NET Core Chat UI supports integration with a Microsoft Bot Framework bot hosted on Azure, enabling a custom chat interface for seamless user interaction. The process involves setting up a secure backend token server, configuring the bot in Azure, and integrating the Syncfusion Chat UI in an ASP.NET Core application. + +## Getting Started With the ChatUI Component + +Before integrating Microsoft Bot Framework, ensure that the Syncfusion Chat UI component is correctly rendered in your ASP.NET Core app: +[ASP.NET Core Getting Started Guide](../getting-started) + +## Prerequisites + +* `Microsoft Azure Account`: Required to create and host the bot. +* `.NET SDK`: Version 6.0 or higher for ASP.NET Core. +* `Syncfusion EJ2 ASP.NET Core`: Install Syncfusion.EJ2.AspNet.Core in your project. +* `Deployed Azure Bot`: A bot should be created and published using the Bot Framework, accessible via an Azure App Service. Refer to [Microsoft's Bot Creation Guide](https://learn.microsoft.com/en-us/azure/bot-service/). + +## Install Dependencies +* Install backend dependencies for bot communication using NuGet: + +```bash + +dotnet add package Microsoft.Bot.Connector.DirectLine +dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson + +``` + +* Install the Syncfusion EJ2 ASP.NET Core package: + +```bash + +dotnet add package Syncfusion.EJ2.AspNet.Core + +``` + +## Configure the Azure Bot + +1. In the [Azure Portal](https://portal.azure.com/#home), navigate to your bot resource. + +2. Enable the Direct Line channel: + * Go to `Channels` > `Direct Line` > `Default-Site`. + * Copy one of the displayed secret keys. + +3. Verify the Messaging endpoint in the Configuration section (e.g., https://your-bot-service.azurewebsites.net/api/messages). + +> `Security Note`: Never expose the Direct Line secret key in frontend code. Use a backend token server to handle it securely. + +## Set Up Token Server + +Create a controller in your ASP.NET Core project to handle Direct Line token generation. Add `Controllers/TokenController.cs`: + +{% tabs %} +{% highlight cs tabtitle="TokenController.cs" %} + +using Microsoft.AspNetCore.Mvc; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace YourNamespace.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class TokenController : ControllerBase + { + private readonly IConfiguration _configuration; + private readonly HttpClient _httpClient; + + public TokenController(IConfiguration configuration, IHttpClientFactory httpClientFactory) + { + _configuration = configuration; + _httpClient = httpClientFactory.CreateClient(); + } + + [HttpPost("directline/token")] + public async Task GenerateToken() + { + var directLineSecret = _configuration["DirectLine:Secret"]; + if (string.IsNullOrEmpty(directLineSecret)) + { + return BadRequest(new { error = "Direct Line secret is not configured." }); + } + + try + { + var request = new HttpRequestMessage(HttpMethod.Post, "https://directline.botframework.com/v3/directline/tokens/generate"); + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", directLineSecret); + + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + + var responseContent = await response.Content.ReadAsStringAsync(); + var tokenResponse = JsonConvert.DeserializeObject(responseContent); + return Ok(new { token = tokenResponse.token }); + } + catch (HttpRequestException ex) + { + return StatusCode(500, new { error = "Failed to generate Direct Line token.", details = ex.Message }); + } + } + } +} + +{% endhighlight %} +{% endtabs %} + +Add the Direct Line secret to `appsettings.json`: + +{% tabs %} +{% highlight js tabtitle="appsettings.json" %} + +{ + "DirectLine": { + "Secret": "PASTE_YOUR_DIRECT_LINE_SECRET_HERE" + } +} + +{% endhighlight %} +{% endtabs %} + +> `Security Note`: Store the Direct Line secret in a secure configuration, such as Azure Key Vault, for production environments.| + +## Integrate ChatUI in ASP.NET Core + +Use the Chat UI `messageSend` event to handle message exchanges. This event is triggered before a message is sent, allowing you to forward it to the bot via the Direct Line API. Use the `addMessage` method to programmatically add the bot's reply to the Chat UI. +Create `Views/Home/Index.cshtml` (assuming MVC) to integrate the Syncfusion Chat UI with the Direct Line API: + +{% tabs %} +{% highlight Html tabtitle="Index.cshtml" %} + +@using Syncfusion.EJ2.InteractiveChat + +@{ + var currentUserModel = new ChatUIUser { Id = "user1", User = "You" }; + var botUserModel = new ChatUIUser { Id = "bot", User = "Bot" }; +} + +
+ + + +
+ + + + + +{% endhighlight %} +{% endtabs %} + +> Ensure Syncfusion scripts and styles are included in `_Layout.cshtml` as per the getting started guide. Also, register `` in `_Layout.cshtml`. Include the Bot Framework Web Chat script for Direct Line functionality. + +## Configure Program.cs + +Ensure CORS and HttpClient are configured in `Program.cs`: + +{% tabs %} +{% highlight cs tabtitle="Program.cs" %} + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers().AddNewtonsoftJson(); +builder.Services.AddHttpClient(); +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowAll", builder => + { + builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); + }); +}); + +var app = builder.Build(); + +app.UseCors("AllowAll"); +app.UseRouting(); +app.MapControllers(); + +app.Run(); + +{% endhighlight %} +{% endtabs %} + +## Run and Test + +### Start the Application: + +Navigate to your project folder and run: + +```bash + +dotnet run + +``` + +Open your app in the browser (e.g., `http://localhost:5000`) to interact with your Microsoft Bot Framework chatbot. + +## Troubleshooting + +* `Token Server Error (500)`: Ensure the DirectLine:Secret in appsettings.json is correct and the token endpoint is accessible. +* `CORS Error`: Verify the CORS policy in Program.cs allows requests from your frontend URL. +* `Bot is Not Responding`: + - Test the bot in the Azure Portal using the `Test in Web Chat` feature to ensure it's running correctly. + - Check the bot's `Messaging endpoint` in the Configuration section and ensure it is correct and accessible. +* `Connection Fails on Load`: Verify the token controller is running and accessible. Check the browser console for network errors. +* `Token Expiration`: Direct Line tokens are short-lived. The Direct Line client typically handles token refresh, but if issues persist, restart the Direct Line connection. \ No newline at end of file diff --git a/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-dialogflow.md b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-dialogflow.md new file mode 100644 index 0000000000..15a02f6b7c --- /dev/null +++ b/ej2-asp-core-mvc/chat-ui/EJ2_ASP.NETCORE/bot-integrations/integration-with-dialogflow.md @@ -0,0 +1,216 @@ +--- +layout: post +title: Integration of Dialogflow in ##Platform_Name## Chat UI Control | Syncfusion +description: Checkout and learn about Integration of Dialogflow in Syncfusion ##Platform_Name## Chat UI control of Syncfusion Essential JS 2 and more. +platform: ej2-asp-core-mvc +control: Integration of Dialogflow +publishingplatform: ##Platform_Name## +documentation: ug +--- + +# Integration of Google Dialogflow With ASP.NET Core Chat UI component + +The Syncfusion Chat UI supports integration with [Google Dialogflow](https://cloud.google.com/dialogflow/docs), enabling advanced conversational AI features in your ASP.NET Core applications. + +## Getting Started With the ChatUI Component + +Before integrating Dialogflow, ensure that the Syncfusion Chat UI component is correctly rendered in your ASP.NET Core app: +[ASP.NET Core Getting Started Guide](../getting-started) + +## Prerequisites + +* Google account to access `Dialogflow` and `Google Cloud Console`. +* .NET SDK (version 6.0 or higher) for ASP.NET Core. +* Syncfusion EJ2 ASP.NET Core installed in your project. +* Dialogflow Service Account with the Dialogflow API Client role and its JSON key file. + +## Install Dependencies +* Install backend dependencies for Dialogflow and server setup using NuGet: + +```bash + +dotnet add package Google.Cloud.Dialogflow.V2 +dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson + +``` +* Install the Syncfusion EJ2 ASP.NET Core package in your project: + +```bash + +dotnet add package Syncfusion.EJ2.AspNet.Core + +``` +## Set Up the Dialogflow Agent + +1. In the Dialogflow console, create an [agent](https://cloud.google.com/agent-assist/docs), set a name (e.g., `MyChatBot`), and configure the default language (e.g., English - `en`). + +2. Add intents with training phrases and responses (e.g., greetings, FAQs). Test using the Dialogflow simulator. + +3. In the Google Cloud Console, go to `APIs & Services` > `Credentials`, create a Service Account with the Dialogflow API Client role, and download the JSON key file. + +> `Security Note`: Never commit the JSON key file to version control. Use environment variables or a secret manager (e.g., Google Cloud Secret Manager) for production. + +## Configure Node.js Backend + +Create `service-acct.json` with your Dialogflow service account credentials in your project root (or use User Secrets for development): + +{% tabs %} +{% highlight js tabtitle="service-acct.json" %} + +{ +"type": "service_account", +"project_id": "your-dialogflow-project-id", +"private_key_id": "abc123xyz...", +"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEv...", +"client_email": "dialogflow-agent@your-dialogflow-project-id.iam.gserviceaccount.com", +... +} +{% endhighlight %} +{% endtabs %} + +Set up an API controller in `Controllers/ChatController.cs` to handle Dialogflow requests: + +{% tabs %} +{% highlight cs tabtitle="ChatController.cs" %} + +using Google.Cloud.Dialogflow.V2; +using Google.Apis.Auth.OAuth2; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; + +namespace YourNamespace.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class ChatController : ControllerBase + { + private readonly SessionsClient _sessionsClient; + private readonly string _projectId; + public ChatController(IConfiguration configuration) + { + var credential = GoogleCredential.FromFile("service-acct.json"); + _sessionsClient = SessionsClient.Create(credential.ToChannelCredentials()); + _projectId = "your-dialogflow-project-id"; // Or from configuration + } + [HttpPost("message")] + public async Task SendMessage([FromBody] MessageRequest request) + { + var sessionId = request.SessionId ?? "default-session"; + var session = SessionName.FromProjectSession(_projectId, sessionId); + var queryInput = new QueryInput + { + Text = new TextInput + { + Text = request.Text, + LanguageCode = "en-US" + } + }; + try + { + var response = await _sessionsClient.DetectIntentAsync(new DetectIntentRequest { Session = session.ToString(), QueryInput = queryInput }); + var reply = response.QueryResult.FulfillmentText; + return Ok(new { reply }); + } + catch (Exception ex) + { + return StatusCode(500, new { reply = "Error connecting to Dialogflow." }); + } + } + } + public class MessageRequest + { + public string Text { get; set; } + public string SessionId { get; set; } + } +} + +{% endhighlight %} +{% endtabs %} + +> Use a unique `sessionId` (e.g., Guid) for each user to maintain conversation context. Add the projectId to appsettings.json if needed. + +## Integrate ChatUI in ASP.NET Core + +Use the Chat UI `messageSend` event to exchange messages. This event is triggered before a message is sent, allowing you to forward it to the backend. + +### Forward Message to backend: + +In the messageSend event handler, send a POST request to your backend API endpoint (`/api/chat/message`). The backend forwards the message to Dialogflow and returns the response. + +### Displaying Bot response: + +Use the `addMessage` method to programmatically add the bot's reply to the Chat UI. + +Create `Views/Home/Index.cshtml` (assuming MVC) to integrate the Syncfusion Chat UI with the Dialogflow backend: + +{% tabs %} +{% highlight Html tabtitle="Index.cshtml" %} + +@using Syncfusion.EJ2.InteractiveChat +@{ + var currentUserModel = new ChatUIUser { Id = "user1", User = "Albert" }; + var botUserModel = new ChatUIUser { Id = "user2", User = "Bot", AvatarUrl = "https://ej2.syncfusion.com/demos/src/chat-ui/images/bot.png" }; +} +
+ +
+ + + +{% endhighlight %} +{% endtabs %} + +> Ensure to include Syncfusion scripts and styles in _Layout.cshtml as per the getting started guide. Also, register ` in _Layout.cshtml.` + +## Run and Test + +### Start the Application: + +Navigate to your project folder and run: + +```bash + +dotnet run + +``` + + Open your app and chat with your Dialogflow-powered bot. + +![ChatUI with Dialogflow](../../images/dialogflow.png) + +## Troubleshooting: + +* `Permission Denied`: Ensure the service account has the `Dialogflow API Client` role in the Google Cloud Console. +* `CORS Error`: Configure CORS in Program.cs if needed (e.g., app.UseCors()). +* `No Response`: Test intents in the Dialogflow Console simulator to ensure they are configured correctly. +* `Quota Exceeded`: Check Dialogflow API quotas in the Google Cloud Console. +* `Network Issues`: Confirm the application is running and the API URL is correct. +* `Invalid Credentials`: Verify the service account JSON or configuration settings are correctly set up. \ No newline at end of file diff --git a/ej2-asp-core-mvc/chat-ui/images/dialogflow.png b/ej2-asp-core-mvc/chat-ui/images/dialogflow.png new file mode 100644 index 0000000000000000000000000000000000000000..8b96f608e9f47c5a16749fed251df4d9a8a8b621 GIT binary patch literal 33992 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 literal 0 HcmV?d00001 diff --git a/ej2-asp-core-mvc/chat-ui/images/gemini.png b/ej2-asp-core-mvc/chat-ui/images/gemini.png new file mode 100644 index 0000000000000000000000000000000000000000..d548f6220875d24dbe0352330c419b03145ee828 GIT binary patch literal 27343 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%0Ag-E`yl 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 literal 0 HcmV?d00001 diff --git a/ej2-asp-core-mvc/chat-ui/images/openai.png b/ej2-asp-core-mvc/chat-ui/images/openai.png new file mode 100644 index 0000000000000000000000000000000000000000..3f56e695a9b30e379b691ed8748c93493e6921e4 GIT binary patch literal 20433 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 X6(ubSNlLx4eE_)o`Li$F!ZQ91q$40R literal 0 HcmV?d00001 diff --git a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminicore.cs b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminicore.cs index f2e0ab297c..9d34a474eb 100644 --- a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminicore.cs +++ b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminicore.cs @@ -39,7 +39,7 @@ public async Task OnPostGetAIResponse([FromBody] PromptRequest re string apiKey = ""; // Replace with your key var googleAI = new GoogleAI(apiKey: apiKey); - var model = googleAI.GenerativeModel(model: Model.Gemini15Flash); + var model = googleAI.GenerativeModel(model: Model.Gemini15Flash); //Replace Your Model Name Here var response = await model.GenerateContent(request.Prompt); diff --git a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/razor b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/razor index ebec3a9e57..ecd93a6004 100644 --- a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/razor +++ b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/razor @@ -69,7 +69,7 @@ 'Content-Type': 'application/json', 'RequestVerificationToken': token }, - body: JSON.stringify({ prompt: args.prompt || 'Hi' }) + body: JSON.stringify({ prompt: args.prompt}) }) .then(response => { if (!response.ok) { diff --git a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/tagHelper b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/tagHelper index 0e59d545ac..69282bbe0c 100644 --- a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/tagHelper +++ b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/tagHelper @@ -65,7 +65,7 @@ headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ prompt: args.prompt || 'Hi' }) + body: JSON.stringify({ prompt: args.prompt}) }) .then(response => { if (!response.ok) { diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/gemini.cs b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/gemini.cs new file mode 100644 index 0000000000..08e0eb9fc7 --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/gemini.cs @@ -0,0 +1,62 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.AI; +using Mscc.GenerativeAI; +using Syncfusion.EJ2.InteractiveChat; +using Newtonsoft.Json; + +private readonly ILogger _logger; +public List HeaderToolbar { get; set; } = new List(); +public HomeController(ILogger logger) +{ + _logger = logger; +} +public IActionResult Index() +{ + HeaderToolbar.Add(new ToolbarItemModel { align = "Right", iconCss = "e-icons e-refresh", tooltip = "Refresh" }); + ViewBag.HeaderToolbar = HeaderToolbar; + var currentUser = new ChatUIUser { Id = "user1", User = "You" }; + var aiUser = new ChatUIUser { Id = "ai", User = "Gemini" }; + ViewBag.CurrentUser = currentUser; + ViewBag.AIUser = aiUser; + + return View(); +} + +[HttpPost] +public async Task GetAIResponse([FromBody] PromptRequest request) +{ + try + { + _logger.LogInformation("Received request with prompt: {Prompt}", request?.Prompt); + if (string.IsNullOrEmpty(request?.Prompt)) + { + _logger.LogWarning("Prompt is null or empty."); + return BadRequest("Prompt cannot be empty."); + } + string apiKey = ""; // Replace with your key + var googleAI = new GoogleAI(apiKey: apiKey); + var model = googleAI.GenerativeModel(model: Model.Gemini15Flash); // Replace Your Model Name Here + + var responseText = await model.GenerateContent(request.Prompt); + if (string.IsNullOrEmpty(responseText?.Text)) + { + _logger.LogError("Gemini AI API returned no text."); + return BadRequest("No response from Gemini AI."); + } + _logger.LogInformation("Gemini AI response received: {Response}", responseText.Text); + return Json(responseText.Text); + } + catch (Exception ex) + { + _logger.LogError("Exception in Gemini AI call: {Message}", ex.Message); + return BadRequest($"Error generating response: {ex.Message}"); + } +} +public class ToolbarItemModel +{ + public string align { get; set; } + public string iconCss { get; set; } + public string tooltip { get; set; } +} diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/razor b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/razor new file mode 100644 index 0000000000..08eaac9cd2 --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/gemini/razor @@ -0,0 +1,87 @@ +@using Syncfusion.EJ2.InteractiveChat +@using Newtonsoft.Json +@using Syncfusion.EJ2 +@{ + ViewData["Title"] = "AI Chat with Gemini AI"; +} + +

+ @Html.EJS().ChatUI("chatUI").User(ViewBag.CurrentUser).HeaderText("Chat with Gemini AI").HeaderToolbar(new ChatUIToolbarSettings() { Items = ViewBag.HeaderToolbar, ItemClicked = "itemClicked" }).HeaderIconCss("e-icons e-ai-chat").EmptyChatTemplate("#emptyChatTemplate").MessageSend("onMessageSend").Created("onCreated").Render() +
+ + + + + + \ No newline at end of file diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/openai.cs b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/openai.cs new file mode 100644 index 0000000000..835a39f831 --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/openai.cs @@ -0,0 +1,75 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.AI; +using OpenAI; +using OpenAI.Chat; +using Syncfusion.EJ2.InteractiveChat; +using Newtonsoft.Json; +using Azure.AI.OpenAI; +using Azure; +private readonly ILogger _logger; +public List HeaderToolbar { get; set; } = new List(); +public HomeController(ILogger logger) +{ + _logger = logger; +} + +public IActionResult Index() +{ + HeaderToolbar.Add(new ToolbarItemModel { align = "Right", iconCss = "e-icons e-refresh", tooltip = "Refresh" }); + ViewBag.HeaderToolbar = HeaderToolbar; + var currentUser = new ChatUIUser { Id = "user1", User = "You" }; + var aiUser = new ChatUIUser { Id = "ai", User = "Azure Open AI" }; + ViewBag.CurrentUser = currentUser; + ViewBag.AIUser = aiUser; + + return View(); +} + +[HttpPost] +public async Task GetAIResponse([FromBody] PromptRequest request) +{ + try + { + _logger.LogInformation("Received request with prompt: {Prompt}", request?.Prompt); + if (string.IsNullOrEmpty(request?.Prompt)) + { + _logger.LogWarning("Prompt is null or empty."); + return BadRequest("Prompt cannot be empty."); + } + string endpoint = "Your_Azure_OpenAI_Endpoint"; // Replace with your Azure OpenAI endpoint + string apiKey = "YOUR_AZURE_OPENAI_API_KEY"; // Replace with your Azure OpenAI API key + string deploymentName = "YOUR_DEPLOYMENT_NAME"; // Replace with your Azure OpenAI deployment name (e.g., gpt-4o-mini) + + var credential = new AzureKeyCredential(apiKey); + var client = new AzureOpenAIClient(new Uri(endpoint), credential); + var chatClient = client.GetChatClient(deploymentName); + + var chatCompletionOptions = new ChatCompletionOptions(); + var completion = await chatClient.CompleteChatAsync( + new[] { new UserChatMessage(request.Prompt) }, + chatCompletionOptions + ); + + string responseText = completion.Value.Content[0].Text; + if (string.IsNullOrEmpty(responseText)) + { + _logger.LogError("Azure OpenAI API returned no text."); + return BadRequest("No response from Azure Open AI."); + } + _logger.LogInformation("Azure OpenAI response received: {Response}", responseText); + return Json(responseText); + } + catch (Exception ex) + { + _logger.LogError("Exception in Azure Open AI call: {Message}", ex.Message); + return BadRequest($"Error generating response: {ex.Message}"); + } +} +public class ToolbarItemModel +{ + public string align { get; set; } + public string iconCss { get; set; } + public string tooltip { get; set; } +} diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/razor b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/razor new file mode 100644 index 0000000000..ec39532a80 --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-MVC/openai/razor @@ -0,0 +1,87 @@ +@using Syncfusion.EJ2.InteractiveChat +@using Newtonsoft.Json +@using Syncfusion.EJ2 +@{ + ViewData["Title"] = "AI Chat with Azure Open AI"; +} + +
+ @Html.EJS().ChatUI("chatUI").User(ViewBag.CurrentUser).HeaderText("Chat with Azure Open AI").HeaderToolbar(new ChatUIToolbarSettings() { Items = ViewBag.HeaderToolbar, ItemClicked = "itemClicked" }).HeaderIconCss("e-icons e-ai-chat").EmptyChatTemplate("#emptyChatTemplate").MessageSend("onMessageSend").Created("onCreated").Render() +
+ + + + + + \ No newline at end of file diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/gemini.cs b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/gemini.cs new file mode 100644 index 0000000000..b0ef1d5362 --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/gemini.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Mscc.GenerativeAI; // Correct namespace for Mscc.GenerativeAI package +using System.Threading.Tasks; + +private readonly ILogger _logger; +public List HeaderToolbar { get; set; } = new List(); + +public IndexModel(ILogger logger) +{ + _logger = logger; +} + +public void OnGet() +{ + HeaderToolbar.Add(new ToolbarItemModel { align = "Right", type = "Button", iconCss = "e-icons e-refresh" }); + HeaderToolbar = HeaderToolbar; +} + +public async Task OnPostGetAIResponse([FromBody] PromptRequest request) +{ + try + { + _logger.LogInformation("Received request with prompt: {Prompt}", request?.Prompt); + + if (string.IsNullOrEmpty(request?.Prompt)) + { + _logger.LogWarning("Prompt is null or empty."); + return BadRequest("Prompt cannot be empty."); + } + + string apiKey = ""; // Replace with your key + var googleAI = new GoogleAI(apiKey: apiKey); + var model = googleAI.GenerativeModel(model: Model.Gemini15Flash); // Replace Your Model Name Here + + var response = await model.GenerateContent(request.Prompt); + + if (string.IsNullOrEmpty(response?.Text)) + { + _logger.LogError("Gemini API returned no text."); + return BadRequest("No response from Gemini."); + } + + _logger.LogInformation("Gemini response received: {Response}", response.Text); + return new JsonResult(response.Text); + } + catch (Exception ex) + { + _logger.LogError("Exception in Gemini call: {Message}", ex.Message); + return BadRequest($"Error generating response: {ex.Message}"); + } +} +public class PromptRequest +{ + public string Prompt { get; set; } +} +public class ToolbarItemModel +{ + public string align { get; set; } + public string iconCss { get; set; } + public string type { get; set; } +} diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper new file mode 100644 index 0000000000..dbbf7ccd2d --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper @@ -0,0 +1,102 @@ +@page +@model IndexModel +@{ + ViewData["Title"] = "Chat UI with Gemini"; +} + + +
+ + + + +
+ +@section Scripts { + +} \ No newline at end of file diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/openai.cs b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/openai.cs new file mode 100644 index 0000000000..afb07f147b --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/openai.cs @@ -0,0 +1,73 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using System.Threading.Tasks; +using OpenAI.Chat; +using OpenAI; +using Azure; +using Azure.AI.OpenAI; +private readonly ILogger _logger; +public List HeaderToolbar { get; set; } = new List(); + +public IndexModel(ILogger logger) +{ + _logger = logger; +} + +public void OnGet() +{ + HeaderToolbar.Add(new ToolbarItemModel { align = "Right", type = "Button", iconCss = "e-icons e-refresh" }); + HeaderToolbar = HeaderToolbar; +} + +public async Task OnPostGetAIResponse([FromBody] PromptRequest request) +{ + try + { + _logger.LogInformation("Received request with prompt: {Prompt}", request?.Prompt); + + if (string.IsNullOrEmpty(request?.Prompt)) + { + _logger.LogWarning("Prompt is null or empty."); + return BadRequest("Prompt cannot be empty."); + } + + string endpoint = "Your_Azure_OpenAI_Endpoint"; // Replace with your Azure OpenAI endpoint + string apiKey = "YOUR_AZURE_OPENAI_API_KEY"; // Replace with your Azure OpenAI API key + string deploymentName = "YOUR_DEPLOYMENT_NAME"; // Replace with your Azure OpenAI deployment name (e.g., gpt-4o-mini) + + var credential = new AzureKeyCredential(apiKey); + var client = new AzureOpenAIClient(new Uri(endpoint), credential); + var chatClient = client.GetChatClient(deploymentName); + + var chatCompletionOptions = new ChatCompletionOptions(); + var completion = await chatClient.CompleteChatAsync( + new[] { new UserChatMessage(request.Prompt) }, + chatCompletionOptions + ); + string responseText = completion.Value.Content[0].Text; + + if (string.IsNullOrEmpty(responseText)) + { + _logger.LogError("Azure Open AI API returned no text."); + return BadRequest("No response from Azure Open AI."); + } + + _logger.LogInformation("Azure Open AI response received: {Response}", responseText); + return new JsonResult(responseText); + } + catch (Exception ex) + { + _logger.LogError("Exception in Azure Open AI call: {Message}", ex.Message); + return BadRequest($"Error generating response: {ex.Message}"); + } +} +public class PromptRequest +{ + public string Prompt { get; set; } +} +public class ToolbarItemModel +{ + public string align { get; set; } + public string iconCss { get; set; } + public string type { get; set; } +} diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper new file mode 100644 index 0000000000..336c48a000 --- /dev/null +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper @@ -0,0 +1,99 @@ +@page +@model IndexModel +@{ + ViewData["Title"] = "Chat UI with Azure Open AI"; +} + + +
+ + + + +
+ +@section Scripts { + +} \ No newline at end of file diff --git a/ej2-asp-core-toc.html b/ej2-asp-core-toc.html index 1d618988f2..6ff7e0027c 100644 --- a/ej2-asp-core-toc.html +++ b/ej2-asp-core-toc.html @@ -570,6 +570,26 @@
  • Getting Started
  • Messages
  • +
  • Chat Bot Integrations + +
  • +
  • AI Integrations + +
  • Time break
  • Timestamp
  • Typing indicator
  • diff --git a/ej2-asp-mvc-toc.html b/ej2-asp-mvc-toc.html index 5670a9008a..e44913b4ae 100644 --- a/ej2-asp-mvc-toc.html +++ b/ej2-asp-mvc-toc.html @@ -573,6 +573,26 @@
    • Getting Started
    • Messages
    • +
    • Chat Bot Integrations + +
    • +
    • AI Integrations + +
    • Time break
    • Timestamp
    • Typing indicator
    • From 679c004a211439b9547f5169594d6cc91efebd57 Mon Sep 17 00:00:00 2001 From: TamilRamGanesan-SF5080 Date: Sat, 27 Sep 2025 12:29:33 +0530 Subject: [PATCH 2/3] 982586: removed console lines --- .../chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper | 1 - .../chat-ui/ai-integrations/Asp.net-core/openai/tagHelper | 1 - 2 files changed, 2 deletions(-) diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper index dbbf7ccd2d..ba3e7f92f4 100644 --- a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/gemini/tagHelper @@ -88,7 +88,6 @@ author: aiModel }); } catch (error) { - console.error('Error fetching Gemini response:', error); chatUI.addMessage({ text: 'Error generating response. Please try again.', author: aiModel diff --git a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper index 336c48a000..2f1a689619 100644 --- a/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper +++ b/ej2-asp-core-mvc/code-snippet/chat-ui/ai-integrations/Asp.net-core/openai/tagHelper @@ -85,7 +85,6 @@ author: aiModel }); } catch (error) { - console.error('Error fetching Azure Open AI response:', error); chatUI.addMessage({ text: 'Error generating response. Please try again.', author: aiModel From 9e3cb025925009589101690106ff15f1731b0795 Mon Sep 17 00:00:00 2001 From: TamilRamGanesan-SF5080 Date: Sat, 27 Sep 2025 12:31:09 +0530 Subject: [PATCH 3/3] added comments for variables --- .../ai-assistview/ai-integrations/gemini-ai/geminimvc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminimvc.cs b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminimvc.cs index c94229f8d3..6c0054c9c0 100644 --- a/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminimvc.cs +++ b/ej2-asp-core-mvc/code-snippet/ai-assistview/ai-integrations/gemini-ai/geminimvc.cs @@ -42,7 +42,7 @@ public async Task GetAIResponse([FromBody] PromptRequest request) string apiKey = ""; // Replace with your key var googleAI = new GoogleAI(apiKey: apiKey); - var model = googleAI.GenerativeModel(model: Model.Gemini15Flash); + var model = googleAI.GenerativeModel(model: Model.Gemini15Flash); //Replace Your Model Name Here var responseText = await model.GenerateContent(request.Prompt);