"""
深度思考的对话实现，需要多步骤的思考和校验，才能最后回答用户的问题，至多校验三次
"""


import datetime
from init import llm
import json
import json5
from full_index_search import full_search,adjust_ctx_size,jieba_fenci,full_search_logic,one_doc_search
from k_database import QA

#深度思考
def DS_chat(query,history):

    try:
        i=0
        yield gen_event_data({"step":i,"rsp":f"{query}--检查是否需要补全或重写?"})
        result =DS_chat0(query,history)
        new_query = deal_llm_think_json_data(result)

        print(new_query)
        yield gen_event_data({"step":i,"rsp":new_query})

        seg_list = jieba_fenci(new_query)
        yield gen_event_data({"rsp":f"搜索关键词 {seg_list}"})

        #至多三次深度回答
        for i in range(1,4):

            for result in search_ctx_chat(new_query,seg_list):
                try:
                    data = deal_llm_think_json_data(result)
                    answer = json.loads(data)
                except Exception as e:
                    print(e,data)
                    yield gen_event_data({"step":i,"rsp":f"{data}"})
                    break
                if answer["code"]==100: #中间ixnxi
                    yield gen_event_data({"step":i,"rsp":answer["content"]})
                else:
                    break
            #其它结果
            if answer["code"]==200 or answer["code"]==202:
                yield gen_event_data({"step":i,"rsp":answer["content"]})
                #QA.create(question=new_query,answer=answer["content"],ref="深度思考")
                return answer["content"]
            elif answer["code"]==201:#单个文件回答
                yield gen_event_data({"step":i,"rsp":f'找到相关文件: {answer["name"]}'})
                answer["question"]=new_query
                yield from one_doc_chat(answer)
                #QA.create(question=new_query,answer=answer["name"],ref="深度思考")
                return answer["name"]
            elif answer["code"]==404:
                yield gen_event_data({"step":i,"rsp":f'重新搜索关键字:{answer["keywords"]}'})
                seg_list = answer["keywords"]
            else:#出错
                break
        #end for
    except Exception as e : 
        yield gen_event_data({"step":-1,"rsp":f"对话出现错误:{e}"})

    if i==3:
        yield gen_event_data({"step":i,"rsp":"对不起,没有找到你想要的答案!"})
        #QA.create(question=query,answer="对不起,没有找到你想要的答案!",ref="深度思考")
        return "对不起,没有找到你想要的答案!"


def gen_event_data(json_data):
    json_str = "data: " + json.dumps(json_data) + "\n\n"
    return json_str.encode("utf-8")


def deal_llm_think_json_data(data):
    #print("==============================")
    #print(data)
    #去掉思考过程
    if data.find("<think>")>=0 and data.find("</think>")>0:
        end = data.find("</think>")
        data = data[end+8:]
    if data.find("```json") >=0:
        begin = data.find("{")
        end = data.rfind("}")
        data = data[begin:end+1]
    return data
    
    

#多轮对话，生成新问题
def DS_chat0(query,history):    
    # 获取当前日期和星期
    today = datetime.datetime.now()
    # 获取今天是星期几，使用weekday()方法（周一=0, 周日=6）
    weekday_number = today.weekday()

    system_prompt=f"""
    今天是{today.strftime('%Y-%m-%d')} 星期{weekday_number+1}。
    你是一个支持多轮问答的智能助手,你需要根据历史对话信息和本次问题来判断需不需改写问题，重新生成一个更加准确的问题.

    并按照如下步骤来分析执行改写情况
    1.特别是本次问题用他、她或它来指代历史对话中对象的情景，如果历史对话中有明确的对象，就需要用历史对话中明确的对象来本次问题的代词。

    如:历史对话中有["中国的国土面积是多少",...],
    本次问题是"他的人口有多少"，则需要将本次问题中的“他”换成历史对话中的“中国”，生成新的问题"中国的人口有多少"
    
    2.如果多多条历史记录的，就用最近对话中对象来指代，如：

    示例1
    历史对话：["杭州是个美丽的城市","他的GDP是多少"]
    本次问题: 他在2024年的常驻人口是多少
    生成问题: 杭州在2024年的常驻人口是多少

    示例2
    历史对话：["杭州是个美丽的城市","他的GDP是多少","他在2024年的常驻人口是多少","厦门也是一个美丽的城市"]
    本次问题：他的旅游忘记是什么时候
    生成问题：厦门的旅游旺季是什么时候

    3.其它需要改写的情况，改写后语句的关键字可以更有利于后续检索的


    切记！！！不输出其它任何信息！！！
    
    历史对话:(时间越早排序越靠前)
    ```
    {history}
    ```

    本次问题：
    {query}

    生成问题：
    """
    messages = [{'role': 'user', 'content': system_prompt}]
    response = llm.chat(
        messages=messages,
        stream=False,
    ) # get a new response from the model where it can see the function response

    return response[0]["content"]

