xref: /plugin/ragasker/syntax.php (revision ee5a17d9fbcd1782f5b6e74c2d73d7240fd2f789)
15f0c5114SCharles Chan<?php
25f0c5114SCharles Chanrequire_once(__DIR__ . '/OpenAIHttpClient.php');
35f0c5114SCharles Chan
45f0c5114SCharles Chanclass syntax_plugin_ragasker extends DokuWiki_Syntax_Plugin {
55f0c5114SCharles Chan
65f0c5114SCharles Chan    public function getType() { return 'substition'; }
75f0c5114SCharles Chan    public function getSort() { return 155; }
85f0c5114SCharles Chan
95f0c5114SCharles Chan    public function connectTo($mode) {
105f0c5114SCharles Chan        // 多种语法支持
115f0c5114SCharles Chan        $this->Lexer->addSpecialPattern('~~RAGASKER~~', $mode, 'plugin_ragasker');
125f0c5114SCharles Chan    }
135f0c5114SCharles Chan
145f0c5114SCharles Chan    public function handle($match, $state, $pos, Doku_Handler $handler) {
155f0c5114SCharles Chan        // 只传递唯一ID用于渲染
165f0c5114SCharles Chan        $uniqid = uniqid('ragasker_', true);
175f0c5114SCharles Chan        return [$uniqid, null];
185f0c5114SCharles Chan    }
195f0c5114SCharles Chan
205f0c5114SCharles Chan    public function render($mode, Doku_Renderer $renderer, $data) {
215f0c5114SCharles Chan        if($mode !== 'xhtml') return false;
225f0c5114SCharles Chan        list($uniqid, $_) = $data;
235f0c5114SCharles Chan        $inputId = $uniqid . '_input';
245f0c5114SCharles Chan        $btnId = $uniqid . '_btn';
255f0c5114SCharles Chan        $resultId = $uniqid . '_result';
265f0c5114SCharles Chan        $renderer->doc .= '<div class="openai-widget" style="border:1px solid #ccc;padding:10px;margin:10px 0;">';
27*ee5a17d9SCharles Chan        $renderer->doc .= '<input type="text" id="' . hsc($inputId) . '" style="width:60%;" placeholder="' . hsc($this->getLang('input_placeholder')) . '" /> ';
28*ee5a17d9SCharles Chan        $renderer->doc .= '<button id="' . hsc($btnId) . '">' . hsc($this->getLang('submit_btn')) . '</button>';
295f0c5114SCharles Chan        $renderer->doc .= '<div id="' . hsc($resultId) . '" style="margin-top:10px;"></div>';
305f0c5114SCharles Chan        $renderer->doc .= '</div>';
315f0c5114SCharles Chan        $renderer->doc .= '<script type="text/javascript">
325f0c5114SCharles Chan        (function(){
335f0c5114SCharles Chan            var btn = document.getElementById("' . hsc($btnId) . '");
345f0c5114SCharles Chan            var input = document.getElementById("' . hsc($inputId) . '");
355f0c5114SCharles Chan            var result = document.getElementById("' . hsc($resultId) . '");
365f0c5114SCharles Chan            var xhr = null;
375f0c5114SCharles Chan            var running = false;
385f0c5114SCharles Chan            var lastKeywords = "";
395f0c5114SCharles Chan            var lastLinkList = null;
405f0c5114SCharles Chan            var lastContentList = null;
41*ee5a17d9SCharles Chan            // 多语言文本
42*ee5a17d9SCharles Chan            var i18n = {
43*ee5a17d9SCharles Chan                step1: "' . hsc(sprintf($this->getLang('step_title'), 1, $this->getLang('step_extracting'))) . '",
44*ee5a17d9SCharles Chan                step2: "' . hsc(sprintf($this->getLang('step_title'), 2, $this->getLang('step_searching'))) . '",
45*ee5a17d9SCharles Chan                step3: "' . hsc(sprintf($this->getLang('step_title'), 3, $this->getLang('step_summarizing'))) . '",
46*ee5a17d9SCharles Chan                api_error: "' . hsc($this->getLang('error_api')) . '",
47*ee5a17d9SCharles Chan                parse_error: "' . hsc($this->getLang('error_parse')) . '",
48*ee5a17d9SCharles Chan                request_error: "' . hsc($this->getLang('error_request')) . '",
49*ee5a17d9SCharles Chan                network_error: "' . hsc($this->getLang('error_network')) . '",
50*ee5a17d9SCharles Chan                stop: "' . hsc($this->getLang('stop_btn')) . '",
51*ee5a17d9SCharles Chan                submit: "' . hsc($this->getLang('submit_btn')) . '",
52*ee5a17d9SCharles Chan                stopped: "' . hsc($this->getLang('stopped')) . '",
53*ee5a17d9SCharles Chan                input_empty: "' . hsc($this->getLang('error_input_empty')) . '"
54*ee5a17d9SCharles Chan            };
555f0c5114SCharles Chan            function step1() {
56*ee5a17d9SCharles Chan                result.innerHTML = "<em>" + i18n.step1 + "</em>";
575f0c5114SCharles Chan                xhr = new XMLHttpRequest();
585f0c5114SCharles Chan                xhr.open("POST", DOKU_BASE + "lib/exe/ajax.php", true);
595f0c5114SCharles Chan                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
605f0c5114SCharles Chan                xhr.onreadystatechange = function() {
615f0c5114SCharles Chan                    if(xhr.readyState === 4) {
625f0c5114SCharles Chan                        if(!running) return;
635f0c5114SCharles Chan                        if(xhr.status === 200) {
64*ee5a17d9SCharles Chan                            console.log(xhr.responseText);
655f0c5114SCharles Chan                            try {
665f0c5114SCharles Chan                                var resp = JSON.parse(xhr.responseText);
675f0c5114SCharles Chan                                if(resp && resp.ragasker_response) {
685f0c5114SCharles Chan                                    result.innerHTML = resp.ragasker_response;
695f0c5114SCharles Chan                                    lastKeywords = resp.keywords || "";
705f0c5114SCharles Chan                                    if(resp.step === 1 && lastKeywords) step2();
715f0c5114SCharles Chan                                } else {
72*ee5a17d9SCharles Chan                                    result.innerHTML = "<span style=\'color:red\'>" + i18n.api_error + "</span>";
735f0c5114SCharles Chan                                    running = false;
74*ee5a17d9SCharles Chan                                    btn.innerText = i18n.submit;
755f0c5114SCharles Chan                                }
765f0c5114SCharles Chan                            } catch(e) {
77*ee5a17d9SCharles Chan                                result.innerHTML = "<span style=\'color:red\'>" + i18n.parse_error + "</span>";
785f0c5114SCharles Chan                                running = false;
79*ee5a17d9SCharles Chan                                btn.innerText = i18n.submit;
805f0c5114SCharles Chan                            }
815f0c5114SCharles Chan                        } else {
82*ee5a17d9SCharles Chan                            result.innerHTML = "<span style=\'color:red\'>" + i18n.request_error + "("+xhr.status+")</span>";
835f0c5114SCharles Chan                            running = false;
84*ee5a17d9SCharles Chan                            btn.innerText = i18n.submit;
855f0c5114SCharles Chan                        }
865f0c5114SCharles Chan                    }
875f0c5114SCharles Chan                };
885f0c5114SCharles Chan                xhr.onerror = function(e) {
89*ee5a17d9SCharles Chan                    result.innerHTML = "<span style=\"color:red\">" + i18n.network_error + "</span>";
905f0c5114SCharles Chan                    running = false;
91*ee5a17d9SCharles Chan                    btn.innerText = i18n.submit;
925f0c5114SCharles Chan                };
935f0c5114SCharles Chan                xhr.send("call=ragasker_generate&ragasker_widget=1&prompt=" + encodeURIComponent(input.value) + "&step=1");
945f0c5114SCharles Chan            }
955f0c5114SCharles Chan            function step2() {
96*ee5a17d9SCharles Chan                result.innerHTML += "<br><em>" + i18n.step2 + "</em>";
975f0c5114SCharles Chan                xhr = new XMLHttpRequest();
985f0c5114SCharles Chan                xhr.open("POST", DOKU_BASE + "lib/exe/ajax.php", true);
995f0c5114SCharles Chan                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
1005f0c5114SCharles Chan                xhr.onreadystatechange = function() {
1015f0c5114SCharles Chan                    if(xhr.readyState === 4) {
1025f0c5114SCharles Chan                        if(!running) return;
1035f0c5114SCharles Chan                        if(xhr.status === 200) {
1045f0c5114SCharles Chan                            try {
1055f0c5114SCharles Chan                                var resp = JSON.parse(xhr.responseText);
1065f0c5114SCharles Chan                                if(resp && resp.ragasker_response) {
1075f0c5114SCharles Chan                                    result.innerHTML += "<hr>" + resp.ragasker_response;
1085f0c5114SCharles Chan                                    lastLinkList = resp.linkList;
1095f0c5114SCharles Chan                                    lastContentList = resp.contentList;
1105f0c5114SCharles Chan                                    if(resp.step === 2 && lastLinkList && lastContentList) step3();
1115f0c5114SCharles Chan                                } else {
112*ee5a17d9SCharles Chan                                    result.innerHTML += "<span style=\'color:red\'>" + i18n.api_error + "</span>";
1135f0c5114SCharles Chan                                    running = false;
114*ee5a17d9SCharles Chan                                    btn.innerText = i18n.submit;
1155f0c5114SCharles Chan                                }
1165f0c5114SCharles Chan                            } catch(e) {
117*ee5a17d9SCharles Chan                                result.innerHTML += "<span style=\'color:red\'>" + i18n.parse_error + "</span>";
1185f0c5114SCharles Chan                                running = false;
119*ee5a17d9SCharles Chan                                btn.innerText = i18n.submit;
1205f0c5114SCharles Chan                            }
1215f0c5114SCharles Chan                        } else {
122*ee5a17d9SCharles Chan                            result.innerHTML += "<span style=\'color:red\'>" + i18n.request_error + "("+xhr.status+")</span>";
1235f0c5114SCharles Chan                            running = false;
124*ee5a17d9SCharles Chan                            btn.innerText = i18n.submit;
1255f0c5114SCharles Chan                        }
1265f0c5114SCharles Chan                    }
1275f0c5114SCharles Chan                };
1285f0c5114SCharles Chan                xhr.onerror = function(e) {
129*ee5a17d9SCharles Chan                    result.innerHTML += "<span style=\"color:red\">" + i18n.network_error + "</span>";
1305f0c5114SCharles Chan                    running = false;
131*ee5a17d9SCharles Chan                    btn.innerText = i18n.submit;
1325f0c5114SCharles Chan                };
1335f0c5114SCharles Chan                xhr.send(
1345f0c5114SCharles Chan                    "call=ragasker_generate&ragasker_widget=1&prompt=" + encodeURIComponent(input.value) +
1355f0c5114SCharles Chan                    "&step=2&keywords=" + encodeURIComponent(lastKeywords)
1365f0c5114SCharles Chan                );
1375f0c5114SCharles Chan            }
1385f0c5114SCharles Chan            function step3() {
139*ee5a17d9SCharles Chan                result.innerHTML += "<hr><em>" + i18n.step3 + "</em>";
1405f0c5114SCharles Chan                xhr = new XMLHttpRequest();
1415f0c5114SCharles Chan                xhr.open("POST", DOKU_BASE + "lib/exe/ajax.php", true);
1425f0c5114SCharles Chan                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
1435f0c5114SCharles Chan                xhr.onreadystatechange = function() {
1445f0c5114SCharles Chan                    if(xhr.readyState === 4) {
1455f0c5114SCharles Chan                        if(!running) return;
1465f0c5114SCharles Chan                        if(xhr.status === 200) {
1475f0c5114SCharles Chan                            try {
1485f0c5114SCharles Chan                                var resp = JSON.parse(xhr.responseText);
1495f0c5114SCharles Chan                                if(resp && resp.ragasker_response) {
1505f0c5114SCharles Chan                                    result.innerHTML += "<hr>" + resp.ragasker_response;
1515f0c5114SCharles Chan                                } else {
152*ee5a17d9SCharles Chan                                    result.innerHTML += "<span style=\'color:red\'>" + i18n.api_error + "</span>";
1535f0c5114SCharles Chan                                }
1545f0c5114SCharles Chan                            } catch(e) {
155*ee5a17d9SCharles Chan                                result.innerHTML += "<span style=\'color:red\'>" + i18n.parse_error + "</span>";
1565f0c5114SCharles Chan                            }
1575f0c5114SCharles Chan                        } else {
158*ee5a17d9SCharles Chan                            result.innerHTML += "<span style=\'color:red\'>" + i18n.request_error + "("+xhr.status+")</span>";
1595f0c5114SCharles Chan                        }
1605f0c5114SCharles Chan                        running = false;
161*ee5a17d9SCharles Chan                        btn.innerText = i18n.submit;
1625f0c5114SCharles Chan                    }
1635f0c5114SCharles Chan                };
1645f0c5114SCharles Chan                xhr.onerror = function(e) {
165*ee5a17d9SCharles Chan                    result.innerHTML += "<span style=\"color:red\">" + i18n.network_error + "</span>";
1665f0c5114SCharles Chan                    running = false;
167*ee5a17d9SCharles Chan                    btn.innerText = i18n.submit;
1685f0c5114SCharles Chan                };
1695f0c5114SCharles Chan                xhr.send(
1705f0c5114SCharles Chan                    "call=ragasker_generate&ragasker_widget=1&prompt=" + encodeURIComponent(input.value) +
1715f0c5114SCharles Chan                    "&step=3&keywords=" + encodeURIComponent(lastKeywords) +
1725f0c5114SCharles Chan                    "&linkList=" + encodeURIComponent(lastLinkList) +
1735f0c5114SCharles Chan                    "&contentList=" + encodeURIComponent(lastContentList)
1745f0c5114SCharles Chan                );
1755f0c5114SCharles Chan            }
1765f0c5114SCharles Chan            if(btn && input && result) {
1775f0c5114SCharles Chan                btn.addEventListener("click", function() {
1785f0c5114SCharles Chan                    if(running) {
1795f0c5114SCharles Chan                        running = false;
1805f0c5114SCharles Chan                        if(xhr) xhr.abort();
181*ee5a17d9SCharles Chan                        btn.innerText = i18n.submit;
182*ee5a17d9SCharles Chan                        result.innerHTML += "<br><span style=\'color:orange\'>" + i18n.stopped + "</span>";
1835f0c5114SCharles Chan                        return;
1845f0c5114SCharles Chan                    }
1855f0c5114SCharles Chan                    var prompt = input.value;
186*ee5a17d9SCharles Chan                    if(!prompt) { result.innerHTML = "<span style=\'color:red\'>" + i18n.input_empty + "</span>"; return; }
1875f0c5114SCharles Chan                    running = true;
188*ee5a17d9SCharles Chan                    btn.innerText = i18n.stop;
1895f0c5114SCharles Chan                    lastKeywords = "";
1905f0c5114SCharles Chan                    lastLinkList = null;
1915f0c5114SCharles Chan                    lastContentList = null;
1925f0c5114SCharles Chan                    result.innerHTML = "";
1935f0c5114SCharles Chan                    step1();
1945f0c5114SCharles Chan                });
1955f0c5114SCharles Chan            }
1965f0c5114SCharles Chan        })();
1975f0c5114SCharles Chan        </script>';
1985f0c5114SCharles Chan        return true;
1995f0c5114SCharles Chan    }
2005f0c5114SCharles Chan
2015f0c5114SCharles Chan    // callOpenAI 逻辑将迁移到 action 处理
2025f0c5114SCharles Chan    // 保留接口以兼容
2035f0c5114SCharles Chan    private function callOpenAI($prompt, $params = []) {
2045f0c5114SCharles Chan        return '';
2055f0c5114SCharles Chan    }
2065f0c5114SCharles Chan
2075f0c5114SCharles Chan    private function formatResponse($text) {
2085f0c5114SCharles Chan        // 转换 Markdown 到 HTML
2095f0c5114SCharles Chan        $text = hsc($text); // HTML 安全转义
2105f0c5114SCharles Chan
2115f0c5114SCharles Chan        // 基础 Markdown 转换
2125f0c5114SCharles Chan        $text = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $text);
2135f0c5114SCharles Chan        $text = preg_replace('/\*(.+?)\*/', '<em>$1</em>', $text);
2145f0c5114SCharles Chan        $text = preg_replace('/`(.+?)`/', '<code>$1</code>', $text);
2155f0c5114SCharles Chan
2165f0c5114SCharles Chan        // 转换换行
2175f0c5114SCharles Chan        $text = nl2br($text);
2185f0c5114SCharles Chan
2195f0c5114SCharles Chan        return $text;
2205f0c5114SCharles Chan    }
2215f0c5114SCharles Chan}
222