package com.ch.jedge.jbot;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ch.jedge.jbot.corpus.CorpusTask;
import com.ch.jedge.jbot.dict.JEDictUtil;
import com.ch.jedge.jbot.dict.JedgeWordDict;
import com.ch.jedge.jbot.intent.JedgeIntentTester;
import com.ch.jedge.jbot.knbase.JedgeGptApp;
import com.ch.jedge.jbot.knbase.JedgeGptDialogEngine;
import com.ch.jedge.jbot.session.JedgeGdeKeywordPool;
import com.ch.jedge.jbot.session.JedgeServiceSession;
import com.changhong.jedge.*;
import com.changhong.qlib.QData;
import com.changhong.qlib.intf.QIData;
import com.changhong.qlib.intf.QIDataList;
import com.changhong.qlib.util.NumberUtils;
import com.changhong.qlib.util.StringUtils;
import com.changhong.qlib.util.TimeUtils;
import com.changhong.qlib.util.file.FileUtils;
import com.changhong.qlib.util.sync.SystemUtils;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;

import static com.ch.jedge.jbot.agent.JedgeGdeConsts.sval_default_app;
import static com.ch.jedge.jbot.JEJBotLLMConst.*;

public class JedgeBotGptService extends JMgbusService {

    protected final Map<String, JedgeWordDict> dict_ = new HashMap<>();
    protected final Map<String, JedgeServiceSession> sessions_ = new HashMap<>();
    protected final Queue<QIData> ret_msgs_ = new LinkedList<>();

    private boolean quit_ = false;
    private boolean active_ = false;
    private String defaultDomainLocal = default_vs_domain_local;
    private String defaultDomain = default_vs_domain_;
    private String kn_talk_log_file;
    private String talk_log_org_;
    protected int vecSize = 1536;
    protected int chatMode = 2;
    String knAsk_prompt;
    private final JedgeIntentTester domainTester;

    private String corpusPath = sval_corpus_cache_path_debug_;

    private final Map<String, CorpusTask>  corpus_set = new HashMap<>();  //记录语料数据记录

    protected final JedgeGptDialogEngine gde = new JedgeGptDialogEngine();

    double defaultMatch = 0.85;

    //基本词库
    //
    public JedgeBotGptService(JMgbusModual holder) {
        super(holder);

        domainTester = new JedgeIntentTester(module, this);

        auto_remove_call_info = false;
        String promptPath = FileUtils.contactPath("./", "cache", "prompt");
        String fn = FileUtils.contactPath(promptPath, "knAsk.txt");
        knAsk_prompt = StringUtils.readStringFromFile(fn);
        if(StringUtils.isNotValidStr(knAsk_prompt)) {
            knAsk_prompt = sval_prompt_kn_talk;
        }

        kn_talk_log_file = talk_log_org_ = FileUtils.contactPath("./", "logs");
        try {
            new File(talk_log_org_).mkdirs();
        } catch (Exception ignored) {}
        talk_log_org_ = FileUtils.contactPath(talk_log_org_,"userAsk.log");
        kn_talk_log_file = FileUtils.contactPath(kn_talk_log_file,"knTalk.log");

        //加载动态词库
        JEDictUtil.loadStaticCommonDicts(dict_, FileUtils.contactPath("./", "cfg", "dict", "common"));

        watchLocalEvent("OnModuleConnected", (w, keyVal, msg) -> {
            String host = msg.getString("host");
            int port = msg.getInteger("port");
            reactiveCommonDictionaries();
            return true;
        });
        watchLocalEvent("OnModuleDisconnected", (w, keyVal, msg) -> {
            String host = msg.getString("host");
            int port = msg.getInteger("port");
            return true;
        });

        domainTester.loadIntentDatasets(FileUtils.contactPath("./", "intents"));

        initJedgeGdeManager();
    }

    private void initJedgeGdeManager() {
        gde.setContext(module, this);
        gde.loadDialogDomains();
    }

    private void reactiveCommonDictionaries() {
        //将词库的所有词汇添加到jieba分词中
        synchronized (dict_) {
            for(JedgeWordDict d : dict_.values()) {
                activeWordDict(d);
//                SystemUtils.tryWait(50);
            }
        }
    }

    public void activeWordDict(JedgeWordDict dict) {
        if(dict==null) return;
        JSONArray items = dict.getWordItemsInJson(false);
        if(!items.isEmpty()) {
            postServiceMessage(sval_modual_word_cut, "/cw/word", new QData().put("w", items));
        }
    }

    @Override
    public void onServiceStart() {
        quit_ = false;
        module.postThread(this::postTextRetSourceMessage);
        module.postThread(this::clearTimeoutSession);
        module.postThread(this::knItemPoolHeartBeat);

        scanHistoryCorpus();

        super.onServiceStart();
    }

    private void scanHistoryCorpus() {
        List<File> files = new ArrayList<>();
        FileUtils.listDirFiles(corpusPath, file -> {
            if(file==null || !file.getName().endsWith(".json") || !file.exists())
                return null;
            files.add(file);
            return null;
        });

        // StringUtils::sortStringList(files);
        for(File f : files) {
            String corpusKey = FileUtils.getFilenameWithoutExt(f.getName());
            QIData d = QData.fromFile(f);
            if(d.isEmpty() || StringUtils.isNotValidStr(d.getKey())) continue;
            synchronized (corpus_set) {
                corpus_set.put(corpusKey, new CorpusTask(d));
            }
        }
    }

