# -*- coding: utf-8 -*-
"""
DeepAgents 中断审批机制示例
核心功能：演示高危工具调用前的人工审批流程，支持删除数据库表/文件的审批控制
"""
import os

from langchain.agents.middleware import ModelCallLimitMiddleware
from langchain.chat_models import init_chat_model
from langchain.tools import tool
from deepagents import create_deep_agent
from langgraph.checkpoint.memory import InMemorySaver  # 内存检查点，用于保存中断状态
from langgraph.types import Command  # 恢复执行的指令类型
from dotenv import load_dotenv, find_dotenv

# 加载环境变量（DASHSCOPE_API_KEY等），优先查找当前目录的.env文件
load_dotenv(find_dotenv())

# 初始化大模型（通义千问）
llm = init_chat_model(
    model=os.getenv("LLM_QWEN_MAX"),          # 模型名称（从环境变量读取）
    model_provider="openai"                   # 兼容OpenAI格式的接口
)

# 删除表工具
@tool
def delete_database(table_name: str):
    """
    高危动作工具，删除传入的表！
    :param table_name: 要删除的表名
    :return: 操作的返回结果
    """
    print(f"调用了删除了delete_database工具。删除了{table_name}表！！")
    return f"删除了表{table_name}！"

# 删除文件工具
@tool
def delete_file(file_name: str):
    """
    高危动作工具，删除传入的文件！
    :param file_name: 要删除的文件名
    :return: 操作的返回结果
    """
    print(f"调用了删除了delete_file工具。删除了{file_name}文件！！")
    return f"删除了文件{file_name}！"

# 查询表数据工具
@tool
def select_database(table_name: str):
    """
    查询动作工具，查询传入的表数据！
    :param table_name: 要查询的表名
    :return: 查询结果
    """
    print(f"调用了select_database工具。查询了{table_name}表数据！！")
    return f"查询了表{table_name}的数据！"


# 创建deepagent，同时给高危工具设置人机交互！ 拦截动作！
# 1. 必备内容 短期记忆 + 线程id
checkpointer = InMemorySaver()
thread_config = {
    "configurable" :{"thread_id":"erdaye"}
}

# 2.常见deepagent，设置高危工具需要拦截处理
main_agent = create_deep_agent(
    model=llm,
    tools=[delete_database, delete_file, select_database],
    checkpointer=checkpointer,  # 必须的记录过程
    system_prompt="回答使用中文，调用对应的工具实现对应的功能！",
    middleware=[
        ModelCallLimitMiddleware(
            thread_limit=100,
            run_limit=1,
            exit_behavior="error",
        )
    ]
    ,
    interrupt_on={
        "delete_database": False, # 通过 编辑 或者拒绝 ,
        "delete_file": False, # 通过 编辑 或者拒绝 ,  {"allowed_decisions": ["approve", "reject"]}
        "select_database": False
    }
)

# 3. 预执行，本次不会真正的执行！ 所有都不会执行，判定本次执行链中是否存在 人机交互节点！ 存在需要设置后 二次执行！
result_1 = main_agent.invoke({
    "messages":[
        {"role":"user","content":"先查询product表的数据！再删除user表，最后，删除zhaoweifeng.txt文件"}
    ]
},config=thread_config) # 记录人 继续工作

print(f"最终结果{result_1['messages'][-1].content}")

# # 4. 检查本次执行是否存在人机交互动作  规定： 有人机交互动作，会将交互的拦截点信息存储到  __interrupt__
# interrupt = result_1['__interrupt__']
# """
# [ -> 列表
#   Interrupt -> 对象
#      (value  ->  value 属性
#           存储拦截信息 -> 字典 {
#                'action_requests': -> 列表，存储了本次触发的拦截的工具信息（名字和参数） [
#                                   {'name': 'delete_database', 'args': {'table_name': 'user'}, 'description': "To"},
#                                   {'name': 'delete_file', 'args': {'file_name': 'zhaoweifeng.txt'}, 'description': "T}"}],
#               'review_configs': [ -> 触发拦截工具以后，拦截工具有的后续动作有哪些？
#                                  {'action_name': 'delete_database', 'allowed_decisions': ['approve', 'edit', 'reject']},
#                                  {'action_name': 'delete_file', 'allowed_decisions': ['approve', 'edit', 'reject']}]}, id='efc9a550c63c45bfdb429bf79f6215a6')]
# """
# #print(interrupt)
#
# if interrupt:
#    # 处理人机交互动作，二次执行
#    # 输出一共有几个工具被拦截了， 这些工具的名字是什么！！！
#    action_requests = interrupt[0].value['action_requests']
#    print(f"本次需要审核的工具数量：{len(action_requests)} ,具体拦截的工具：{[action['name'] for action in action_requests]}")
#
#    # 审批意见
#    decisions = []
#    for action in action_requests:
#        #   'name': 'delete_database', 'args': {'table_name': 'user'}, 'description': "To"},
#        action_name = action['name']
#        action_args = action['args']
#        # 检查代码，放行 还是 拦截 还是 编辑
#        if action_name == "delete_database":
#            # 拦截
#            # 每一次的拦截动作 {type: "reject | approve"}
#            # 按照 decisions 元素顺序，进行拦截还还是放行
#            decisions.append({"type":"reject"})
#        elif action_name == "delete_file":
#            # 放行
#            decisions.append({"type": "approve"})
#
#    # 二次执行 (真执行)  1. 不需要传入用户的query 2. 必须config = 确保线程id等于第一次执行的！  3. 审批意见 Command对象 -》 langgraph
#    result_2 = main_agent.invoke(
#       # 审批意见 传入审批意见，再次执行！！
#       Command(
#           resume={
#               "decisions":decisions
#           }
#       ),
#       config=thread_config
#    )
#
#    print(f"最终结果{result_2['messages'][-1].content}")



