From eaed07a9bb88d27680fb2574b0980d1b1f938412 Mon Sep 17 00:00:00 2001 From: atsuki-t Date: Thu, 12 Oct 2023 16:08:08 +0000 Subject: [PATCH 1/2] add ChatGPTHelper --- .../components/PageEditor/ChatGPTHelper.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 apps/app/src/components/PageEditor/ChatGPTHelper.ts diff --git a/apps/app/src/components/PageEditor/ChatGPTHelper.ts b/apps/app/src/components/PageEditor/ChatGPTHelper.ts new file mode 100644 index 0000000000..0e7b6416c0 --- /dev/null +++ b/apps/app/src/components/PageEditor/ChatGPTHelper.ts @@ -0,0 +1,78 @@ +import fm from 'front-matter'; +import yaml from 'js-yaml'; + +import axios from '~/utils/axios'; +import loggerFactory from '~/utils/logger'; + +const logger = loggerFactory('growi:PageEditor:ChatGPTHelper'); + +// TODO: このメソッドの代わりに '~/features/cms/server/utils' を元にした CMS と LMS の両方に対応した utils を作成してそれを使用する +const extractMetadata = (markdown: string): Record | undefined => { + const extractedData = fm(markdown); + + const { frontmatter } = extractedData; + if (frontmatter == null) { + return undefined; + } + + return yaml.load(frontmatter); +}; + +// cms もしくは lms の frontmatter が存在しない場合 unefined を返す +const getCMSOrLMSMetadataAndType = (metadata) => { + if (metadata?.cms != null) { + return { cmsOrLMSMetadata: metadata.cms, generateTextType: '記事' }; + } + if (metadata?.lms != null) { + return { cmsOrLMSMetadata: metadata.lms, generateTextType: '教材' }; + } +}; + +export const generateCMSOrLMSText = async(markdownText: string): Promise => { + const URL = 'https://api.openai.com/v1/chat/completions'; + const API_KEY = ''; + + const metadata = extractMetadata(markdownText); + + const cmsOrLMSMetadataAndType = getCMSOrLMSMetadataAndType(metadata); + if (cmsOrLMSMetadataAndType == null) { + return; + } + + const { cmsOrLMSMetadata, generateTextType } = cmsOrLMSMetadataAndType; + + const title = cmsOrLMSMetadata?.title; + const autogen = cmsOrLMSMetadata?.autogen; + + try { + // TODO: frontMatter と、frontMatter を切り離した markdown の両方を変数に持たせ、frontMatter は ChatGPT に渡さずに response にくっつけて返す + const requestText = ` + ${autogen == null ? '' : `${autogen}という要素を入れた、`}${title == null ? '以下の markdown 部分の内容に合う' : `「${title}」というタイトルの`} + ${generateTextType}を日本語で markdown の形式で出力してください。 + その際、最初に入っていた frontmatter をインデントや改行なども一切変えず、含まれた状態で出力してください。 + さらに、その際に # から始まるセクションタイトルのみが入った markdown があった場合、そのセクションタイトルを雛形として適切な内容を入れてください。 + + ${markdownText} + `; + + const response = await axios.post( + URL, + { + model: 'gpt-4', + messages: [ + { role: 'user', content: requestText }, + ], + }, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${API_KEY}`, + }, + }, + ); + return response.data.choices[0].message.content; + } + catch (error) { + logger.error(error); + } +}; From 552dc2e9d2170fbf22c612659c23555b918c25f7 Mon Sep 17 00:00:00 2001 From: atsuki-t Date: Thu, 12 Oct 2023 16:08:32 +0000 Subject: [PATCH 2/2] add render chatGPT text button --- .../PageEditor/CodeMirrorEditor.jsx | 19 +++++++++++++++++++ .../src/components/PageEditor/EditorIcon.jsx | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/apps/app/src/components/PageEditor/CodeMirrorEditor.jsx b/apps/app/src/components/PageEditor/CodeMirrorEditor.jsx index 21eb4ebe95..73f53ed23b 100644 --- a/apps/app/src/components/PageEditor/CodeMirrorEditor.jsx +++ b/apps/app/src/components/PageEditor/CodeMirrorEditor.jsx @@ -17,6 +17,7 @@ import loggerFactory from '~/utils/logger'; import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror'; import AbstractEditor from './AbstractEditor'; +import { generateCMSOrLMSText } from './ChatGPTHelper'; import CommentMentionHelper from './CommentMentionHelper'; import EditorIcon from './EditorIcon'; import EmojiPicker from './EmojiPicker'; @@ -852,6 +853,15 @@ class CodeMirrorEditor extends AbstractEditor { this.props.onClickTemplateBtn({ onSubmit }); } + async renderChatGPTText() { + const cm = this.getCodeMirror(); + const generatedText = await generateCMSOrLMSText(cm.getValue()); + + if (generatedText != null) { + cm.setValue(generatedText); + } + } + showLinkEditModal() { const onSubmit = (linkText) => { return markdownLinkUtil.replaceFocusedMarkdownLinkWithEditor(this.getCodeMirror(), linkText); @@ -1051,6 +1061,15 @@ class CodeMirrorEditor extends AbstractEditor { > , + , ]; } diff --git a/apps/app/src/components/PageEditor/EditorIcon.jsx b/apps/app/src/components/PageEditor/EditorIcon.jsx index 4c175c6a41..72bc51e8df 100644 --- a/apps/app/src/components/PageEditor/EditorIcon.jsx +++ b/apps/app/src/components/PageEditor/EditorIcon.jsx @@ -147,6 +147,12 @@ const EditorIcon = (props) => { ); + case 'ChatGPT': + return ( + + + + ); }