perf: 支持AI分析报错

pull/409/head
xiaojunnuo 2025-05-14 15:03:47 +08:00
parent abf015f485
commit aa96859798
8 changed files with 67 additions and 188 deletions

View File

@ -35,3 +35,7 @@ networks:
- subnet: 2001:db8::/64
```
## 3. SSL_CERT_NOT_MATCH_DOMAIN_ERROR
部署证书任务报类似 `SSL_CERT_NOT_MATCH_DOMAIN_ERROR`错误
这是由于当前流水线的证书域名与要部署的目标站点的域名不匹配导致的,在申请证书任务中,增加目标站点域名,重新运行流水线即可

File diff suppressed because one or more lines are too long

View File

@ -1,151 +0,0 @@
function embedChatbot() {
const chatBtnId = "fastgpt-chatbot-button";
const chatWindowId = "fastgpt-chatbot-window";
const script = document.getElementById("chatbot-iframe");
const botSrc = script?.getAttribute("data-bot-src");
const defaultOpen = script?.getAttribute("data-default-open") === "true";
const canDrag = script?.getAttribute("data-drag") === "true";
const MessageIcon =
script?.getAttribute("data-open-icon") ||
`data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTMyNzg1NjY0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxMzIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDMyQzI0Ny4wNCAzMiAzMiAyMjQgMzIgNDY0QTQxMC4yNCA0MTAuMjQgMCAwIDAgMTcyLjQ4IDc2OEwxNjAgOTY1LjEyYTI1LjI4IDI1LjI4IDAgMCAwIDM5LjA0IDIyLjRsMTY4LTExMkE1MjguNjQgNTI4LjY0IDAgMCAwIDUxMiA4OTZjMjY0Ljk2IDAgNDgwLTE5MiA0ODAtNDMyUzc3Ni45NiAzMiA1MTIgMzJ6IG0yNDQuOCA0MTZsLTM2MS42IDMwMS43NmExMi40OCAxMi40OCAwIDAgMS0xOS44NC0xMi40OGw1OS4yLTIzMy45MmgtMTYwYTEyLjQ4IDEyLjQ4IDAgMCAxLTcuMzYtMjMuMzZsMzYxLjYtMzAxLjc2YTEyLjQ4IDEyLjQ4IDAgMCAxIDE5Ljg0IDEyLjQ4bC01OS4yIDIzMy45MmgxNjBhMTIuNDggMTIuNDggMCAwIDEgOCAyMi4wOHoiIGZpbGw9IiM0ZTgzZmQiIHAtaWQ9IjQxMzMiPjwvcGF0aD48L3N2Zz4=`;
const CloseIcon =
script?.getAttribute("data-close-icon") ||
"data:image/svg+xml;base64,PHN2ZyB0PSIxNjkwNTM1NDQxNTI2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYzNjciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNTEyIDEwMjRBNTEyIDUxMiAwIDEgMSA1MTIgMGE1MTIgNTEyIDAgMCAxIDAgMTAyNHpNMzA1Ljk1NjU3MSAzNzAuMzk1NDI5TDQ0Ny40ODggNTEyIDMwNS45NTY1NzEgNjUzLjYwNDU3MWE0NS41NjggNDUuNTY4IDAgMSAwIDY0LjQzODg1OCA2NC40Mzg4NThMNTEyIDU3Ni41MTJsMTQxLjYwNDU3MSAxNDEuNTMxNDI5YTQ1LjU2OCA0NS41NjggMCAwIDAgNjQuNDM4ODU4LTY0LjQzODg1OEw1NzYuNTEyIDUxMmwxNDEuNTMxNDI5LTE0MS42MDQ1NzFhNDUuNTY4IDQ1LjU2OCAwIDEgMC02NC40Mzg4NTgtNjQuNDM4ODU4TDUxMiA0NDcuNDg4IDM3MC4zOTU0MjkgMzA1Ljk1NjU3MWE0NS41NjggNDUuNTY4IDAgMCAwLTY0LjQzODg1OCA2NC40Mzg4NTh6IiBmaWxsPSIjNGU4M2ZkIiBwLWlkPSI2MzY4Ij48L3BhdGg+PC9zdmc+";
if (!botSrc) {
console.error(`Can't find appid`);
return;
}
if (document.getElementById(chatBtnId)) {
return;
}
const ChatBtn = document.createElement("div");
ChatBtn.id = chatBtnId;
ChatBtn.style.cssText = "position: fixed; bottom: 30px; right: 60px; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; transition: 0;";
const ChatBtnDiv = document.createElement("img");
ChatBtnDiv.src = defaultOpen ? CloseIcon : MessageIcon;
ChatBtnDiv.setAttribute("width", "100%");
ChatBtnDiv.setAttribute("height", "100%");
ChatBtnDiv.draggable = false;
const parent = document.createElement("div");
const iframe = document.createElement("iframe");
iframe.allow = "*";
iframe.referrerPolicy = "no-referrer";
iframe.title = "FastGPT Chat Window";
parent.id = chatWindowId;
iframe.src = botSrc;
parent.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; width: 375px; height: 667px; max-width: 90vw; max-height: 85vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;";
parent.style.visibility = defaultOpen ? "unset" : "hidden";
iframe.style.cssText = "border: none;width:100%;height:100%";
document.body.appendChild(parent);
parent.appendChild(iframe);
const tips = document.createElement("div");
tips.innerText = "内容由AI生成仅供参考";
tips.style.cssText = "padding:5px;font-size:10px;display:flex; justify-content:center;border-top: 1px solid #ddd;";
parent.appendChild(tips);
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.type === "attributes" && mutation.attributeName === "data-bot-src") {
const newBotSrc = script.getAttribute("data-bot-src");
if (newBotSrc) {
iframe.src = newBotSrc;
}
}
});
});
observer.observe(script, {
attributes: true,
attributeFilter: ["data-bot-src"],
});
let chatBtnDragged = false;
let chatBtnDown = false;
let chatBtnMouseX;
let chatBtnMouseY;
const updateChatWindowPosition = () => {
const chatWindow = document.getElementById(chatWindowId);
const btn = ChatBtn.getBoundingClientRect();
const [vw, vh, ww, wh] = [window.innerWidth, window.innerHeight, chatWindow.offsetWidth, chatWindow.offsetHeight];
let right = 0;
if (btn.left >= ww) {
right = vw - btn.left + 10; // 左侧有空间则放在左侧,间距 10
} else if (vw - btn.right >= ww) {
right = vw - btn.right - ww - 10; // 右侧有空间则放在右侧
}
let bottom = Math.max(30, vh - btn.bottom); // 聊天窗口底部和按钮对齐,最少 30
if (btn.top < wh) {
bottom = Math.min(bottom, vh - wh - 30); // 确保聊天窗口不超出顶部
}
chatWindow.style.right = `${right}px`;
chatWindow.style.bottom = `${bottom}px`;
};
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.src = CloseIcon;
} else {
chatWindow.style.visibility = "hidden";
ChatBtnDiv.src = MessageIcon;
}
});
ChatBtn.addEventListener("mousedown", e => {
e.stopPropagation();
chatBtnMouseX = e.clientX;
chatBtnMouseY = e.clientY;
chatBtnDown = true;
ChatBtn.initialRight = parseInt(ChatBtn.style.right) || 60;
ChatBtn.initialBottom = parseInt(ChatBtn.style.bottom) || 30;
});
window.addEventListener("mousemove", e => {
e.stopPropagation();
if (!canDrag || !chatBtnDown) return;
chatBtnDragged = true;
const deltaX = e.clientX - chatBtnMouseX;
const deltaY = e.clientY - chatBtnMouseY;
let newRight = ChatBtn.initialRight - deltaX;
let newBottom = ChatBtn.initialBottom - deltaY;
newRight = Math.max(20, Math.min(window.innerWidth - 60, newRight));
newBottom = Math.max(30, Math.min(window.innerHeight - 70, newBottom));
ChatBtn.style.right = `${newRight}px`;
ChatBtn.style.bottom = `${newBottom}px`;
updateChatWindowPosition();
});
window.addEventListener("mouseup", e => {
chatBtnDown = false;
});
ChatBtn.appendChild(ChatBtnDiv);
document.body.appendChild(ChatBtn);
updateChatWindowPosition();
}
window.addEventListener("load", embedChatbot);

