xref: /plugin/ragasker/syntax.php (revision 5f0c5114d87f8140fd00a9b6f05655e54e173dde)
1<?php
2require_once(__DIR__ . '/OpenAIHttpClient.php');
3
4class syntax_plugin_ragasker extends DokuWiki_Syntax_Plugin {
5
6    public function getType() { return 'substition'; }
7    public function getSort() { return 155; }
8
9    public function connectTo($mode) {
10        // 多种语法支持
11        $this->Lexer->addSpecialPattern('~~RAGASKER~~', $mode, 'plugin_ragasker');
12    }
13
14    public function handle($match, $state, $pos, Doku_Handler $handler) {
15        // 只传递唯一ID用于渲染
16        $uniqid = uniqid('ragasker_', true);
17        return [$uniqid, null];
18    }
19
20    public function render($mode, Doku_Renderer $renderer, $data) {
21        if($mode !== 'xhtml') return false;
22        list($uniqid, $_) = $data;
23        $inputId = $uniqid . '_input';
24        $btnId = $uniqid . '_btn';
25        $resultId = $uniqid . '_result';
26        $renderer->doc .= '<div class="openai-widget" style="border:1px solid #ccc;padding:10px;margin:10px 0;">';
27        $renderer->doc .= '<input type="text" id="' . hsc($inputId) . '" style="width:60%;" placeholder="请输入你的问题..." /> ';
28        $renderer->doc .= '<button id="' . hsc($btnId) . '">提交</button>';
29        $renderer->doc .= '<div id="' . hsc($resultId) . '" style="margin-top:10px;"></div>';
30        $renderer->doc .= '</div>';
31        $renderer->doc .= '<script type="text/javascript">
32        (function(){
33            var btn = document.getElementById("' . hsc($btnId) . '");
34            var input = document.getElementById("' . hsc($inputId) . '");
35            var result = document.getElementById("' . hsc($resultId) . '");
36            var xhr = null;
37            var running = false;
38            var lastKeywords = "";
39            var lastLinkList = null;
40            var lastContentList = null;
41            function step1() {
42                result.innerHTML = "<em>步骤1:正在提取关键词...</em>";
43                xhr = new XMLHttpRequest();
44                xhr.open("POST", DOKU_BASE + "lib/exe/ajax.php", true);
45                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
46                xhr.onreadystatechange = function() {
47                    if(xhr.readyState === 4) {
48                        if(!running) return;
49                        if(xhr.status === 200) {
50                            try {
51                                var resp = JSON.parse(xhr.responseText);
52                                if(resp && resp.ragasker_response) {
53                                    result.innerHTML = resp.ragasker_response;
54                                    lastKeywords = resp.keywords || "";
55                                    if(resp.step === 1 && lastKeywords) step2();
56                                } else {
57                                    result.innerHTML = "<span style=\'color:red\'>API返回异常</span>";
58                                    running = false;
59                                    btn.innerText = "提交";
60                                }
61                            } catch(e) {
62                                result.innerHTML = "<span style=\'color:red\'>解析响应失败</span>";
63                                running = false;
64                                btn.innerText = "提交";
65                            }
66                        } else {
67                            result.innerHTML = "<span style=\'color:red\'>请求失败("+xhr.status+")</span>";
68                            running = false;
69                            btn.innerText = "提交";
70                        }
71                    }
72                };
73                xhr.onerror = function(e) {
74                    result.innerHTML = "<span style=\"color:red\">网络错误</span>";
75                    running = false;
76                    btn.innerText = "提交";
77                };
78                xhr.send("call=ragasker_generate&ragasker_widget=1&prompt=" + encodeURIComponent(input.value) + "&step=1");
79            }
80            function step2() {
81                result.innerHTML += "<br><em>步骤2:正在搜索页面...</em>";
82                xhr = new XMLHttpRequest();
83                xhr.open("POST", DOKU_BASE + "lib/exe/ajax.php", true);
84                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
85                xhr.onreadystatechange = function() {
86                    if(xhr.readyState === 4) {
87                        if(!running) return;
88                        if(xhr.status === 200) {
89                            try {
90                                var resp = JSON.parse(xhr.responseText);
91                                if(resp && resp.ragasker_response) {
92                                    result.innerHTML += "<hr>" + resp.ragasker_response;
93                                    // 这里 linkList/contentList 是 JSON 字符串
94                                    lastLinkList = resp.linkList;
95                                    lastContentList = resp.contentList;
96                                    if(resp.step === 2 && lastLinkList && lastContentList) step3();
97                                } else {
98                                    result.innerHTML += "<span style=\'color:red\'>API返回异常</span>";
99                                    running = false;
100                                    btn.innerText = "提交";
101                                }
102                            } catch(e) {
103                                result.innerHTML += "<span style=\'color:red\'>解析响应失败</span>";
104                                running = false;
105                                btn.innerText = "提交";
106                            }
107                        } else {
108                            result.innerHTML += "<span style=\'color:red\'>请求失败("+xhr.status+")</span>";
109                            running = false;
110                            btn.innerText = "提交";
111                        }
112                    }
113                };
114                xhr.onerror = function(e) {
115                    result.innerHTML += "<span style=\"color:red\">网络错误</span>";
116                    running = false;
117                    btn.innerText = "提交";
118                };
119                xhr.send(
120                    "call=ragasker_generate&ragasker_widget=1&prompt=" + encodeURIComponent(input.value) +
121                    "&step=2&keywords=" + encodeURIComponent(lastKeywords)
122                );
123            }
124            function step3() {
125                result.innerHTML += "<hr><em>步骤3:AI总结回答...</em>";
126                xhr = new XMLHttpRequest();
127                xhr.open("POST", DOKU_BASE + "lib/exe/ajax.php", true);
128                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
129                xhr.onreadystatechange = function() {
130                    if(xhr.readyState === 4) {
131                        if(!running) return;
132                        if(xhr.status === 200) {
133                            try {
134                                var resp = JSON.parse(xhr.responseText);
135                                if(resp && resp.ragasker_response) {
136                                    result.innerHTML += "<hr>" + resp.ragasker_response;
137                                } else {
138                                    result.innerHTML += "<span style=\'color:red\'>API返回异常</span>";
139                                }
140                            } catch(e) {
141                                result.innerHTML += "<span style=\'color:red\'>解析响应失败</span>";
142                            }
143                        } else {
144                            result.innerHTML += "<span style=\'color:red\'>请求失败("+xhr.status+")</span>";
145                        }
146                        running = false;
147                        btn.innerText = "提交";
148                    }
149                };
150                xhr.onerror = function(e) {
151                    result.innerHTML += "<span style=\"color:red\">网络错误</span>";
152                    running = false;
153                    btn.innerText = "提交";
154                };
155                // 这里 linkList/contentList 需保证为 JSON 字符串
156                xhr.send(
157                    "call=ragasker_generate&ragasker_widget=1&prompt=" + encodeURIComponent(input.value) +
158                    "&step=3&keywords=" + encodeURIComponent(lastKeywords) +
159                    "&linkList=" + encodeURIComponent(lastLinkList) +
160                    "&contentList=" + encodeURIComponent(lastContentList)
161                );
162            }
163            if(btn && input && result) {
164                btn.addEventListener("click", function() {
165                    if(running) {
166                        // 终止
167                        running = false;
168                        if(xhr) xhr.abort();
169                        btn.innerText = "提交";
170                        result.innerHTML += "<br><span style=\'color:orange\'>已终止</span>";
171                        return;
172                    }
173                    var prompt = input.value;
174                    if(!prompt) { result.innerHTML = "<span style=\'color:red\'>请输入内容</span>"; return; }
175                    running = true;
176                    btn.innerText = "终止";
177                    lastKeywords = "";
178                    lastLinkList = null;
179                    lastContentList = null;
180                    result.innerHTML = "";
181                    step1();
182                });
183            }
184        })();
185        </script>';
186        return true;
187    }
188
189    // callOpenAI 逻辑将迁移到 action 处理
190    // 保留接口以兼容
191    private function callOpenAI($prompt, $params = []) {
192        return '';
193    }
194
195    private function formatResponse($text) {
196        // 转换 Markdown 到 HTML
197        $text = hsc($text); // HTML 安全转义
198
199        // 基础 Markdown 转换
200        $text = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $text);
201        $text = preg_replace('/\*(.+?)\*/', '<em>$1</em>', $text);
202        $text = preg_replace('/`(.+?)`/', '<code>$1</code>', $text);
203
204        // 转换换行
205        $text = nl2br($text);
206
207        return $text;
208    }
209}
210