Przeglądaj źródła

Merge pull request #2340 from visuddhinanda/development

Development
visuddhinanda 8 miesięcy temu
rodzic
commit
df710666fb

+ 2 - 0
dashboard-v4/dashboard/src/components/chat/AiChat.tsx

@@ -22,6 +22,7 @@ import MsgTyping from "./MsgTyping";
 import MsgLoading from "./MsgLoading";
 import MsgSystem from "./MsgSystem";
 import MsgError from "./MsgError";
+import PromptButtonGroup from "./PromptButtonGroup";
 
 const { TextArea } = Input;
 
@@ -533,6 +534,7 @@ const AIChatComponent = ({
                     icon={<PaperClipOutlined />}
                   />
                 </Tooltip>
+                <PromptButtonGroup onText={setInputValue} />
               </Space>
               <Space>
                 <Dropdown menu={modelMenu} trigger={["click"]}>

+ 145 - 0
dashboard-v4/dashboard/src/components/chat/PromptButtonGroup.tsx

@@ -0,0 +1,145 @@
+import { useEffect, useState } from "react";
+import { Button, Dropdown, Space } from "antd";
+import type { MenuProps } from "antd";
+import { IArticleListResponse } from "../api/Article";
+import { get } from "../../request";
+import { useAppSelector } from "../../hooks";
+import { currentUser } from "../../reducers/current-user";
+
+// 接口定义
+export interface IPromptNode {
+  text: string;
+  prompt?: string;
+  children?: IPromptNode[];
+}
+
+// Markdown 解析函数
+export function parseMarkdownToPromptNodes(markdown: string): IPromptNode[] {
+  const lines = markdown
+    .split("\n")
+    .map((l) => l.trim())
+    .filter(Boolean);
+
+  const result: IPromptNode[] = [];
+  let currentButton: IPromptNode | null = null;
+  let currentChild: { title: string; content: string[] } | null = null;
+
+  for (let line of lines) {
+    if (line.startsWith("# ")) {
+      if (currentButton) {
+        if (currentChild) {
+          currentButton.children = currentButton.children || [];
+          currentButton.children.push({
+            text: currentChild.title,
+            prompt: currentChild.content.join("\n"),
+          });
+        }
+        result.push(currentButton);
+      }
+      currentButton = { text: line.replace("# ", ""), children: [] };
+      currentChild = null;
+    } else if (line.startsWith("## ")) {
+      if (currentChild) {
+        currentButton!.children!.push({
+          text: currentChild.title,
+          prompt: currentChild.content.join("\n"),
+        });
+      }
+      currentChild = {
+        title: line.replace("## ", ""),
+        content: [],
+      };
+    } else {
+      currentChild?.content.push(line);
+    }
+  }
+
+  if (currentChild) {
+    currentButton!.children!.push({
+      text: currentChild.title,
+      prompt: currentChild.content.join("\n"),
+    });
+  }
+  if (currentButton) {
+    // 若没有children但有currentChild.prompt,作为主按钮 prompt
+    if (!currentButton.children?.length && currentChild?.content.length) {
+      currentButton.prompt = currentChild.content.join("\n");
+      delete currentButton.children;
+    }
+    result.push(currentButton);
+  }
+
+  return result;
+}
+
+interface IWidget {
+  onText?: (prompt: string) => void;
+}
+// 按钮组组件
+const PromptButtonGroup = ({ onText }: IWidget) => {
+  const user = useAppSelector(currentUser);
+  const [data, setData] = useState<IPromptNode[]>([]);
+
+  useEffect(() => {
+    if (!user) {
+      return;
+    }
+    const getPrompt = async (studio: string) => {
+      const urlTpl = `/v2/article?view=template&studio_name=${user?.realName}&subtitle=_template_prompt_&content=true`;
+      const json = await get<IArticleListResponse>(urlTpl);
+      if (json.ok) {
+        if (json.data.rows.length > 0) {
+          if (json.data.rows[0].content) {
+            return json.data.rows[0].content;
+          }
+        }
+      }
+      return false;
+    };
+
+    getPrompt(user?.realName).then((value) => {
+      if (value) {
+        setData(parseMarkdownToPromptNodes(value));
+      } else {
+        getPrompt("admin").then((value) => {
+          if (value) {
+            setData(parseMarkdownToPromptNodes(value));
+          }
+        });
+      }
+    });
+  }, [user, user?.realName]);
+
+  return (
+    <Space>
+      {data.map((node) => {
+        if (node.children && node.children.length > 0) {
+          const items: MenuProps["items"] = node.children.map((child, idx) => ({
+            key: `${node.text}-${idx}`,
+            label: child.text,
+            onClick: () => onText && onText(child.prompt || ""),
+          }));
+
+          return (
+            <Dropdown key={node.text} menu={{ items }} trigger={["click"]}>
+              <Button type="link" size="small">
+                {node.text}
+              </Button>
+            </Dropdown>
+          );
+        } else {
+          return (
+            <Button
+              key={node.text}
+              onClick={() => onText && onText(node.prompt || "")}
+            >
+              {node.text}
+            </Button>
+          );
+        }
+      })}
+    </Space>
+  );
+};
+
+export default PromptButtonGroup;

+ 1 - 22
dashboard-v4/dashboard/src/pages/library/search/search.tsx

@@ -277,28 +277,7 @@ const Widget = () => {
                   systemPrompt={sysPrompt}
                   onChat={() => setChat(true)}
                 />
-                <div>
-                  <Space>
-                    <Button
-                      onClick={() =>
-                        setPrompt(
-                          `写一个关于**${key}**的概要,概要中的观点应该引用上述巴利文经文,并逐条列出每个巴利原文每个段落的摘要`
-                        )
-                      }
-                    >
-                      概要
-                    </Button>
-                    <Button
-                      onClick={() =>
-                        setPrompt(
-                          `写一个介绍**${key}**的百科词条,词条中的观点应该引用巴利文经文,并给出引用的巴利原文和译文`
-                        )
-                      }
-                    >
-                      术语
-                    </Button>
-                  </Space>
-                </div>
+
                 {chat ? (
                   <></>
                 ) : (