View File

@ -74,8 +74,12 @@ const tokenTheme = computed(() => {
// settingStore.init();
const chatBox = ref();
onMounted(async () => {
// await util.sleep(5000);
// await chatBox.value.openChat({ q: "hello" });
});
// onMounted(async () => {
// await util.sleep(2000);
// await chatBox.value.openChat({ q: "hello" });
// });
const openChat = (q: string) => {
chatBox.value.openChat({ q });
};
provide("fn:ai.open", openChat);
</script>

View File

@ -24,7 +24,7 @@
</div>
<!-- 聊天按钮 -->
<div v-show="!chatVisible" class="maxkb-chat-button" :style="buttonPosition" @click="toggleChat">
<div v-show="!chatVisible" class="maxkb-chat-button" @click="toggleChat">
<img src="https://maxkb.handfree.work/ui/MaxKB.gif" />
</div>
@ -123,7 +123,7 @@ onMounted(() => {
});
async function openChat(req: { q: string }) {
showGuide.value = true;
chatVisible.value = true;
const iframeId = "maxkb-chat";
@ -227,10 +227,10 @@ defineExpose({
#maxkb .maxkb-tips .maxkb-button button::after {
border: none;
}
#maxkb .maxkb-tips . {
#maxkb .maxkb-tips {
position: absolute;
right: 20px;
top: 20px;
//top: 20px;
cursor: pointer;
}
#maxkb-chat-container {
@ -248,7 +248,7 @@ defineExpose({
#maxkb .maxkb-chat-button {
position: fixed;
right: 0px;
right: 10px;
bottom: 30px;
cursor: pointer;
z-index: 10000;

View File

@ -21,7 +21,7 @@ async function batchUpdateGroupRequest(groupId: number) {
const pipelineGroupDictRef = dict({
url: "/pi/pipeline/group/all",
value: "id",
label: "name"
label: "name",
});
const { openCrudFormDialog } = useFormWrapper();
@ -33,9 +33,9 @@ async function openGroupSelectDialog() {
type: "dict-select",
dict: pipelineGroupDictRef,
form: {
rules: [{ required: true, message: "请选择分组" }]
}
}
rules: [{ required: true, message: "请选择分组" }],
},
},
},
form: {
mode: "edit",
@ -44,18 +44,18 @@ async function openGroupSelectDialog() {
await batchUpdateGroupRequest(form.groupId);
},
col: {
span: 22
span: 22,
},
labelCol: {
style: {
width: "100px"
}
width: "100px",
},
},
wrapper: {
title: "批量修改分组",
width: 600
}
}
width: 600,
},
},
} as any;
await openCrudFormDialog({ crudOptions });
}

