From 2e48ca5bfe9176ce9ead88ab3ee010dd063d5cdd Mon Sep 17 00:00:00 2001 From: terwer Date: Wed, 23 Aug 2023 18:35:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20AI=E9=9B=86=E6=88=90-=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=A0=87=E9=A2=98=E5=92=8C=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=91=98=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/publish/BatchPublishIndex.vue | 4 +- .../publish/SinglePublishDoPublish.vue | 9 +- .../publish/form/PublishDescription.vue | 50 +++++++--- src/components/publish/form/PublishTitle.vue | 49 +++++++++- src/models/aiPrompt.ts | 63 ++++++++++++ src/pages/AiChat.vue | 11 ++- src/utils/ai/prompt.ts | 96 +++++++++++++++++++ 7 files changed, 257 insertions(+), 25 deletions(-) create mode 100644 src/models/aiPrompt.ts create mode 100644 src/utils/ai/prompt.ts diff --git a/src/components/publish/BatchPublishIndex.vue b/src/components/publish/BatchPublishIndex.vue index 7f241afc..df0e3072 100644 --- a/src/components/publish/BatchPublishIndex.vue +++ b/src/components/publish/BatchPublishIndex.vue @@ -294,7 +294,7 @@ const handleRefresh = () => { BrowserUtil.reloadPage() } -const chckedChatGPTEnabled = () => { +const checkChatGPTEnabled = () => { let flag = false try { useChatGPT() @@ -321,7 +321,7 @@ onMounted(async () => { // ================== // 这里可以控制一些功能开关 - formData.useAi = chckedChatGPTEnabled() + formData.useAi = checkChatGPTEnabled() formData.editType = PageEditMode.EditMode_simple }) diff --git a/src/components/publish/SinglePublishDoPublish.vue b/src/components/publish/SinglePublishDoPublish.vue index 23e82581..674c6a3c 100644 --- a/src/components/publish/SinglePublishDoPublish.vue +++ b/src/components/publish/SinglePublishDoPublish.vue @@ -354,7 +354,7 @@ const initPage = async () => { } } -const chckedChatGPTEnabled = () => { +const checkChatGPTEnabled = () => { let flag = false try { useChatGPT() @@ -402,7 +402,7 @@ onMounted(async () => { // ================== // 这里可以控制一些功能开关 - formData.useAi = chckedChatGPTEnabled() + formData.useAi = checkChatGPTEnabled() formData.editType = PageEditMode.EditMode_simple }) @@ -445,9 +445,9 @@ onMounted(async () => { @@ -478,7 +478,8 @@ onMounted(async () => { v-model:use-ai="formData.useAi" v-model:page-id="id" v-model:desc="formData.mergedPost.shortDesc" - v-model:content="formData.mergedPost.html" + v-model:md="formData.mergedPost.markdown" + v-model:html="formData.mergedPost.html" @emitSyncDesc="syncDesc" /> diff --git a/src/components/publish/form/PublishDescription.vue b/src/components/publish/form/PublishDescription.vue index 7dfe57c2..73a3f283 100644 --- a/src/components/publish/form/PublishDescription.vue +++ b/src/components/publish/form/PublishDescription.vue @@ -28,7 +28,9 @@ import { useVueI18n } from "~/src/composables/useVueI18n.ts" import { reactive, watch } from "vue" import { createAppLogger } from "~/src/utils/appLogger.ts" import { ElMessage } from "element-plus" -import { HtmlUtil, SmartUtil, StrUtil } from "zhi-common" +import { HtmlUtil, JsonUtil, SmartUtil, StrUtil } from "zhi-common" +import { prompt, ShortDescAIResult } from "~/src/utils/ai/prompt.ts" +import { useChatGPT } from "~/src/composables/useChatGPT.ts" const logger = createAppLogger("publish-description") const { t } = useVueI18n() @@ -74,6 +76,13 @@ watch( } ) +watch( + () => props.md, + (newValue) => { + formData.md = newValue + } +) + const emit = defineEmits(["emitSyncDesc"]) const handleMakeDesc = async () => { @@ -81,18 +90,33 @@ const handleMakeDesc = async () => { formData.isDescLoading = true try { if (formData.useAi) { - if (StrUtil.isEmptyString(formData.html)) { - throw new Error("正文为空,无法生成摘要") - } - logger.debug("使用人工智能提取摘要", { q: formData.html }) - const result = await SmartUtil.autoSummary(formData.html) - logger.debug("auto summary reault =>", result) - if (!StrUtil.isEmptyString(result.errMsg)) { - throw new Error(result.errMsg) - } else { - formData.desc = result.result + const inputWord = `${formData.md}\n${prompt.shortDescPrompt.content}` + const { chat } = useChatGPT() + const chatText = await chat(inputWord) + if (StrUtil.isEmptyString(chatText)) { + ElMessage.error("请求错误,请在偏好设置配置请求地址和ChatGPT key!") + return } - ElMessage.warning("使用人工智能提取摘要成功") + const resJson = JsonUtil.safeParse(chatText, {} as ShortDescAIResult) + formData.desc = resJson.desc + logger.info("使用AI智能生成的摘要结果 =>", { + inputWord: inputWord, + chatText: chatText, + }) + + // 自部署无监督摘要 + // if (StrUtil.isEmptyString(formData.html)) { + // throw new Error("正文为空,无法生成摘要") + // } + // logger.debug("使用人工智能提取摘要", { q: formData.html }) + // const result = await SmartUtil.autoSummary(formData.html) + // logger.debug("auto summary reault =>", result) + // if (!StrUtil.isEmptyString(result.errMsg)) { + // throw new Error(result.errMsg) + // } else { + // formData.desc = result.result + // } + ElMessage.success("使用人工智能提取摘要成功") } else { formData.desc = HtmlUtil.parseHtml(formData.html, MAX_PREVIEW_LENGTH, true) ElMessage.success(`操作成功,未开启人工智能,直接截取文章前${MAX_PREVIEW_LENGTH}个字符作为摘要`) @@ -100,7 +124,7 @@ const handleMakeDesc = async () => { // ElMessage.success(t("main.opt.success")) } catch (e) { logger.error(t("main.opt.failure") + "=>", e) - ElMessage.error(t("main.opt.failure") + "=>"+ e) + ElMessage.error(t("main.opt.failure") + "=>" + e) } formData.isDescLoading = false diff --git a/src/components/publish/form/PublishTitle.vue b/src/components/publish/form/PublishTitle.vue index 03fbe9b0..6c5ffe6b 100644 --- a/src/components/publish/form/PublishTitle.vue +++ b/src/components/publish/form/PublishTitle.vue @@ -27,7 +27,13 @@ import { reactive } from "vue" import { useVueI18n } from "~/src/composables/useVueI18n.ts" import { watch } from "vue" +import { JsonUtil, StrUtil } from "zhi-common" +import { ElMessage } from "element-plus" +import { useChatGPT } from "~/src/composables/useChatGPT.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { prompt, TitleAIResult } from "~/src/utils/ai/prompt.ts" +const logger = createAppLogger("publish-title") const { t } = useVueI18n() const props = defineProps({ @@ -49,11 +55,12 @@ const props = defineProps({ }, }) -const TITLE_AI_PROPMTS = "" const formData = reactive({ postTitle: props.modelValue, isLoading: false, useAi: props.useAi, + md: props.md, + html: props.html, }) watch( @@ -63,13 +70,51 @@ watch( } ) +watch( + () => props.modelValue, + (newValue) => { + formData.postTitle = newValue + } +) + +watch( + () => props.md, + (newValue) => { + formData.md = newValue + } +) + const emit = defineEmits(["emitSyncPublishTitle"]) const handleTitleChange = () => { emit("emitSyncPublishTitle", formData.postTitle) } -const handleMakeTitle = () => {} +const handleMakeTitle = async () => { + try { + formData.isLoading = true + const inputWord = `${formData.md}\n${prompt.titlePrompt.content}` + const { chat } = useChatGPT() + const chatText = await chat(inputWord) + if (StrUtil.isEmptyString(chatText)) { + ElMessage.error("请求错误,请在偏好设置配置请求地址和ChatGPT key!") + return + } + const resJson = JsonUtil.safeParse(chatText, {} as TitleAIResult) + formData.postTitle = resJson.title + emit("emitSyncPublishTitle", formData.postTitle) + logger.info("使用AI智能生成的标题结果 =>", { + inputWord: inputWord, + chatText: chatText, + }) + ElMessage.success("使用人工智能提取标题成功") + } catch (e) { + logger.error(t("main.opt.failure") + "=>", e) + ElMessage.error(t("main.opt.failure") + "=>" + e) + } finally { + formData.isLoading = false + } +}