"""教学用只读工具分发器；没有模型调用，也没有退款或外部写入能力。"""
from dataclasses import dataclass

@dataclass(frozen=True)
class Session:
    # 会话对象应由服务端认证层构造，不能由模型输出转换而来。
    user_id: str
    can_read_orders: bool

ORDERS = {"A100": {"owner": "u1", "status": "delivered"}}

def dispatch(proposal, session):
    # 把模型的提议作为不可信数据，只接受精确定义的协议字段。
    if not isinstance(proposal, dict) or set(proposal) != {"tool", "arguments"}:
        raise ValueError("工具提议结构错误")
    if proposal["tool"] != "lookup_order":
        raise PermissionError("未注册该工具")
    args = proposal["arguments"]
    if not isinstance(args, dict) or set(args) != {"order_id"}:
        raise ValueError("只允许 order_id 参数")
    if not isinstance(args["order_id"], str):
        raise ValueError("order_id 必须为字符串")
    if not session.can_read_orders:
        raise PermissionError("当前会话没有订单读取权限")
    order = ORDERS.get(args["order_id"])
    # 不存在和无权访问统一返回不可用，避免通过错误信息泄露他人订单。
    if order is None or order["owner"] != session.user_id:
        return {"status": "unavailable"}
    # 只返回完成任务需要的字段，不把整个数据库记录传给模型。
    return {"status": "ok", "order_id": args["order_id"], "order_status": order["status"]}

if __name__ == "__main__":
    session = Session(user_id="u1", can_read_orders=True)
    # 这些提议由作者构造，用来解释后端边界；不是模型生成结果。
    proposals = [
        {"tool": "lookup_order", "arguments": {"order_id": "A100"}},
        {"tool": "refund", "arguments": {"order_id": "A100"}},
    ]
    for proposal in proposals:
        try:
            print(dispatch(proposal, session))
        except (ValueError, PermissionError) as exc:
            print("已拒绝：", exc)
