diff --git a/AntSK/AntSK.xml b/AntSK/AntSK.xml
index 4a5124b3..3554945d 100644
--- a/AntSK/AntSK.xml
+++ b/AntSK/AntSK.xml
@@ -57,6 +57,31 @@
+
+
+ 发送知识库问答
+
+
+
+
+
+
+
+
+ 发送普通对话
+
+
+
+
+
+
+
+
+ 历史会话的会话总结
+
+
+
+
根据文档ID获取文档
diff --git a/AntSK/Layouts/OpenLayout.razor b/AntSK/Layouts/OpenLayout.razor
new file mode 100644
index 00000000..0e303ca2
--- /dev/null
+++ b/AntSK/Layouts/OpenLayout.razor
@@ -0,0 +1,8 @@
+@namespace AntSK
+@inherits LayoutComponentBase
+
+@Body
+
+@code {
+
+}
diff --git a/AntSK/Pages/AppPage/AppOpen.razor b/AntSK/Pages/AppPage/AppOpen.razor
index 5af8eec4..3f051452 100644
--- a/AntSK/Pages/AppPage/AppOpen.razor
+++ b/AntSK/Pages/AppPage/AppOpen.razor
@@ -1,21 +1,19 @@
@namespace AntSK.Pages.AppPage
@page "/App/Open/{AppID}"
-
-
+
- Api访问
+ Api接入
@@ -31,11 +29,27 @@
- 免登录窗口
+ 页面嵌入
-
+
-
+
diff --git a/AntSK/Pages/AppPage/AppOpen.razor.cs b/AntSK/Pages/AppPage/AppOpen.razor.cs
index e8995548..a04eaf23 100644
--- a/AntSK/Pages/AppPage/AppOpen.razor.cs
+++ b/AntSK/Pages/AppPage/AppOpen.razor.cs
@@ -40,28 +40,39 @@ public partial class AppOpen
private Apps _appModel = new Apps();
- private string _openApiUrl { get; set; }
+ private string _openApiUrl { get; set; }
+
+ private string _openChatUrl { get; set; }
private string _desc { get; set; }
+ private string _script { get; set; }
+
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_appModel = _apps_Repositories.GetFirst(p => p.Id == AppId);
_openApiUrl = NavigationManager.BaseUri + "api/v1/chat/completions";
+ _openChatUrl= NavigationManager.BaseUri + "openchat/"+AppId;
GetDesc();
+ GetScript();
}
private void GetDesc()
{
- _desc = @$"为了方便其他应用对接,接口符合openai规范,省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization: ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages=new List() { new OpenAIMessage() { role="user",content="你好,你是谁"} } }, Formatting.Indented)}";
+ _desc = @$"为了方便其他应用对接,接口符合openai规范,省略了温度TopP等参数。{Environment.NewLine}BaseUrl:{Environment.NewLine}{_openApiUrl} {Environment.NewLine}headers:{Environment.NewLine}Authorization: ""{_appModel.SecretKey}"" {Environment.NewLine}Body: {Environment.NewLine}{JsonConvert.SerializeObject(new OpenAIModel() { messages = new List() { new OpenAIMessage() { role = "user", content = "你好,你是谁" } } }, Formatting.Indented)}";
+ }
+
+ private void GetScript()
+ {
+ _script = $"";
}
private void HandleSubmit()
{
}
- private async Task Reset()
+ private async Task Reset()
{
var content = "是否确认重置秘钥,重之后之前的秘钥将无法访问API接口";
var title = "重置";
@@ -72,7 +83,8 @@ private async Task Reset()
_apps_Repositories.Update(_appModel);
GetDesc();
}
-
+
}
+
}
}
diff --git a/AntSK/Pages/ChatPage/Chat.razor b/AntSK/Pages/ChatPage/Chat.razor
index 516a2148..81663b09 100644
--- a/AntSK/Pages/ChatPage/Chat.razor
+++ b/AntSK/Pages/ChatPage/Chat.razor
@@ -7,12 +7,12 @@
@using AntSK.Services.Auth
@inherits AuthComponentBase
-
-
-
-
-
- 选择应用
+
+
+
+
+
+ 选择应用
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 调试结果
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 调试结果
+
+
+
+
+
- @item.SourceName 相似度: @item.Relevance
-
- @((MarkupString)(@item.Text))
-
-
-
+ @item.SourceName 相似度: @item.Relevance
+
+ @((MarkupString)(@item.Text))
+
+
+
-@code {
+ @code {
}
diff --git a/AntSK/Pages/ChatPage/OpenChat.razor b/AntSK/Pages/ChatPage/OpenChat.razor
new file mode 100644
index 00000000..74e348d8
--- /dev/null
+++ b/AntSK/Pages/ChatPage/OpenChat.razor
@@ -0,0 +1,45 @@
+@namespace AntSK.Pages.ChatPage
+@using AntSK.Domain.Repositories
+@using AntSK.Models
+@using Microsoft.AspNetCore.Components.Web.Virtualization
+@page "/OpenChat/{AppId}"
+@layout OpenLayout
+
+
+
+@code {
+
+}
diff --git a/AntSK/Pages/ChatPage/OpenChat.razor.cs b/AntSK/Pages/ChatPage/OpenChat.razor.cs
new file mode 100644
index 00000000..fbfd623b
--- /dev/null
+++ b/AntSK/Pages/ChatPage/OpenChat.razor.cs
@@ -0,0 +1,213 @@
+using AntDesign;
+using AntSK.Domain.Model;
+using AntSK.Domain.Repositories;
+using AntSK.Domain.Utils;
+using Azure.AI.OpenAI;
+using Azure.Core;
+using DocumentFormat.OpenXml.EMMA;
+using MarkdownSharp;
+using Microsoft.AspNetCore.Components;
+using Microsoft.KernelMemory;
+using Microsoft.OpenApi.Models;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.ChatCompletion;
+using Microsoft.SemanticKernel.Connectors.OpenAI;
+using Newtonsoft.Json;
+using SqlSugar;
+using System;
+using System.Text;
+
+namespace AntSK.Pages.ChatPage
+{
+ public partial class OpenChat
+ {
+ [Parameter]
+ public string AppId { get; set; }
+ [Inject]
+ protected MessageService? Message { get; set; }
+ [Inject]
+ protected IApps_Repositories _apps_Repositories { get; set; }
+ [Inject]
+ protected IKmss_Repositories _kmss_Repositories { get; set; }
+ [Inject]
+ protected IKmsDetails_Repositories _kmsDetails_Repositories { get; set; }
+ [Inject]
+ protected MemoryServerless _memory { get; set; }
+ [Inject]
+ protected Kernel _kernel { get; set; }
+
+ protected bool _loading = false;
+ protected List MessageList = [];
+ protected string? _messageInput;
+ protected string _json = "";
+ protected bool Sendding = false;
+
+ protected Apps app = new Apps();
+ protected override async Task OnInitializedAsync()
+ {
+ await base.OnInitializedAsync();
+ app = _apps_Repositories.GetFirst(p=>p.Id==AppId);
+ }
+ protected async Task OnSendAsync()
+ {
+ if (string.IsNullOrWhiteSpace(_messageInput))
+ {
+ _ = Message.Info("请输入消息", 2);
+ return;
+ }
+
+ Sendding = true;
+ await SendAsync(_messageInput);
+ _messageInput = "";
+ Sendding = false;
+
+ }
+ protected async Task OnCopyAsync(MessageInfo item)
+ {
+ await Task.Run(() =>
+ {
+ _messageInput = item.Questions;
+ });
+ }
+
+ protected async Task OnClearAsync(string id)
+ {
+ await Task.Run(() =>
+ {
+ MessageList = MessageList.Where(w => w.ID != id).ToList();
+ });
+ }
+
+ protected async Task SendAsync(string questions)
+ {
+ string msg = questions;
+ //处理多轮会话
+ if (MessageList.Count > 0)
+ {
+ msg = await HistorySummarize(questions);
+ }
+
+ Apps app=_apps_Repositories.GetFirst(p => p.Id == AppId);
+ switch (app.Type)
+ {
+ case "chat":
+ //普通会话
+ await SendChat(questions, msg, app);
+ break;
+ case "kms":
+ //知识库问答
+ await SendKms(questions, msg, app);
+ break;
+ }
+
+ return await Task.FromResult(true);
+ }
+
+ ///
+ /// 发送知识库问答
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task SendKms(string questions, string msg, Apps app)
+ {
+ //知识库问答
+ var filters = new List();
+
+ var kmsidList = app.KmsIdList.Split(",");
+ foreach (var kmsid in kmsidList)
+ {
+ filters.Add(new MemoryFilter().ByTag("kmsid", kmsid));
+ }
+
+ var kmsResult = await _memory.AskAsync(msg, index: "kms", filters: filters);
+ if (kmsResult != null)
+ {
+ if (!string.IsNullOrEmpty(kmsResult.Result))
+ {
+ string answers = kmsResult.Result;
+ var markdown = new Markdown();
+ string htmlAnswers = markdown.Transform(answers);
+ var info1 = new MessageInfo()
+ {
+ ID = Guid.NewGuid().ToString(),
+ Questions = questions,
+ Answers = answers,
+ HtmlAnswers = htmlAnswers,
+ CreateTime = DateTime.Now,
+ };
+ MessageList.Add(info1);
+ }
+ }
+ }
+
+ ///
+ /// 发送普通对话
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task SendChat(string questions, string msg, Apps app)
+ {
+ if (string.IsNullOrEmpty(app.Prompt))
+ {
+ //如果模板为空,给默认提示词
+ app.Prompt = "{{$input}}";
+ }
+ var promptTemplateFactory = new KernelPromptTemplateFactory();
+ var promptTemplate = promptTemplateFactory.Create(new PromptTemplateConfig(app.Prompt));
+ var renderedPrompt = await promptTemplate.RenderAsync(_kernel);
+
+ var func = _kernel.CreateFunctionFromPrompt(app.Prompt, new OpenAIPromptExecutionSettings());
+ var chatResult = _kernel.InvokeStreamingAsync(function: func, arguments: new KernelArguments() { ["input"] = msg });
+ MessageInfo info = null;
+ var markdown = new Markdown();
+ await foreach (var content in chatResult)
+ {
+ if (info == null)
+ {
+ info = new MessageInfo();
+ info.ID = Guid.NewGuid().ToString();
+ info.Questions = questions;
+ info.Answers = content.Content!;
+ info.HtmlAnswers = content.Content!;
+ info.CreateTime = DateTime.Now;
+
+ MessageList.Add(info);
+ }
+ else
+ {
+ info.HtmlAnswers += content.Content;
+ await Task.Delay(50);
+ }
+ await InvokeAsync(StateHasChanged);
+ }
+ //全部处理完后再处理一次Markdown
+ info!.HtmlAnswers = markdown.Transform(info.HtmlAnswers);
+ await InvokeAsync(StateHasChanged);
+ }
+
+ ///
+ /// 历史会话的会话总结
+ ///
+ ///
+ ///
+ private async Task HistorySummarize(string questions)
+ {
+ StringBuilder history = new StringBuilder();
+ foreach (var item in MessageList)
+ {
+ history.Append($"user:{item.Questions}{Environment.NewLine}");
+ history.Append($"assistant:{item.Answers}{Environment.NewLine}");
+ }
+
+ KernelFunction sunFun = _kernel.Plugins.GetFunction("ConversationSummaryPlugin", "SummarizeConversation");
+ var summary = await _kernel.InvokeAsync(sunFun, new() { ["input"] = $"内容是:{history.ToString()} {Environment.NewLine} 请注意用中文总结" });
+ string his = summary.GetValue();
+ var msg = $"历史对话:{his}{Environment.NewLine} 用户问题:{Environment.NewLine}{questions}"; ;
+ return msg;
+ }
+ }
+}
diff --git a/AntSK/Pages/ChatPage/OpenChat.razor.css b/AntSK/Pages/ChatPage/OpenChat.razor.css
new file mode 100644
index 00000000..de3ea6b1
--- /dev/null
+++ b/AntSK/Pages/ChatPage/OpenChat.razor.css
@@ -0,0 +1,3 @@
+.ant-card-body {
+ height: 90% !important;
+}
\ No newline at end of file
diff --git a/AntSK/wwwroot/assets/ai.png b/AntSK/wwwroot/assets/ai.png
new file mode 100644
index 00000000..a97eac57
Binary files /dev/null and b/AntSK/wwwroot/assets/ai.png differ
diff --git a/AntSK/wwwroot/js/iframe.js b/AntSK/wwwroot/js/iframe.js
new file mode 100644
index 00000000..cd61f904
--- /dev/null
+++ b/AntSK/wwwroot/js/iframe.js
@@ -0,0 +1,175 @@
+async function embedChatbot() {
+ const chatBtnId = 'aiagent-chatbot-button'
+ const chatWindowId = 'aiagent-chatbot-window'
+ const script = document.getElementById('antsk-iframe')
+ const botSrc = script?.getAttribute('data-src')
+ const width = script?.getAttribute('data-width') || '30rem'
+ const height = script?.getAttribute('data-height') || '50rem'
+ const primaryColor = script?.getAttribute('data-color') || '#4e83fd'
+ const defaultOpen = script?.getAttribute('data-default-open') === 'true'
+ const MessageIconUrl = script?.getAttribute('data-message-icon-url')
+
+ if (!botSrc) {
+ console.error(`Can't find chaturl`)
+ return
+ }
+ if (document.getElementById(chatBtnId)) {
+ return
+ }
+
+ const MessageIcon = `
+ `
+
+ const CloseIcon = ``
+ const MessageIconImg = document.createElement('img')
+ MessageIconImg.src = MessageIconUrl
+ MessageIconImg.style.cssText = 'width: 100%; height: 100%;'
+ const ChatBtn = document.createElement('div')
+ ChatBtn.id = chatBtnId
+ ChatBtn.style.cssText = 'position: fixed; bottom: 1rem; right: 1rem; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; transition: 0;'
+
+ const ChatBtnDiv = document.createElement('div')
+ if (MessageIconUrl) {
+ ChatBtnDiv.appendChild(MessageIconImg)
+ } else {
+ ChatBtnDiv.innerHTML = MessageIcon
+ }
+ /**
+ * 添加样式
+ */
+ // 创建一个style元素
+ const style = document.createElement('style')
+ style.type = 'text/css'
+
+ // 添加CSS规则到style元素
+ const rules = document.createTextNode('.resizing-iframe iframe { pointer-events: none; }' + '.resizing-iframe { user-select: none; }')
+
+ // 将规则附加到style元素
+ style.appendChild(rules)
+
+ // 将style元素添加到document的head中,使其生效
+ document.head.appendChild(style)
+ const parentDiv = document.createElement('div')
+ parentDiv.style.cssText = `border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 4rem; right: 1rem; min-width:${width}; min-height: ${height};width:${width}; height: ${height}; max-width: 90vw; max-height: 85vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;`
+ parentDiv.id = chatWindowId
+ parentDiv.style.visibility = defaultOpen ? 'unset' : 'hidden'
+ const iframe = document.createElement('iframe')
+ iframe.allow = 'fullscreen;microphone'
+ iframe.title = 'AIAgent Chat Window'
+ iframe.target = '_self'
+ // iframe.id = chatWindowId
+ iframe.src = botSrc
+ iframe.style.cssText = `width: 100%; height: 100%; border: none;`
+ parentDiv.appendChild(iframe)
+ document.body.appendChild(parentDiv)
+ let resizing = false
+ let startX, startWidth
+
+ const resizer = document.createElement('div')
+ resizer.className = 'resizer'
+ iframe.parentElement.appendChild(resizer) // 假设iframe和resizer有相同的父元素
+
+ // 为resizer添加样式
+ resizer.style.width = '2px'
+ resizer.style.height = '100%'
+ resizer.style.background = '#f8f9fd'
+ resizer.style.position = 'absolute'
+ resizer.style.left = '0'
+ resizer.style.bottom = '0'
+ resizer.style.cursor = 'e-resize'
+
+ // 监听鼠标按下事件
+ resizer.addEventListener('mousedown', (e) => {
+ e.preventDefault()
+ document.body.classList.add('resizing-iframe')
+ startX = e.clientX
+ // startY = e.clientY
+ startWidth = parseInt(document.defaultView.getComputedStyle(parentDiv).width, 10)
+ // startHeight = parseInt(document.defaultView.getComputedStyle(parentDiv).height, 10)
+ resizing = true
+ })
+
+ // 监听鼠标移动事件
+ document.addEventListener('mousemove', (e) => {
+ if (!resizing) return
+ requestAnimationFrame(() => {
+ let newWidth = startWidth + (startX - e.clientX)
+ // let newHeight = startHeight + (e.clientY - startY)
+ parentDiv.style.width = newWidth + 'px'
+ // parentDiv.style.height = newHeight + 'px'
+ })
+ })
+
+ // 监听鼠标释放事件
+ document.addEventListener('mouseup', (e) => {
+ document.body.classList.remove('resizing-iframe')
+ resizing = false
+ })
+
+ let chatBtnDragged = false
+ let chatBtnDown = false
+ let chatBtnMouseX
+ let chatBtnMouseY
+ ChatBtn.addEventListener('click', function () {
+ if (chatBtnDragged) {
+ chatBtnDragged = false
+ return
+ }
+ const chatWindow = document.getElementById(chatWindowId)
+
+ if (!chatWindow) return
+ const visibilityVal = chatWindow.style.visibility
+ if (visibilityVal === 'hidden') {
+ chatWindow.style.visibility = 'unset'
+ ChatBtnDiv.innerHTML = CloseIcon
+ } else {
+ chatWindow.style.visibility = 'hidden'
+ if (MessageIconUrl) {
+ ChatBtnDiv.innerHTML = ''
+ ChatBtnDiv.appendChild(MessageIconImg)
+ } else {
+ ChatBtnDiv.innerHTML = MessageIcon
+ }
+ }
+ })
+
+ ChatBtn.addEventListener('mousedown', (e) => {
+ if (!chatBtnMouseX && !chatBtnMouseY) {
+ chatBtnMouseX = e.clientX
+ chatBtnMouseY = e.clientY
+ }
+
+ chatBtnDown = true
+ })
+ ChatBtn.addEventListener('mousemove', (e) => {
+ if (!chatBtnDown) return
+ chatBtnDragged = true
+ const transformX = e.clientX - chatBtnMouseX
+ const transformY = e.clientY - chatBtnMouseY
+
+ ChatBtn.style.transform = `translate3d(${transformX}px, ${transformY}px, 0)`
+
+ e.stopPropagation()
+ })
+ ChatBtn.addEventListener('mouseup', (e) => {
+ chatBtnDown = false
+ })
+ ChatBtn.addEventListener('mouseleave', (e) => {
+ chatBtnDown = false
+ })
+
+ ChatBtn.appendChild(ChatBtnDiv)
+ document.body.appendChild(ChatBtn)
+}
+document.body.onload = embedChatbot