#检索上下文回答
def search_ctx_chat(query,seg_list):

    #全文检索相关知识
    context,meta = full_search_logic(query,30,seg_list)
    context = adjust_ctx_size(context)
    count = len(context)
    ctx = []
    for i in range(count):
        ctx.append({"name":meta[i][0],"path":meta[i][1],"base":meta[i][2]})
    
    yield json.dumps({"code":100,"content":f"找到{count}个相关信息{ctx}"})

    system_prompt=f"""
    你是一个知识问答的助手，你可以根据提供的上下文信息来回答用户的问题，并能按如下步骤来深度思考准确回答问题:
    1.首先判断上下文和问题的相关性，如果上下文足以回答问题的，按如下json格式进行输出
            {{"code":200,"content":"回答的内容"}}
    2.如果提供的上下文不足以准确回答信息，但某个上下文和上下文提供的文件对回答问题的相关性已经具备，需要进一步检索该文件内容来回答信息的。
        可以按如下json格式回答信息
            {{"code":201,"name":"对应的文件名","path":"文件路径","base":"知识库"}}
    3.如果提供的上下文和文件完全不足以回答问题时，你可以调整优化问题关键字列表，让助手重新检索上下文，如：
        A.删除掉一些和问题相关度不高的关键字，让检索的范围变大,找到更合适的上下文
        B.根据问题的情况更换、调整或生成一些新关键字，重新检索，以便更好的回答问题
        C.使用更合适的同义词替换现有关键字
        最后输出json格式格式如下：
            {{"code":404,"keywords":["关键词1","关键词2","关键词n"]}}
    4.如果这个问题是一个常识性的问题，不需要上下文，只使用大模型内部知识就可以回答的，可以按如下json方式进行输出
        {{"code":202,"content":"回答的内容"}}

    5.一定按json格式进行输出,回答的内容中有特殊符号如"的需要进行转化为\",不要影响json的输出的格式。

    上下文:
    ```
    {context}
    ```

    上下文对应的文件名称和路径信息：
    ```
    {ctx}
    ```
    
    问题：
    {query}

    问题关键字：
    {seg_list}

    """
    messages = [{'role': 'user', 'content': system_prompt}]

    
    response = llm.chat(
        messages=messages,
        stream=False,
    ) # get a new response from the model where it can see the function response

    yield response[0]["content"]
 

def one_doc_chat(chat):
    query = chat["question"]
    context= one_doc_search(chat["question"],chat["base"],chat["name"],chat["path"])

    context = adjust_ctx_size(context)
    
    system_prompt=f"""
    你是一个知识问答的助手。
    你可以根据提供的上下文信息来回答用户的问题,上下文中排在前面的可信度越高，越应该优先采纳。
    如果提供的上下文不足以回答问题时，你可以回答'对不起，暂时没有找到相关的信息，无法回答你的问题'
    
    上下文:
    ```
    {context}
    ```

    问题：
    {query}

    """
    messages = [{'role': 'user', 'content': system_prompt}]
    responses = llm.chat(
        messages=messages,
        stream=True,
    ) # get a new response from the model where it can see the function response
    json_str=""
    try:
        for response in responses:
            json_str = "data: " + json.dumps({'rsp':response[0]["content"]}) + "\n\n"
            yield json_str.encode("utf-8")

        #QA.create(question=query,answer=response[0]["content"],ref="深度思考")
        return response[0]["content"]
    except Exception as e:
        json_str = "data: " + json.dumps({"rsp": f"大模型运行出现错误:{e}, 请检查配置是否正确或者网络通信是否通畅!\n如果是本地大模型还请检查大模型是否正常启动!"}) + "\n\n"
        yield json_str.encode("utf-8")