    private void knItemPoolHeartBeat() {
        module.warnLog("knItemPoolHeartBeat thread start ok.");
        while (!quit_) {
            SystemUtils.tryWaitforSingal(gde, 1000);
            try {
                gde.onHeartbeat();
                domainTester.onHeartbeat();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        module.warnLog("knItemPoolHeartBeat thread end.");
    }

    public JSONArray cutWords(String query) {
        if(query.contains("、")) {
            query = query.replaceAll("、","_");
        }
        QIData p = postServiceRequest(sval_modual_word_cut, "/cw/cut", new QData().put("t", query));
        if(JMgbusUtil.isMgbusResultOk(p)) {
            JSONArray re = p.getJsonArray("payload");
            if(re != null)
                return re;
        }
        return new JSONArray();
    }

    @Override
    public void onServiceStop() {
        quit_ = true;
        SystemUtils.notifyAllObject(sessions_);
        SystemUtils.notifyAllObject(ret_msgs_);
        SystemUtils.notifyAllObject(gde);
        //等待退出完成
        while(active_) {
            SystemUtils.tryWaitforSingal(ret_msgs_, 20);
        }
        super.onServiceStop();
    }

    //配置API
    @MgbusApi
    public QIData gptSet(QIData eMsg) {
        return transRequest(sval_default_jbot_jedge, eMsg);
    }

    //添加知识
    @MgbusApi
    public QIData knAdd(QIData req) {
        boolean hasUdKn = req.containsKey("kn") && req.get("kn") instanceof JSONArray;
        JedgeServiceSession s = findSession(req, "");
        int opr = req.getBoolean("del", false) ? 1:0;
        String key = req.getString("p");
        CorpusTask corpus= corpus_set.get(key);
        String orgSrc= req.getString("~s");
        if(corpus== null) {
            module.warnLog(String.format("Invalid corpus key when building vector data : %s", key));
            return JMgbusUtil.MgbusResult(401, String.format("No corpus by %s", key));
        }
        corpus.loadQuestions(corpusPath);  //重新载入
        if(!hasUdKn) { //读取本地文件中的语料，插入到milvus数据库
            module.postThread(() -> addKnowledges(corpus, s, corpus.getQAArray(), opr, false, orgSrc));
        } else { //从请求中添加指定语料
            //添加一条具体的知识
            module.postThread(() -> addKnowledges(corpus, s, (JSONArray) req.get("kn"), opr, true, orgSrc));
        }
        return JMgbusUtil.MgbusResult(200, "ok");
    }

    private void addKnowledges(CorpusTask corpus, JedgeServiceSession s, JSONArray knList, int opr, boolean updateVi, String srcMod) {
        int total = knList.size();
        int current = 1;
        int fail=0, succ=0;
        for(Object k : knList) {
            if(k instanceof JSONObject) {
                QIData rr = new QData((JSONObject) k);
                if (!rr.containsKey("q") || !rr.containsKey("a"))
                    continue;
                int id = rr.getInteger("id");
                String q = rr.getString("q");
                long oldVi =
                        updateVi?corpus.getCorpusVi(id):  //从本地读取vi，html提交的vi不可靠
                                rr.getLong("vi");
                //找到id的值
                if(oldVi == 0 ) {
                    module.warnLog(String.format("Invalid corpus id : %d, corpus [%s] aborted.", id, q));
                    continue;
                }
                if (q.contains(",")) {
                    String[] ql = q.split(",");
                    int i=0;
                    int subSize = ql.length;
                    for (String rq : ql) {
                        rr.putString("q", rq);
                        rr.put("vi", (oldVi));  //id偏移，无
                        i++;
                        if(addKnowledge(s, rq, rr, opr, current, total, i, subSize, srcMod)) {
                            succ++;
                        } else fail++;
                    }
                } else {
                    rr.put("vi", oldVi);
                    if(addKnowledge(s, q, rr, opr, current, total, 1, 1, srcMod)){
                        succ++;
                    } else fail++;
                }
                current++;
            }
        }
        if(opr==0)
            module.infoLog("All KN items add.");
        else
            module.infoLog("All KN items removed.");

        QIData r = new QData();
        r.clear();
        if(StringUtils.isValidStr(srcMod))
            r.putString("~s", srcMod);
        r.putInteger("t", total);
        r.putInteger("c", current-1);
        r.putInteger("succ", succ);
        r.putInteger("fail", fail);
        r.putString("msg", String.format("总共%d个,%d个成功,%d个失败.", total, succ, fail));
        postServiceMessage(s.srcMod,"/gpt/OnMessageAddEnd", r);
    }

    private boolean addKnowledge(JedgeServiceSession s, String qKey, QIData msg,
                                 int opr, int current, int total,
                                 int subIndex, int subTotal, String srcMod) {
        //检查Key是否已经存在，如果存在，则可能覆盖这条知识点
        msg.putString("r", s.domain);
        msg.putBoolean("d", true); //检查如果已存在、且数据发生变化,则删除原有向量，很可能是更新。
        msg.putString("n", s.currentKnBase);

        QIData r = new QData();
        r.putInteger("t", total);
        r.putInteger("c", current);
        r.putInteger("si", subIndex);
        r.putInteger("st", subTotal);
        if(StringUtils.isValidStr(srcMod)) r.putString("~s", srcMod);

        /*
        TODO::处理注释
        String a = msg.getString("a");
        while(a.contains("{{{") && a.contains("}}}")) {
            //替换备注
            String sub = StringUtils.getSubStrBetween(a, '{',1 , '}', 1);
            if(StringUtils.isValidStr(sub)) {
                if(sub.startsWith("{{") && sub.endsWith("}}")) {
                    a.replaceAll()
                }
            }
        }
        */
        //生成当前聊天过程中最合理的知识组合Key
        List<String> keyList = checkKnowledgeKey(qKey);
        //对这个keyList进行处理，如果中间包含了关联关系，则将关联词汇建立推理关系。
        if(keyList.size()>0) {
            module.exMarkLog(String.format("具有关联关系的词:%s", StringUtils.ListToString(keyList,',')));
            //如何建立推理知识库:通过@@@标识，在读取到这样的数据后，再次通过@@@进行读取。
            //TODO::针对关联词汇，自动建立推理数据（当对话的过程中，关联词与主key的距离过大、且未获得数据时，可直接通过推理获得关联词）
        }
        if(keyList.isEmpty()) { //未检测出有效关键字
            return true;
        } else {
            int knKey = 0;
            for(String realKey : keyList) {
                knKey ++;
                r.putInteger("ki", knKey);
                msg.putString("q", realKey);
                if(opr==0 && existsKnowledge(realKey, msg)) {
                    r.putString("m", opr==0?"插入成功":"删除成功");
                    postServiceMessage(s.srcMod,"/gpt/OnMessageAdd", r);
                    module.infoLog(String.format("Discard adding knowledge already exists : %s", qKey));
                    return true;
                } else if(opr==1 && !existsKnowledge(realKey, msg)) {
                    r.putString("m", opr==0?"插入成功":"删除成功");
                    postServiceMessage(s.srcMod,"/gpt/OnMessageAdd", r);
                    module.infoLog(String.format("Discard remove knowledge do not exist : %s", qKey));
                    return true;
                }

                r.putString("n", s.currentKnBase);
                String uri = opr==0?"/gv/insert":"/gv/remove";
                if(opr==0) {
                    //获得q的向量
                    JSONArray vec = getVectorOfString(realKey);
                    if (vec==null) {
                        r.putString("m", "该条知识文本向量获取失败");
                        postServiceMessage(s.srcMod, "/gpt/OnMessageAdd", r);
                        return false; //获取向量失败
                    }
                    //插入一个基于问题的id的Hash值
                    msg.put("v", vec);
                    msg.putBoolean("nec", true); //不做存在性检查
                }

                //加入的时间策略间隔
                long tm = (opr == 0) ? new Date().getTime() : 0;
                //mgbus的知识库服务module
                QIData rsp = postServiceRequest(sval_modual_vec_db, uri,msg);
                module.highLog(String.format("%s result : %s", (opr == 0)?"添加":"删除",rsp.toJSONString()));
                boolean re = JMgbusUtil.isMgbusResultOk(rsp);
                String mm = String.format("%s%s", opr==0?"插入":"删除", re?"成功":"失败");
                r.putString("m", mm);
                postServiceMessage(s.srcMod,"/gpt/OnMessageAdd", r);
                if(opr == 0 && (current<total || subIndex!=subTotal)) {
                    tm = new Date().getTime() - tm;
                    if (tm < lval_min_time_gap)
                        SystemUtils.tryWait((int) (lval_min_time_gap - tm));
                }
                if(!re)
                    module.warnLog(String.format("插入知识文本向量获取失败 : key is %s", qKey));
                return re;
            }
        }
        return true;
    }

    private boolean existsKnowledge(String qKey, QIData knData) {
        String uri = "/gv/exists";
        //http的知识库服务module
        QIData p = postServiceRequest(sval_modual_vec_db, uri, knData);
        if(JMgbusUtil.isMgbusResultOk(p)) {
            return true;
        }
        module.warnLog(String.format("数据不存在 %s", qKey));
        return false;
    }

    //查询知识条目
    @MgbusApi
    public QIData knQry(QIData eMsg) {
        return transRequest(sval_default_jbot_jedge, eMsg);
    }

    //删除知识条目
    @MgbusApi
    public QIData knDel(QIData eMsg) {
        eMsg.putBoolean("del", true);
        return knAdd(eMsg);
    }

    private QIData transRequest(String mod, QIData eMsg) {
        String srcMod = JMgbusUtil.GetMgModSource(eMsg);
        eMsg.removeKey("~c");
        eMsg.putString("~s", srcMod);
        //~d表面直接发送、禁止被抢占式发送。
        return postServiceRequest(mod, eMsg.getString("uri"), eMsg.putBoolean("~d", true));
    }

    //切换知识库（默认，如果不存在则添加）
    @MgbusApi
    public QIData knSwitch(QIData req) {

        JedgeServiceSession s = findSession(req, "");

        String name = req.getString("knn");
        if(StringUtils.isNotValidStr(name) && !StringUtils.isValidStr(s.currentKnBase)) {
            name = s.currentKnBase;
        }

        if(StringUtils.isNotValidStr(name)) {
            return JMgbusUtil.MgbusResult(401,"No target db name");
        }
        boolean local = req.getBoolean("l", false);
        if(!local)
            name +="_CGPT";
        QIData msg = new QData();
        msg.setName(name);
        //mgbus的知识库服务module
        boolean autoCr = req.getBoolean("c", false);
        QIData rsp = JMgbusUtil.MgbusResult(200,"ok");
        QIData p = postServiceRequest(sval_modual_vec_db, autoCr?"/gv/switchDb":"/gv/findDb", msg);
        if(JMgbusUtil.isMgbusResultOk(p)) {
            module.highLog(String.format("Switch result : %s", p.toJSONString(true)));
            rsp.putLong("sid", s.sid);
            return rsp;
        } else
            return JMgbusUtil.MgbusResult(401,String.format("Switch to database %s failed", req.getName()));
    }

    @MgbusApi
    public QIData knDbDel(QIData req) {
        JedgeServiceSession s = findSession(req,"");

        String name = req.getString("knn");
        if(StringUtils.isNotValidStr(name) && StringUtils.isValidStr(s.currentKnBase)) {
            name = s.currentKnBase;
        }

        if(StringUtils.isNotValidStr(name)) {
            return JMgbusUtil.MgbusResult(401,"No target db name");
        }

        QIData msg = new QData();
        msg.setName(name);
        QIData p = postServiceRequest(sval_modual_vec_db, "/gv/dropDb", msg);
        String m;
        if(!JMgbusUtil.isMgbusResultOk(p)) {
            m = String.format("Drop kn database %s failed.", name);
        } else {
            m = String.format("Drop kn database %s ok.", name);
        }

        name +="_CGPT";
        msg.setName(name);
        p = postServiceRequest(sval_modual_vec_db, "/gv/dropDb", msg);
        String mm;
        if(!JMgbusUtil.isMgbusResultOk(p)) {
            mm = String.format("Drop kn database %s failed.", name);
        } else {
            mm = String.format("Drop kn database %s ok.", name);
        }
        m+=("\n");
        m+=(mm);
        return JMgbusUtil.MgbusResult(200,m);
    }

    //切换知识库（默认，如果不存在则添加）
    @MgbusApi
    public QIData knTalk(QIData req) {
        String prompt = req.getString("text");
        module.SimpleMark("用户输入>>" + prompt);
        if(StringUtils.isNotValidStr(prompt)) {
            prompt = req.getObjSimpleFmtString("param.text");
            if(StringUtils.isNotValidStr(prompt)) {
                return JMgbusUtil.MgbusResult(401,"未知输入");
            }
        }

        JedgeServiceSession s = findSession(req, prompt);

        if(!s.isStream) {  //非流式请求，目前只针对chatGPT
            return  makeGptFastCall(s, prompt);
        }

        String finalPrompt = prompt;
//        module.postThread(() -> {
////            if(GdeTalk(s, finalPrompt)) {
////                return;
////            }
////
            if(s.knTalk) {
                module.highLog(String.format("GPT Prompt>>%s", finalPrompt));
//                if (JedgeGptApp.isDebugMode())
//                    returnMgbusMessage(s.srcMod, s.cbUri, "正在处理...\n", false);
                //第一步、提取意图和知识关键字；
                QIData intent = checkWordsOfIntent(s, s.currentPrompt);
                knTalkLog(finalPrompt);
                if (intent != null) {
                    if (JMgbusUtil.isMgbusResultOk(intent)) {
                        String returnPrompt = String.format("你是一个人工智能助手,用户输入:%s,请只根据系统的返回内容:%s,为用户输入生成一个直接的极简答复,不要生成其它内容。", finalPrompt, intent.getString("msg"));
//                        makeRealGptStreamCall(s, returnPrompt);
                        if (JedgeGptApp.isDebugMode() && !s.voiceTalk)
                            returnMgbusMessage(s.srcMod, s.cbUri, "[动作执行完毕]", true);
                    } else if(intent.getInteger("code") == 601)  {
                        if (JedgeGptApp.isDebugMode() && !s.voiceTalk)
                            returnMgbusMessage(s.srcMod, s.cbUri, "[模型直问]", true);
                        // makeRealGptStreamCall(s, finalPrompt);
                    } else
                        returnMgbusMessage(s.srcMod, s.cbUri, "[动作失败]", true);
                } else {
                    if (JedgeGptApp.isDebugMode() && !s.voiceTalk)
                        returnMgbusMessage(s.srcMod, s.cbUri, "[模型直问]", true);
                        //makeRealGptStreamCall(s, finalPrompt);
                }
            } else {
                knTalkLog(finalPrompt);
                makeRealGptStreamCall(s, finalPrompt);
            }
            s.getGdtSession().refreshQARange();     //刷新聊天距离
//        });

        QIData resp = JedgeLlmUtil.simpleOk();
        resp.put("sid", s.sid);
        return resp;
    }

    private QIData checkWordsOfIntent(JedgeServiceSession s, String finalPrompt) {
        JedgeIntentTester dmTester = domainTester;
        String lastDomain = null;
        JedgeIntentTester lastDmTester = null;
        QIData domainResult = null;
        int failCount = 0;
        while(dmTester!=null) {
            String newPrompt = domainTester.getIntentTestPrompt(finalPrompt);
            domainResult = getJsonResultForPrompt(s, newPrompt);
            lastDmTester = dmTester;
            if(domainResult!=null && domainResult.containsKey("domain")) {  //子分类为其它时，直接在当前的domin
                String domain = domainResult.getString("domain");
                if (JedgeGptApp.isDebugMode() && !s.voiceTalk)
                    returnMgbusMessage(s.srcMod, s.cbUri, String.format("你想要:%s\n", domain), false);
                lastDomain = domain;
                dmTester = domainTester.getSubDomainTester(domain);
                if (dmTester == null) {
                    break;   //不存在子分类
                }
            } else {
                failCount ++;
                if(failCount>3)
                    break;
            }
        }

        if(StringUtils.isValidStr(lastDomain)) {
            JedgeIntentTester.JedgeDomainDef tester = lastDmTester.getIntentTester(lastDomain);
            //获得知识增强(通过domain中的Key)
            //可通过额外的知识获得增强
            //获取意图领域，并识别准确的意图
            if(tester!=null) {
                // 获得子分类器,如果有则继续分类,如果没有,则调用intent生成器来完成生成。
                String newPrompt = tester.getIntentClassificationTemplate(finalPrompt);
                QIData finalResult = getJsonResultForPrompt(s, newPrompt);      //提取参数
                if(finalResult instanceof QIDataList) {
                    QIData p = null;
                    for(Object inst : ((QIDataList) finalResult).asJsonArray()) {
                        if(inst instanceof JSONObject) {
                            QIData req = new QData((JSONObject) inst);
                            if(domainResult!=null) {
                                req.copyFrom(domainResult, "exKeyWords");
                            }
                            req.putString("query", finalPrompt);
                            if(JedgeGptApp.isDebugMode() && !s.voiceTalk)
                                returnMgbusMessage(s.srcMod, s.cbUri, String.format("生成命令(%s)...\n", req), false);
                            module.warnLog(String.format("生成指令:%s", req));
                            p = tester.makeActionCall(req);
                            if(JedgeGptApp.isDebugMode() && !s.voiceTalk)
                                returnMgbusMessage(s.srcMod, s.cbUri, String.format("执行%s.\n", JMgbusUtil.isMgbusResultOk(p)?"成功":"失败"), false);
                        }
                    }
                    return p;
                } else if(finalResult != null) {
                    if(JedgeGptApp.isDebugMode() && !s.voiceTalk)
                        returnMgbusMessage(s.srcMod, s.cbUri, String.format("生成命令(%s)...\n", finalResult), false);
                    if(domainResult!=null) {
                        finalResult.copyFrom(domainResult, "exKeyWords");
                    }
                    finalResult.putString("query", finalPrompt);
                    QIData p = tester.makeActionCall(finalResult);
                    if(JedgeGptApp.isDebugMode() && !s.voiceTalk)
                        returnMgbusMessage(s.srcMod, s.cbUri, String.format("执行返回(%s)...\n", p.getString("msg")), false);
                    return p;
                }
            }
        }
        return null;
    }

    private boolean GdeTalk(JedgeServiceSession s, String finalPrompt) {
        return gde.handleKnTalk(s, s.currentKnBase, finalPrompt);
    }

    public String getKnAskPrompt() {
        return knAsk_prompt;
    }

    @MgbusApi
    public QIData knTalkNew(QIData eMsg) {
        return JMgbusUtil.MgbusResult(200,"ok");
    }

    @MgbusApi
    public QIData OnMessageAdd(QIData req) {
        String srcMod = req.getString("~s");
        if(StringUtils.isValidStr(srcMod))
            req.removeKey("~s");
        postServiceMessage(srcMod, req.getString("uri"), req);
        return null;
    }

    @MgbusApi
    public QIData OnMessageAddEnd(QIData req) {
        String srcMod = req.getString("~s");
        if(StringUtils.isValidStr(srcMod))
            req.removeKey("~s");
        postServiceMessage(srcMod, req.getString("uri"), req);
        return null;
    }

    //为特定的domain注册新的API，
    @MgbusApi
    public QIData registerAPI(QIData req) {
        //domain的关键字
        return domainTester.addNewDomain(req);
    }

    @MgbusApi
    public QIData removeAPI(QIData req) {
        return domainTester.removeDomain(req);
    }

    @MgbusApi
    public QIData registerDomain(QIData req) {
        return domainTester.addNewDomain(req);
    }

    @MgbusApi
    public QIData updateDataset(QIData req) {
        return domainTester.updateDataset(req);
    }

    @MgbusApi
    public QIData registerDataset(QIData req) {
        return domainTester.addNewDataSet(req);
    }

    @MgbusApi
    public QIData removeDomain(QIData req) {
        return domainTester.removeDomain(req);
    }

    @MgbusApi
    public QIData LocalStream(QIData req) {
        String sid = req.getString("sid");
        String text = req.asJsonObject().getString("delta");
        boolean isEnd = req.getBoolean("finished", false);
        JedgeServiceSession session;
        synchronized (sessions_) {
            session = sessions_.get(sid);
        }
        if(session != null) {
            if(session.printSingle) {
                module.infoLog(String.format("Gpt>>%s(%s)", text, isEnd));
                if (isEnd) {
//                    pullKnowledgeFromResponse(session, session.currentResponse);
                    session.buildLastHistory();
                } else {
                    //text = text.trim();
                    if(StringUtils.isValidStr(text))
                        session.append2LastQA(text);
                }
                if (session.srcMod != null && session.cbUri != null) {
                    returnGptResponseMessage(session.srcMod, session.cbUri, text, isEnd, false);
                } else
                    module.warnLog(String.format("Unknown mod to return, discussed : %s", text));
            } else {
                text += ("\n");
                module.warnLog(String.format("Unhandled yet content : %s", text));
            }
        } else {
            module.warnLog(String.format("Dropping no session return content : %s", text));
        }
        return JMgbusUtil.MgbusResult(200,"ok");
    }

    private void pullKnowledgeFromResponse(JedgeServiceSession s, String currentResponse) {
        JSONArray results = cutWords(currentResponse);
        JedgeGdeKeywordPool pool = s.gdeSession.getKnPool();
        //分词后，进行联想，扩充这个词库
        List<String> knKeyList = pool.getTypeKeyItems(results, val_verb_type_user_defined_noun);
//        List<String> relKeyList = pool.getTypeKeyItems(results, sval_verb_type_user_defined_relation_noun);
//        List<String> joinKeyList = pool.getTypeKeyItems(results, sval_verb_type_user_defined_join_noun);
        //解决
        for(String mkey : knKeyList) {
//            String content =
            pool.getKnowledgeFromMilvus(s, mkey, false);
//            module.highLog(String.format("Recall info from answer(%s) : %s", mkey, content));
        }
    }

    @MgbusApi
    public QIData cutStream(QIData eMsg) {
        return transRequest(sval_default_jbot_jedge, eMsg);
    }

    @MgbusApi
    public QIData createNewSet(QIData eMsg) {
        String text = eMsg.getString("text");
        if(StringUtils.isNotValidStr(text)) {
            return JMgbusUtil.MgbusResult(401, "No text when create corpus");
        }
        //如果输入语料文本过短,则是没有意义的。
        String taskKey = eMsg.getString("key");
        if(StringUtils.isNotValidStr(taskKey))
            taskKey = StringUtils.randomChars(8);
        QIData d = new QData();
        d.setKey(taskKey);
        String name = "未命名语料-" + taskKey;
        d.setName(eMsg.getString("name", name));
        d.putString("t", text);
        d.setKey(taskKey);
        CorpusTask corpus = new CorpusTask(d);
        corpus.saveDefineData(corpusPath); //保存语料的基本信息
        corpus_set.put(taskKey, corpus);
        QIData rsp = JMgbusUtil.MgbusResult(200, String.format("create OK with key : %s", taskKey));
        rsp.setKey(taskKey);
        rsp.setName(name);
        return rsp;
    }

    @MgbusApi
    public QIData appendCut(QIData eMsg) {
        return transRequest(sval_default_jbot_jedge, eMsg);
    }

    @MgbusApi
    public QIData downCorpus(QIData eMsg) {
        return transRequest(sval_default_jbot_jedge, eMsg);
    }

    @MgbusApi
    public QIData corpusList(QIData eMsg) {
        if(local_corpus_) {
            //获取当前所有可用的语料数据的信息
            JSONArray payload = new JSONArray();
            synchronized (corpus_set) {
                //todo:: 按title排序
                List<String> kk = new ArrayList<>(corpus_set.keySet());
                Collections.sort(kk);
                for(String key : kk) {
                    CorpusTask corpus = corpus_set.get(key);
                    JSONObject obj = new JSONObject();
                    obj.put("key", corpus.getKey());
                    obj.put("name", corpus.getName());
                    payload.add(obj);
                }
            }
            QIData rsp = JMgbusUtil.MgbusResult(200,"ok");
            rsp.put("payload", payload);
            return rsp;
        }
        return transRequest(sval_default_jbot_jedge, eMsg);
    }

    @MgbusApi
    public QIData corpusRemove(QIData req) {
        String key = req.getString("key");
        CorpusTask corpus= corpus_set.remove(key);
        if(corpus== null) {
            return JMgbusUtil.MgbusResult(401, String.format("No corpus by %s", key));
        }
        corpus.removeCorpus(corpusPath);
        return JMgbusUtil.MgbusResult(200, "ok");
    }

    @MgbusApi
    public QIData corpusChangeName(QIData req) {
        String key = req.getString("key");
        String name = req.getName();
        if(StringUtils.isNotValidStr(name)) {
            return JMgbusUtil.MgbusResult(401, "No new name");
        }
        CorpusTask corpus= corpus_set.get(key);
        if(corpus== null) {
            return JMgbusUtil.MgbusResult(401, String.format("No corpus by %s", key));
        }
        corpus.setName(name);
        corpus.saveDefineData(corpusPath);
        return JMgbusUtil.MgbusResult(200, "ok");
    }

    @MgbusApi
    public QIData saveCorpusData(QIData req) {
        String key = req.getString("key");
        String text =req.getString("text");
        CorpusTask corpus= corpus_set.get(key);
        if(corpus== null) {
            return JMgbusUtil.MgbusResult(401, String.format("No corpus by %s", key));
        }
        if(StringUtils.isValidStr(text) && corpus.updateText(text)) {
            corpus.saveDefineData(corpusPath);
        }
        return JMgbusUtil.MgbusResult(200, "ok");
    }

    @MgbusApi
    public QIData updateCorpus(QIData eMsg) {
        String key = eMsg.getString("key");
        CorpusTask corpus= corpus_set.get(key);
        if(corpus== null) {
            return JMgbusUtil.MgbusResult(401, String.format("No corpus by %s", key));
        }
        int idx = eMsg.getInteger("id");
        int cmd = eMsg.getInteger("cmd");
        //1-删除
        switch(cmd) {
            case -1: {
                String ask = eMsg.getString("q");
                String ans = eMsg.getString("a");
                if(corpus.updateQuestion(idx, ask, ans))   //如果idx为空,则创建
                    corpus.saveQuestions(corpusPath);
            }
            break;
            case 1:
                if(corpus.removeQuestion(idx))
                    corpus.saveQuestions(corpusPath);
                else {
                    return JMgbusUtil.MgbusResult(401, String.format("Fail to remove knowledge id : %d", idx));
                }
                break;
            default:
                return JMgbusUtil.MgbusResult(401, String.format("Unknown update command : %d", cmd));
        }
        return JMgbusUtil.MgbusResult(200, "ok");
    }

    @MgbusApi
    public QIData getVecDbList(QIData eMsg) {
        //获取当前的app列表
        if(old_vdb_mode_) {
            //获取当前所有可用的语料数据的信息
            QIData p = postServiceRequest(sval_modual_vec_db, "/gv/listDb", new QData());
            QIData rsp;
            if(!JMgbusUtil.isMgbusResultOk(p) ) {
                return JMgbusUtil.MgbusResult(201,"Fail request listDb");
            }
            module.highLog(String.format("result:%s", p.toJSONString()));
            rsp = JMgbusUtil.MgbusResult(200,"ok");
            rsp.copyFrom(p, "payload");
            return rsp;
        }

        //获取当前所有可用的语料数据的信息
        QIData p = new QData();
        // QIData p = postServiceRequest(sval_modual_vec_db, "/gv/listDb", new QData());
        if(!JMgbusUtil.isMgbusResultOk(p) ) {
            QIData rsp;
            rsp = JMgbusUtil.MgbusResult(200,"ok");
            rsp.put("payload", gde.getGdeAppList());
            return rsp;
//            return JMgbusUtil.MgbusResult(401, "fail request listDb");
        }
        module.printClientLog(String.format("result:%s", p.toJSONString()));
        QIData rsp;
        rsp = JMgbusUtil.MgbusResult(200,"ok");
        rsp.copyFrom(p, "payload");
        return rsp;
    }

    @MgbusApi
    public QIData syncCorpusInfo(QIData req) {
        if(local_corpus_) {
            String key = req.getString("key");
            CorpusTask corpus;
            synchronized (corpus_set) {
                corpus = corpus_set.get(key);
            }
            if (corpus == null) {
                return JMgbusUtil.MgbusResult(201, String.format("No corpus by %s", key));
            }
            QIData rsp = JMgbusUtil.MgbusResult(200, "Ok");
            rsp.putString("text", corpus.getBaseText());
            if (corpus.empty())
                corpus.loadQuestions(corpusPath);
            JSONArray arr = corpus.getQAArray();
            if (arr.size() <= ival_max_kn_items) {
                rsp.put("payload", arr);
                return rsp;
            }
            JSONArray newArr = new JSONArray();
            JSONArray postArr = new JSONArray();
            int i = 0;
            for (Object a : arr) {
                if (i < ival_max_kn_items)
                    newArr.add(a);
                else
                    postArr.add(a);
                i++;
            }
            rsp.put("payload", newArr);
            String jeMod = JMgbusUtil.GetMgModSource(req);
            String jeUriCb = req.getString("cb");
            //先返回一批过去，然后再分批次推送过去
            module.postThread(() -> {
                for (Object o : postArr) {
                    String msg = new QData((JSONObject) o).toJSONString();
                    SystemUtils.tryWait(5);
                    // returnMgbusMessageWithKey(corpus.getKey(), jeMod, jeUriCb, msg, false);
                    returnMgbusMessageWithKey(corpus.getKey(), jeMod, jeUriCb, msg, false, false);
                }
            });
            return rsp;
        }
        return transRequest(sval_default_jbot_jedge, req);
    }

    protected void postTextRetSourceMessage() {
        active_ = true;
        module.warnLog("Posting thread start ok.");
        while(!quit_) {
            try {
                QIData msg = null;
                {
                    if(!ret_msgs_.isEmpty()) {
                        msg = ret_msgs_.poll();
                    }
                }
                if(msg!= null) {
                    String jeMod = msg.getString("~m");
                    String jeUriCb = msg.getString("~u");
                    postServiceMessage(jeMod, jeUriCb, msg);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            synchronized (ret_msgs_) {
                SystemUtils.tryWaitforSingal(ret_msgs_, ival_mufis_text_message_gap_time_);
            }
        }
        module.warnLog("Posting thread end.");
        active_ = false;
    }

    protected void clearTimeoutSession() {
        module.warnLog("clearTimeoutSession thread start ok.");
        while (!quit_) {
            synchronized (sessions_) {
                List<String> toDel = new ArrayList<>();
                Set<String> keys = sessions_.keySet();
                for(String k : keys) {
                    JedgeServiceSession s = sessions_.get(k);
                    s.ttl ++;
                    if(s.ttl > JEJBotLLMConst.ival_timeout_session_ ) {        //会话3分钟没有任何返回,清空
                        toDel.add(k);
                        module.highLog(String.format("Timeout talk session %s from %s", k, s.srcMod));
                        // returnMgbusMessage(s.srcMod, s.cbUri, "会话超时", true, true);
                    } else {
                        //会话池心跳
                        try {
                            if(s.ttl % 5 ==0 )
                                s.getGdtSession().knItemPoolHeartBeat();
                        }catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                for(String key : toDel)
                    sessions_.remove(key);
            }
            SystemUtils.tryWaitforSingal(sessions_, 100);
        }
        module.warnLog("clearTimeoutSession thread end.");
    }

    public void returnMgbusMessage(String srcMod, String cbUri, String txtMessage, boolean isEndMsg) {
        returnMgbusMessage(srcMod, cbUri, txtMessage, isEndMsg, false);
    }

    public void returnMgbusMessage(String srcMod, String cbUri, String txtMessage, boolean isEndMsg, boolean isErr) {
        module.exMarkLog(String.format("GPT>>%s",txtMessage));
        QIData msg = new QData();
        msg.putString("msg", txtMessage);
        if(isEndMsg)
            msg.putBoolean("end", isEndMsg);
        if(isErr)
            msg.putBoolean("err", isErr);
        QIData d = msg.getData("~c");
        if(d==null || d.isEmpty()) {
            d = new QData();
        }
        d.put("nb",true);
        msg.put("~c", d);
        postServiceMessage(srcMod, cbUri, msg);
    }

    public void returnMgbusMessageWithKey(String key, String jeMod, String jeUriCb, String txtMessage,
                                          boolean isEndMsg , boolean isErr ) {
        module.exMarkLog(String.format("Corpus More >>%s",txtMessage));
        QIData msg = new QData();
        msg.setKey(key);
        msg.putString("msg", txtMessage);
        if(isEndMsg)
            msg.putBoolean("end", isEndMsg);
        if(isErr)
            msg.putBoolean("err", isErr);
        QIData d = msg.getData("~c");
        if(d==null || d.isEmpty()) {
            d = new QData();
        }
        d.put("nb",true);
        msg.put("~c", d);
        postServiceMessage(jeMod, jeUriCb, msg);
    }

    public void returnGptResponseMessage(String srcMod, String cbUri, String txtMessage, boolean isEndMsg, boolean isErr) {
//        if(txtMessage!=null)
//            module.exMarkLog(String.format("GPT>>%s",txtMessage));
        QIData msg = new QData();
        msg.putString("msg", txtMessage);
        if(isEndMsg)
            msg.putBoolean("end", isEndMsg);
        if(isErr)
            msg.putBoolean("err", isErr);
        QIData d = msg.getData("~c");
        if(d==null || d.isEmpty()) {
            d = new QData();
        }
        d.put("nb",true);
        msg.put("~c", d);
        msg.putString("~m", srcMod);
        msg.putString("~u", cbUri);
        synchronized (ret_msgs_) {
            ret_msgs_.add(msg);
            ret_msgs_.notify();
        }
    }

    public JSONArray getVectorOfString(String prompt) {
        //计算prompt的请求,结果返回到 vecArr中
        //发起本地embedding的请求
        QIData r = new QData();
        r.putString("text", prompt);
        if(vecSize>0)
            r.putInteger("size", vecSize);
        QIData p = postServiceRequest(sval_module_llm,"/glm6b/embedding", r);
        if(JMgbusUtil.isMgbusResultOk(p)) {
            return p.getJsonArray("embedding");
        }
        return null;
    }

    public void makeRealGptStreamCall(JedgeServiceSession s, String realPrompt) {
        //发起gpt的stream请求
        // chatGLM的聊天
        // // "history":[["你好","你好👋！我是人工智能助手 ChatGLM-6B，很高兴见到你，欢迎问我任何问题。"]
        QIData gr = new QData();
//        gr.put("history", s.history_);
        gr.put("query", realPrompt);
        gr.put("cb",  "/gpt/LocalStream");//本地stream返回
        gr.put("sid", s.sid);
        QIData p = postServiceRequest(sval_module_llm, "/glm6b/stream", gr);
        if (!JMgbusUtil.isMgbusResultOk(p)) {
            returnMgbusMessage(s.srcMod, s.cbUri, "中转服务器请求Stream GPT数据失败", true, true);
        }
    }

    public QIData getJsonResultForPrompt(JedgeServiceSession s, String realPrompt) {
        String result = makeRealGptChatCall(s, realPrompt);
        if(result!=null) {
            result = result.trim();
            result = JedgeLlmUtil.unescapeString(result);
            QIData re = QData.fromString(result);
            if(!re.isEmpty()) {
                return re;
            } else {
                result = JedgeLlmUtil.unescapeString(result);
                re = QData.fromString(result);
                if(!re.isEmpty())
                    return re;
            }
            String newResult = StringUtils.getSubStrBetween(result, '{',1,'}',-1);
            re = QData.fromString(newResult);
            if(!re.isEmpty())
                return re;
            newResult = StringUtils.getSubStrBetween(result, '[',1,']',-1);
            re = QData.fromString(newResult);
            if(!re.isEmpty())
                return re;
        }
        return null;
    }

    public String makeRealGptChatCall(JedgeServiceSession s, String realPrompt) {
        QIData gr = new QData();
        gr.put("history", s.history_);
        gr.put("query", realPrompt);
        gr.put("cb",  "/gpt/LocalStream");//本地stream返回
        gr.put("sid", s.sid);
        JMgbusUtil.setRequestTimeout(gr,60000);
        QIData p = postServiceRequest(sval_module_llm, "/glm6b/chat", gr);
        if (!JMgbusUtil.isMgbusResultOk(p)) {
            returnMgbusMessage(s.srcMod, s.cbUri, "中转服务器请求Chat GPT数据失败", true, true);
            return null;
        } else {
            return p.getString("r");
        }
    }

    private String makeRealPromptFromSessionContext(JedgeServiceSession s, String prompt) {
        return makeRealPromptFromSessionContext(s, prompt, false);
    }

    private String makeRealPromptFromSessionContext(JedgeServiceSession s, String prompt, boolean helping) {
        if(s.knTalk) {
            //针对知识库的聊天流程:
            /**
             *  从上下文的切片中提取所需要的词库,提交给jieba，优化分词。
             *  第一, 提取分词,  提取重要关键字
             *  第二, 对新增关键字，通过main+relation的多项组合、通过相似度去命中知识,
             *  第三, 更新池中的知识
             *  第四, 组合当前语句的意图组合（缺省的内容，相似度组合）， 并利用相似度去命中意图
             *
             */
            return s.getGdtSession().getComposedPromptByActiveKey(s, prompt, helping);
        } else {
            return prompt;
        }
    }

    public String getKnowledgeFromMilvus(JedgeServiceSession s, String knKey, boolean helping) {
        //
        JSONArray vecArr = getVectorOfString(knKey);
        if(vecArr==null || vecArr.isEmpty()) {
            if(chatMode==0) { //继续聊天
                returnMgbusMessage(s.srcMod, s.cbUri, "我不确定我说的是不是对的，不过，", true);
                return knKey;
            } else if (chatMode==2) { //中断
                knTalkLog(knKey, false);
                returnMgbusMessage(s.srcMod, s.cbUri, "文本向量获取失败", true, true);
                return null; //获取向量失败
            }
        }

        //需要获得推理的引用，并需要把推理的结果推到

        List<String> rr = getKnowledgeFromMilvus(s, vecArr);
        if(rr==null || rr.isEmpty()) return null;

        //
        StringBuilder context = new StringBuilder(rr.get(0));
        if(rr.size()>1) {
            //执行推理
            String word = rr.get(1);
            String[] predicts = word.split(sval_predict_split);
            for(String prKey : predicts) {
                if(prKey.startsWith(sval_predict_split))
                    prKey = prKey.substring(sval_predict_split.length());
                if(StringUtils.isNotValidStr(prKey))
                    continue;
                String result = getKnowledgeFromMilvus(s, prKey, helping);
                if(StringUtils.isValidStr(result))
                    context.append(result);//追加推理后的内容
            }
        }
        if(context.length() == 0) {
            if(s.history_.isEmpty()) {  //未检索出有效信息
                if(chatMode==0) {   //开放对话模式
                    //返回不可信的提示:
                    //returnMgbusMessage(s.srcMod, s.cbUri, "我不确定我说的是不是对的，不过，", true);
                    //清除对话历史
                    s.clearHistory();
                    return knKey;
                } else if(chatMode==1) {    //知识库对话模式
                    //给出对话引导
                    knTalkLog(knKey, false);
                    //递归，命中消息帮助消息
                    String realPrompt = makeRealPromptFromSessionContext( s, "帮助", true);
                    if(realPrompt==null || realPrompt.isEmpty()) {
//                        if(!helping)
//                            returnMgbusMessage(s.srcMod, s.cbUri, "您可以对我说，帮助，或者说点什么", true);
                    }
                    return realPrompt;
                } else { //mode==2
                    //returnMgbusMessage(s.srcMod, s.cbUri, "系统好像没有您要问的答案，请换一个话题试试？", true);
                    return null;
                }
            } else {
                //从历史信息中提取内容
                //TODO::如果没用命中问题，提供一个当前的最大可能的默认知识体系？
                for (Object v : s.history_) {
                    if(v instanceof JSONArray && ((JSONArray) v).size()>1 && ((JSONArray) v).get(1) instanceof String) {
                        context.append(((JSONArray) v).get(1));
                    }
                }
                if(context.toString().isEmpty()) {
                    knTalkLog(knKey, false);
                    returnMgbusMessage(s.srcMod, s.cbUri, "系统好像没有您要问的答案，请换一个话题试试？", true);
                    return null;
                }
                //拼装一个新的prompt
                return String.format(knAsk_prompt, "未知", context, "大模型", knKey);
            }
        } else {
            if(context.charAt(0)=='@' && context.charAt(1)=='<') {
                String reTxt = randomFromFixResult(context.toString());
                return String.format(sval_prompt_kn_explain, reTxt);
            } else {
                //拼装一个新的prompt
                return context.toString();
            }
        }
    }

    private String randomFromFixResult(String context) {
        //取两个@<< >>@之间的内容
        int idx = context.indexOf("@[[");
        int lst = context.indexOf("]]@");
        if(idx<0 || lst < 0) {
            idx = context.indexOf("@<<");
            lst = context.indexOf(">>@");
            if(idx<0 || lst<0)
                return null;
            String realContext = context.substring(idx, lst);
            if (!realContext.isEmpty()) {
                String[] answerList = realContext.split(",");
                idx = NumberUtils.randomInt(answerList.length);
                if (idx < answerList.length)
                    return answerList[idx];
            }
        } else {
            return context.substring(idx, lst);
        }
        return null;
    }

    public List<String> getKnowledgeFromMilvus(JedgeServiceSession s, JSONArray vec) {
        QIData rr = new QData();
        rr.put("v", vec);
        if(StringUtils.isNotValidStr(s.domain))
            s.domain = default_vs_domain_;
        rr.putString("r", s.domain);
        rr.putString("n", s.currentKnBase);
        rr.putInteger("qn", s.rn);    //回答问题的条数
        rr.put("t", defaultMatch);

        QIData p = postServiceRequest(sval_modual_vec_db, "/gv/query", rr);
        if(JMgbusUtil.isMgbusResultOk(p)) {
            return makeContextFromMilvusResult(p);
        } else if( p.getInteger("code")!=404 ) {
            module.warnLog(String.format("Fail to request to vdb module ： %s", p.toJSONString()));
            return null;
        }
        return null;
    }

    public String getKnowledgeFromMilvus(long sid, JSONArray vec) {
        JedgeServiceSession s;
        synchronized (sessions_) {
            s = sessions_.get(String.valueOf(sid));
        }
        if(s==null) return null;
        List<String> re = getKnowledgeFromMilvus(s, vec);
        return StringUtils.ListToString(re,',');
    }

    private List<String> makeContextFromMilvusResult(QIData p) {
        //结果拼装策略
        //拼装成一个对话请求
        List<String> r = new ArrayList<>();
        char cn = 0; double last = 0.0;
        JSONArray arr = p.getJsonArray("payload");
        if(arr ==null || arr.isEmpty()) return null;
        StringBuilder result = new StringBuilder();
        StringBuilder nextPredict = new StringBuilder();
        for(Object kn : arr) {
            if(kn instanceof JSONObject && !((JSONObject) kn).isEmpty()) {
                module.infoLog(String.format("Checking result : %s", ((JSONObject) kn).toJSONString()));
                if(!((JSONObject) kn).containsKey("s")) continue;
                //策略: 高度相似,只取一个
                Object v = ((JSONObject) kn).get("s");
                if( ! (v instanceof BigDecimal))
                    continue;
                double d =  ((BigDecimal) v).doubleValue();
                if( d > 0.86 && last>0.1 &&
                        (last - d >0.3 || cn > 2 )) {
                    break;
                }

                Object ao = ((JSONObject) kn).get("a");
                if(! (ao instanceof String)) continue;
                String a = (String) ao;
                //检查推理数据
                if(a.startsWith(sval_predict_sign))
                    nextPredict.append(sval_predict_split).append(a.substring(sval_predict_sign_size));
                else
                    result.append(a);
                if(d>0.9) {    //高度相似，只取一个答案
                    break;
                }
                cn++;
                last = d;
            }
        }
        r.add(result.toString());
        if(nextPredict.length()>0)
            r.add(nextPredict.toString());
        return  r;
    }

    private QIData makeGptFastCall(JedgeServiceSession s, String prompt) {
        //非Stream的聊天请求
        return JedgeLlmUtil.simpleOk();
    }

    private void knTalkLog(String realPrompt) {
        knTalkLog(realPrompt, false);
    }

    private void knTalkLog(String prompt, boolean succ) {
        String log =  "["+ TimeUtils.getNowShortStrWithMillis()+"] ["+(succ?"SUC":"FAL")+"]>>" + prompt;
        log = log.replaceAll( "\n","");
//        FileUtils.appendLine(kn_talk_log_file, log+"\n", "UTF-8");
    }

    protected JedgeServiceSession findSession(QIData req, String prompt) {
        String sid = String.valueOf(req.getInteger("sid"));
        JedgeServiceSession s = sessions_.get(sid);
        if(s!=null && req.getBoolean("c", false)) { //清理会话
            sessions_.remove(sid);
            s = null;    //清空原会话，重新创建新的对话历史
        }
        //更新会话的部分状态
        if(s!=null) {
            if(req.containsKey("v") && req.get("v") instanceof Boolean) {
                s.voiceTalk = req.getBoolean("v", false);
            }
            if(req.containsKey("k") && req.get("k") instanceof Boolean) {
                s.knTalk = req.getBoolean("k", false);
            }
            if(req.containsKey("r") && req.get("r") instanceof String) {
                String Domain = req.getString("r");
                if(StringUtils.isValidStr(Domain))
                    s.domain = Domain;
            }
            if(req.containsKey("n") && req.get("n") instanceof Integer) {
                s.rn = req.getInteger("n");
            }
            if((req.containsKey("knn"))) {
                Object val = req.get("knn");
                if(val instanceof String) {
                    JedgeGptApp app = gde.getGdeApp((String) val);
                    if(app==null) {
                        if(JedgeGptApp.isDebugMode() && !old_vdb_mode_)
                            returnMgbusMessage(JMgbusUtil.GetMgModSource(req),
                                    req.getString("cb", "/spmp/PostGptStream"),
                                    "知识库不存在", true, true);
                    }
                    s.currentKnBase = (String) val;
                }
            }
        } else {
            //创建新的Session
            boolean localGlm = req.getBoolean("l", true);
            boolean knTalk = req.getBoolean("k", false);
            String domain = req.getString("r");
            if(StringUtils.isNotValidStr(domain)) domain = localGlm? defaultDomainLocal : defaultDomain;
            int rn = req.getInteger("n", 6);
            boolean printSingle = req.getBoolean("talkStream", true);
            boolean isStream = req.getBoolean("stream", true); //默认是stream对话

            String currentKnBase = null;
            if((req.containsKey("knn"))) {
                Object val = req.get("knn");
                if(val instanceof String)
                    currentKnBase = (String) val;
                else
                    currentKnBase = sval_default_app;
            } else {
                //提供一个默认的知识库对话窗口。
                currentKnBase = sval_default_app;
            }
            JedgeGptApp app = gde.getGdeApp(currentKnBase);
            if(app==null) {
                if(JedgeGptApp.isDebugMode() && !old_vdb_mode_)
                    returnMgbusMessage(JMgbusUtil.GetMgModSource(req),
                            req.getString("cb", "/spmp/PostGptStream"),
                            "知识库不存在", true, true);
            }
            s = new JedgeServiceSession(app, this);
            s.currentKnBase = currentKnBase;
            s.srcMod = JMgbusUtil.GetMgModSource(req);
            s.cbUri = req.getString("cb", "/spmp/PostGptStream");
            s.sid = 0x10000000 + NumberUtils.randomLong(0x6FFFFFFF);
            sid = String.valueOf(s.sid);
            s.printSingle = printSingle;

            s.rn = rn;
            s.domain = domain;
            s.localGlm = localGlm;
            s.knTalk = knTalk;
            s.voiceTalk = req.getBoolean("v", false);
            s.isStream = isStream;
            synchronized (sessions_) {
                sessions_.put(sid, s);
            }
        }

        s.currentPrompt = prompt;
        return s;
    }

    //生成已对接的key
    //  主key：
    //  分类key：
    // 主key或，分类keyAnd
    //  joinKey、subKey和MainKey之间如何组合？
    //  joinKey放在最前面
    // 如果只有一个relKey或joinKey的短语，如何进行分类命中？

    private List<String> checkKnowledgeKey(String knKey) {
        List<String> result = new ArrayList<>();
        List<String> keyList = new ArrayList<>();
        StringBuilder subKey = new StringBuilder();
        StringBuilder joinKey = new StringBuilder();        //开头使用，且仅使用最前面的词。

        JSONArray words = cutWords(knKey);
        if(words!=null && !words.isEmpty()) {
            for(Object w : words) {
                if(w instanceof JSONObject) {
                    String t = ((JSONObject) w).getString("t");
                    if(val_verb_type_user_defined_noun.equals(t)) {
                        keyList.add(((JSONObject) w).getString("w"));
                    } else if(sval_verb_type_user_defined_relation_noun.equals(t)) {
                        if(subKey.length() > 0)
                            subKey.append('$');
                        subKey.append(((JSONObject) w).getString("w"));
                    } else  if(sval_verb_type_user_defined_join_noun.equals(t)) {
                        joinKey.append(((JSONObject) w).getString("w"));
                    }
                }
            }
            //建立relKey到mainKey的关联关系。当出现relKey时，在关键字池里面
        }
        for(String mainKey : keyList) {
            String curKey = subKey.length()>0?mainKey+'$'+subKey:mainKey;
            if(joinKey.length()>0)
                curKey = joinKey.toString()+'$'+curKey;
            result.add(curKey);
        }
        return result;
    }

    public JMgbusModual getModule() {
        return module;
    }
}