View File

@ -22,6 +22,11 @@
</div>
</a-tab-pane>
</a-tabs>
<template #footer>
<fs-button key="aiChat" type="primary" icon="ion:color-wand-outline" @click="taskModal.onAiChat">AI</fs-button>
<fs-button key="cancel" icon="ion:close-circle-outline" @click="taskModal.onOk"></fs-button>
<fs-button key="submit" icon="ion:checkmark-circle-outline" type="primary" @click="taskModal.onOk"></fs-button>
</template>
</a-modal>
</template>
@ -37,11 +42,15 @@ export default {
props: {},
emits: ["run"],
setup(props: any, ctx: any) {
const openAiChat: any = inject("fn:ai.open", (q: string) => {});
const taskModal = ref({
open: false,
onOk() {
taskViewClose();
},
onAiChat() {
onAiChat();
},
cancelText: "关闭",
});
const { isMobile } = usePreferences();
@ -52,6 +61,22 @@ export default {
return "left";
});
function onAiChat() {
debugger;
const logs = currentHistory.value?.logs[activeKey.value];
let logText = "";
for (let log of logs) {
logText += log + "\n";
}
const maxLength = 5000;
if (logText.length > maxLength) {
logText = logText.substring(logText.length - maxLength);
}
if (openAiChat) {
openAiChat(logText);
}
}
const detail = ref({ nodes: [] });
const activeKey = ref();
const currentHistory: Ref<RunHistory> | undefined = inject("currentHistory");

View File

@ -9,7 +9,10 @@
<a-col :span="6">
<statistic-card title="用户总数" :count="count.userCount">
<template #footer>
<router-link to="/sys/authority/user" class="flex"><fs-icon icon="ion:settings-outline" class="mr-5 fs-16" /> 管理用户</router-link>
<router-link to="/sys/authority/user" class="flex">
<fs-icon icon="ion:settings-outline" class="mr-5 fs-16" />
管理用户
</router-link>
</template>
</statistic-card>
</a-col>
@ -21,7 +24,10 @@
<a-col :span="6">
<statistic-card title="全站流水线总数" :count="count.pipelineCount">
<template #footer>
<router-link to="/certd/pipeline" class="flex"><fs-icon icon="ion:settings-outline" class="mr-5 fs-16" /> 管理流水线</router-link>
<router-link to="/certd/pipeline" class="flex">
<fs-icon icon="ion:settings-outline" class="mr-5 fs-16" />
管理流水线
</router-link>
</template>
</statistic-card>
</a-col>
@ -42,21 +48,23 @@
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { onMounted, ref, Ref } from "vue";
import { FsIcon } from "@fast-crud/fast-crud";
import StatisticCard from "/@/views/framework/home/dashboard/statistic-card.vue";
import DayCount from "/@/views/framework/home/dashboard/charts/day-count.vue";
import { GetStatisticCount } from "./api";
const count = ref({});
function transformCountPerDayToChartData(key) {
count.value[key] = count.value[key].map((item) => {
const count: Ref = ref({});
function transformCountPerDayToChartData(key: string) {
count.value[key] = count.value[key].map((item:any) => {
return {
name: item.date,
value: item.count
value: item.count,
};
});
}
async function loadCount() {
count.value = await GetStatisticCount();
transformCountPerDayToChartData("userRegisterCountPerDay");