#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
AI模型处理器

用于管理生成、与嵌入模型的创建和配置

支持的模型类型：
- doubao: 豆包模型
- qwen: 通义千问模型
- deepseek: DeepSeek模型
- lq_qwen3_8b: 本地Qwen3-8B模型
- lq_qwen3_8b_lq_lora: 本地Qwen3-8B-lq-lora模型
- lq_qwen3_4b: 本地Qwen3-4B模型
- qwen_local_14b: 本地Qwen3-14B模型
- lq_qwen3_8b_emd: 本地Qwen3-Embedding-8B嵌入模型
- siliconflow_embed: 硅基流动Qwen3-Embedding-8B嵌入模型
- lq_bge_reranker_v2_m3: 本地BGE-reranker-v2-m3重排序模型
- qwen3_5_35b_a3b: DashScope Qwen3.5-35B-A3B模型（默认兜底模型）
- qwen3_5_27b: DashScope Qwen3.5-27B模型
- qwen3_5_122b_a10b: DashScope Qwen3.5-122B-A10B模型
- shutian_qwen3_5_122b: 蜀天Qwen3.5-122B-A10B模型（183.220.37.46:25423）
- shutian_qwen3_8b: 蜀天Qwen3-8B模型（183.220.37.46:25424）
- shutian_qwen3_5_35b: 蜀天Qwen3.5-35B模型（183.220.37.46:25427）
- shutian_qwen3_embed: 蜀天Qwen3-Embedding-8B模型（183.220.37.46:25425）
- shutian_qwen3_reranker: 蜀天Qwen3-Reranker-8B模型（183.220.37.46:25426）
"""

# 禁用 transformers 的深度学习框架检测，避免启动时耗时扫描
import os
os.environ["TRANSFORMERS_VERBOSITY"] = "error"
os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"

import requests
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from foundation.infrastructure.config.config import config_handler
from foundation.observability.logger.loggering import review_logger as logger


class ModelConnectionError(Exception):
    """模型连接错误"""
    pass


class ModelConfigError(Exception):
    """模型配置错误"""
    pass


class ModelAPIError(Exception):
    """模型API调用错误"""
    pass


class ModelHandler:
    """
    AI模型处理器类，用于管理多种AI模型的创建和配置
    """

    # 模型连接超时时间配置（秒）
    CONNECTION_TIMEOUT = 30
    REQUEST_TIMEOUT = 120
    MAX_RETRIES = 2

    def __init__(self):
        """
        初始化模型处理器

        加载配置处理器，用于后续读取各种模型的配置信息
        """
        self.config = config_handler
        self._model_cache = {}  # 模型实例缓存

    def _check_connection(self, base_url: str, api_key: str = None, timeout: int = 5) -> bool:
        """
        检查模型服务连接是否可用

        支持两种检查方式：
        1. GET /models - 标准 OpenAI 兼容接口
        2. POST /chat/completions - 直接测试 chat 接口（部分服务只支持此接口）

        Args:
            base_url: 模型服务地址
            api_key: API密钥（可选）
            timeout: 超时时间（秒）

        Returns:
            bool: 连接是否可用
        """
        headers = {}
        if api_key and api_key != "dummy":
            headers["Authorization"] = f"Bearer {api_key}"

        # 方法1: 尝试 /models 端点
        try:
            health_url = f"{base_url.rstrip('/')}/models"
            response = requests.get(
                health_url,
                headers=headers,
                timeout=timeout
            )
            if 200 <= response.status_code < 300:
                logger.debug(f"连接检查通过 (/models): {base_url}")
                return True
        except requests.exceptions.Timeout:
            logger.debug(f"连接检查超时 (/models): {base_url}")
        except Exception as e:
            logger.debug(f"连接检查失败 (/models): {base_url}, {e}")

        # 方法2: 尝试 /chat/completions 端点（发送一个简单请求）
        try:
            chat_url = f"{base_url.rstrip('/')}/chat/completions"
            test_payload = {
                "model": "test",
                "messages": [{"role": "user", "content": "test"}],
                "max_tokens": 1
            }
            response = requests.post(
                chat_url,
                headers={**headers, "Content-Type": "application/json"},
                json=test_payload,
                timeout=timeout
            )
            # 即使返回 400/401/404 也说明服务是通的
            # 只有连接错误/超时才是真的连不上
            if response.status_code != 503:  # 503 表示服务不可用
                logger.debug(f"连接检查通过 (/chat/completions): {base_url}, 状态码: {response.status_code}")
                return True
        except requests.exceptions.Timeout:
            logger.warning(f"连接检查超时: {base_url}")
        except requests.exceptions.ConnectionError as e:
            logger.warning(f"连接检查错误: {base_url}, {e}")
        except Exception as e:
            logger.warning(f"连接检查异常: {base_url}, {e}")

        return False

    def _handle_model_error(self, model_name: str, error: Exception, fallback_model=None):
        """
        统一处理模型错误

        Args:
            model_name: 模型名称
            error: 异常对象
            fallback_model: 降级模型实例（可选）

        Returns:
            降级模型实例，如果不可用则返回None
        """
        error_type = type(error).__name__
        error_msg = str(error)

        logger.error(f"模型初始化失败 [{model_name}]: {error_type} - {error_msg}")

        # 如果提供了降级模型，记录日志并返回
        if fallback_model:
            logger.warning(f"使用降级模型: {fallback_model.__class__.__name__}")
            return fallback_model

        # 如果没有降级模型，返回None让调用方处理
        return None

    def get_models(self):
        """
        获取AI模型实例

        Returns:
            ChatOpenAI: 配置好的AI模型实例

        Note:
            优先从 model_setting.yaml 读取默认模型配置，如果不存在则回退到 config.ini 的 MODEL_TYPE
            支持的模型类型：doubao, qwen, deepseek, lq_qwen3_8b, lq_qwen3_8b_lora, lq_qwen3_4b, qwen_local_14b
        """
        # 优先从 model_setting.yaml 读取默认模型配置
        try:
            from foundation.ai.models.model_config_loader import get_model_for_function
            model_type = get_model_for_function("default")
            if model_type:
                logger.debug(f"从 model_setting.yaml 读取默认模型: {model_type}")
            else:
                model_type = self.config.get("model", "MODEL_TYPE")
        except Exception as e:
            logger.debug(f"从 model_setting.yaml 读取默认模型失败: {e}，回退到 config.ini")
            model_type = self.config.get("model", "MODEL_TYPE")
        logger.info(f"正在初始化AI模型，模型类型: {model_type}")

        # 检查缓存
        cache_key = f"chat_{model_type}"
        if cache_key in self._model_cache:
            logger.info(f"使用缓存的模型: {model_type}")
            return self._model_cache[cache_key]

        model = None

        try:
            if model_type == "doubao":
                model = self._get_doubao_model()
            elif model_type == "qwen":
                model = self._get_qwen_model()
            elif model_type == "qwen3_30b":
                model = self._get_qwen3_30b_model()
            elif model_type == "deepseek":
                model = self._get_deepseek_model()
            elif model_type == "lq_qwen3_8b":
                model = self._get_lq_qwen3_8b_model()
            elif model_type == "lq_qwen3_8b_lq_lora":
                model = self._get_lq_qwen3_8b_lora_model()
            elif model_type == "lq_qwen3_4b":
                model = self._get_lq_qwen3_4b_model()
            elif model_type == "qwen_local_14b":
                model = self._get_qwen_local_14b_model()
            elif model_type == "qwen3_5_35b_a3b":
                model = self._get_qwen3_5_35b_a3b_model()
            elif model_type == "qwen3_5_27b":
                model = self._get_qwen3_5_27b_model()
            elif model_type == "qwen3_5_122b_a10b":
                model = self._get_qwen3_5_122b_a10b_model()
            elif model_type == "shutian_qwen3_5_122b":
                model = self._get_shutian_qwen3_5_122b_model()
            elif model_type == "shutian_qwen3_8b":
                model = self._get_shutian_qwen3_8b_model()
            elif model_type == "shutian_qwen3_5_35b":
                model = self._get_shutian_qwen3_5_35b_model()
            else:
                logger.warning(f"未知的模型类型 '{model_type}'，使用默认 qwen3_5_35b_a3b 模型")
                model = self._get_qwen3_5_35b_a3b_model()

            if model:
                self._model_cache[cache_key] = model
                logger.info(f"AI模型初始化完成: {model_type}")
                return model
            else:
                raise ModelAPIError(f"模型初始化返回None: {model_type}")

        except Exception as e:
            logger.error(f"获取模型失败 [{model_type}]: {e}")

            # 使用 qwen3_5_35b_a3b 作为兜底降级方案
            if model_type != "qwen3_5_35b_a3b":
                logger.info("尝试使用 qwen3_5_35b_a3b 模型作为降级方案")
                try:
                    fallback_model = self._get_qwen3_5_35b_a3b_model()
                    if fallback_model:
                        self._model_cache[cache_key] = fallback_model
                        logger.warning("已切换到 qwen3_5_35b_a3b 降级模型")
                        return fallback_model
                except Exception as fallback_error:
                    logger.error(f"降级模型也失败: {fallback_error}")

            # 如果所有模型都失败，抛出异常
            raise ModelConnectionError(f"无法初始化任何模型服务: {e}")

    def get_model_by_name(self, model_type: str = None):
        """
        根据模型名称动态获取指定的AI模型实例

        Args:
            model_type: 模型类型名称,如果为None则使用配置文件中的默认模型
                       支持的模型类型：doubao, qwen, qwen3_30b, deepseek, gemini,
                                     lq_qwen3_8b, lq_qwen3_8b_lq_lora,
                                     lq_qwen3_4b, qwen_local_14b

        Returns:
            ChatOpenAI: 配置好的AI模型实例

        Note:
            该方法支持动态切换模型,不受配置文件中的默认MODEL_TYPE限制
            如果model_type为None,则使用配置文件中的默认模型
            如果model_type无效,则使用gemini作为降级模型
        """
        # 如果未指定模型类型,使用配置文件中的默认模型
        if model_type is None:
            model_type = self.config.get("model", "MODEL_TYPE")

        logger.info(f"动态获取AI模型，模型类型: {model_type}")

        # 检查缓存
        cache_key = f"chat_{model_type}"
        if cache_key in self._model_cache:
            logger.info(f"使用缓存的模型: {model_type}")
            return self._model_cache[cache_key]

        model = None

        try:
            if model_type == "doubao":
                model = self._get_doubao_model()
            elif model_type == "qwen":
                model = self._get_qwen_model()
            elif model_type == "qwen3_30b":
                model = self._get_qwen3_30b_model()
            elif model_type == "deepseek":
                model = self._get_deepseek_model()
            elif model_type == "lq_qwen3_8b":
                model = self._get_lq_qwen3_8b_model()
            elif model_type == "lq_qwen3_8b_lq_lora":
                model = self._get_lq_qwen3_8b_lora_model()
            elif model_type == "lq_qwen3_4b":
                model = self._get_lq_qwen3_4b_model()
            elif model_type == "qwen_local_14b":
                model = self._get_qwen_local_14b_model()
            elif model_type == "qwen3_5_35b_a3b":
                model = self._get_qwen3_5_35b_a3b_model()
            elif model_type == "qwen3_5_27b":
                model = self._get_qwen3_5_27b_model()
            elif model_type == "qwen3_5_122b_a10b":
                model = self._get_qwen3_5_122b_a10b_model()
            elif model_type == "shutian_qwen3_5_122b":
                model = self._get_shutian_qwen3_5_122b_model()
            elif model_type == "shutian_qwen3_8b":
                model = self._get_shutian_qwen3_8b_model()
            elif model_type == "shutian_qwen3_5_35b":
                model = self._get_shutian_qwen3_5_35b_model()
            else:
                logger.warning(f"未知的模型类型 '{model_type}'，使用默认 qwen3_5_35b_a3b 模型")
                model = self._get_qwen3_5_35b_a3b_model()

            if model:
                self._model_cache[cache_key] = model
                logger.info(f"AI模型动态初始化完成: {model_type}")
                return model
            else:
                raise ModelAPIError(f"模型初始化返回None: {model_type}")

        except Exception as e:
            logger.error(f"动态获取模型失败 [{model_type}]: {e}")

            # 使用 qwen3_5_35b_a3b 作为兜底降级方案
            if model_type != "qwen3_5_35b_a3b":
                logger.info("尝试使用 qwen3_5_35b_a3b 模型作为降级方案")
                try:
                    fallback_model = self._get_qwen3_5_35b_a3b_model()
                    if fallback_model:
                        # 注意：不要把降级模型存入原模型的缓存，避免后续调用都使用错误的模型
                        fallback_cache_key = "chat_qwen3_5_35b_a3b"
                        self._model_cache[fallback_cache_key] = fallback_model
                        logger.warning(f"已切换到 qwen3_5_35b_a3b 降级模型（不会缓存为 {model_type}）")
                        return fallback_model
                except Exception as fallback_error:
                    logger.error(f"降级模型也失败: {fallback_error}")

            # 如果所有模型都失败，抛出异常
            raise ModelConnectionError(f"无法初始化任何模型服务: {e}")

    def get_model_by_function(self, function_name: str):
        """
        根据功能名称获取对应的AI模型实例

        从 config/model_setting.yaml 加载功能对应的模型配置

        Args:
            function_name: 功能名称，如:
                - doc_classification_secondary: 文档二级分类
                - doc_classification_tertiary: 文档三级分类
                - completeness_review_generate: 完整性审查生成
                - completeness_review_classify: 完整性审查分类
                - rag_query_understand: RAG查询理解
                - rag_answer_generate: RAG答案生成
                - sensitive_check: 敏感信息检查
                - grammar_check: 语法检查

        Returns:
            ChatOpenAI: 配置好的AI模型实例

        Example:
            model = model_handler.get_model_by_function("doc_classification_tertiary")
        """
        try:
            from foundation.ai.models.model_config_loader import get_model_for_function
            model_type = get_model_for_function(function_name)
            logger.info(f"根据功能 '{function_name}' 获取模型: {model_type}")
            return self.get_model_by_name(model_type)
        except Exception as e:
            logger.warning(f"根据功能获取模型失败 [{function_name}]: {e}，使用默认模型")
            return self.get_model_by_name("qwen3_5_35b_a3b")

    def get_embedding_model(self):
        """
        获取Embedding模型实例

        Returns:
            OpenAIEmbeddings: 配置好的Embedding模型实例

        Note:
            根据配置文件中的EMBEDDING_MODEL_TYPE参数选择对应模型
            支持的模型类型：lq_qwen3_8b_emd, siliconflow_embed
            默认返回本地 lq_qwen3_8b_emd 模型
        """
        embedding_model_type = self.config.get("model", "EMBEDDING_MODEL_TYPE", "lq_qwen3_8b_emd")
        logger.info(f"正在初始化Embedding模型，模型类型: {embedding_model_type}")

        # 检查缓存
        cache_key = f"embed_{embedding_model_type}"
        if cache_key in self._model_cache:
            logger.info(f"使用缓存的Embedding模型: {embedding_model_type}")
            return self._model_cache[cache_key]

        model = None

        try:
            if embedding_model_type == "siliconflow_embed":
                model = self._get_siliconflow_embedding_model()
            elif embedding_model_type == "lq_qwen3_8b_emd":
                model = self._get_lq_qwen3_8b_emd()
            elif embedding_model_type == "shutian_qwen3_embed":
                model = self._get_shutian_qwen3_embed()
            else:
                # 默认返回本地模型
                logger.warning(f"未知的Embedding模型类型 '{embedding_model_type}'，使用默认本地模型")
                model = self._get_lq_qwen3_8b_emd()

            if model:
                self._model_cache[cache_key] = model
                logger.info(f"Embedding模型初始化完成: {embedding_model_type}")
                return model
            else:
                raise ModelAPIError(f"Embedding模型初始化返回None: {embedding_model_type}")

        except Exception as e:
            logger.error(f"获取Embedding模型失败 [{embedding_model_type}]: {e}")
            raise ModelConnectionError(f"无法初始化Embedding模型服务: {e}")

    def _get_doubao_model(self):
        """
        获取豆包模型

        Returns:
            ChatOpenAI: 配置好的豆包模型实例
        """
        try:
            doubao_url = self.config.get("doubao", "DOUBAO_SERVER_URL")
            doubao_model_id = self.config.get("doubao", "DOUBAO_MODEL_ID")
            doubao_api_key = self.config.get("doubao", "DOUBAO_API_KEY")

            # 验证配置完整性
            if not all([doubao_url, doubao_model_id, doubao_api_key]):
                missing = []
                if not doubao_url:
                    missing.append("DOUBAO_SERVER_URL")
                if not doubao_model_id:
                    missing.append("DOUBAO_MODEL_ID")
                if not doubao_api_key:
                    missing.append("DOUBAO_API_KEY")
                raise ModelConfigError(f"豆包模型配置不完整，缺少: {', '.join(missing)}")

            # 检查连接
            if not self._check_connection(doubao_url, doubao_api_key):
                logger.warning(f"豆包模型服务连接失败: {doubao_url}")
                raise ModelConnectionError(f"无法连接到豆包模型服务: {doubao_url}")

            llm = ChatOpenAI(
                base_url=doubao_url,
                model=doubao_model_id,
                api_key=doubao_api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
                extra_body={
                    "enable_thinking": False,
                })

            logger.info(f"豆包模型初始化成功: {doubao_model_id}")
            return llm

        except ModelConfigError:
            raise
        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"豆包模型初始化异常: {e}")
            return self._handle_model_error("doubao", error)

    def _get_qwen_model(self):
        """
        获取通义千问模型

        Returns:
            ChatOpenAI: 配置好的通义千问模型实例
        """
        try:
            qwen_url = self.config.get("qwen", "QWEN_SERVER_URL")
            qwen_model_id = self.config.get("qwen", "QWEN_MODEL_ID")
            qwen_api_key = self.config.get("qwen", "QWEN_API_KEY")

            # 验证配置完整性
            if not all([qwen_url, qwen_model_id, qwen_api_key]):
                missing = []
                if not qwen_url:
                    missing.append("QWEN_SERVER_URL")
                if not qwen_model_id:
                    missing.append("QWEN_MODEL_ID")
                if not qwen_api_key:
                    missing.append("QWEN_API_KEY")
                raise ModelConfigError(f"通义千问模型配置不完整，缺少: {', '.join(missing)}")

            # 检查连接
            if not self._check_connection(qwen_url, qwen_api_key):
                logger.warning(f"通义千问模型服务连接失败: {qwen_url}")
                raise ModelConnectionError(f"无法连接到通义千问模型服务: {qwen_url}")

            llm = ChatOpenAI(
                base_url=qwen_url,
                model=qwen_model_id,
                api_key=qwen_api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
                extra_body={
                    "enable_thinking": False,
                })

            logger.info(f"通义千问模型初始化成功: {qwen_model_id}")
            return llm

        except ModelConfigError:
            raise
        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"通义千问模型初始化异常: {e}")
            return self._handle_model_error("qwen", error)

    def _get_qwen3_30b_model(self):
        """
        获取Qwen3-30B模型

        Returns:
            ChatOpenAI: 配置好的Qwen3-30B模型实例
        """
        try:
            qwen3_30b_url = self.config.get("qwen3_30b", "QWEN3_30B_SERVER_URL")
            qwen3_30b_model_id = self.config.get("qwen3_30b", "QWEN3_30B_MODEL_ID")
            qwen3_30b_api_key = self.config.get("qwen3_30b", "QWEN3_30B_API_KEY")

            # 验证配置完整性
            if not all([qwen3_30b_url, qwen3_30b_model_id, qwen3_30b_api_key]):
                missing = []
                if not qwen3_30b_url:
                    missing.append("QWEN3_30B_SERVER_URL")
                if not qwen3_30b_model_id:
                    missing.append("QWEN3_30B_MODEL_ID")
                if not qwen3_30b_api_key:
                    missing.append("QWEN3_30B_API_KEY")
                raise ModelConfigError(f"Qwen3-30B模型配置不完整，缺少: {', '.join(missing)}")

            # 检查连接
            if not self._check_connection(qwen3_30b_url, qwen3_30b_api_key):
                logger.warning(f"Qwen3-30B模型服务连接失败: {qwen3_30b_url}")
                raise ModelConnectionError(f"无法连接到Qwen3-30B模型服务: {qwen3_30b_url}")

            llm = ChatOpenAI(
                base_url=qwen3_30b_url,
                model=qwen3_30b_model_id,
                api_key=qwen3_30b_api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
                extra_body={
                    "enable_thinking": False,
                })

            logger.info(f"Qwen3-30B模型初始化成功: {qwen3_30b_model_id}")
            return llm

        except ModelConfigError:
            raise
        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"Qwen3-30B模型初始化异常: {e}")
            return self._handle_model_error("qwen3_30b", error)

    def _get_deepseek_model(self):
        """
        获取DeepSeek模型

        Returns:
            ChatOpenAI: 配置好的DeepSeek模型实例
        """
        try:
            deepseek_url = self.config.get("deepseek", "DEEPSEEK_SERVER_URL")
            deepseek_model_id = self.config.get("deepseek", "DEEPSEEK_MODEL_ID")
            deepseek_api_key = self.config.get("deepseek", "DEEPSEEK_API_KEY")

            # 验证配置完整性
            if not all([deepseek_url, deepseek_model_id, deepseek_api_key]):
                missing = []
                if not deepseek_url:
                    missing.append("DEEPSEEK_SERVER_URL")
                if not deepseek_model_id:
                    missing.append("DEEPSEEK_MODEL_ID")
                if not deepseek_api_key:
                    missing.append("DEEPSEEK_API_KEY")
                raise ModelConfigError(f"DeepSeek模型配置不完整，缺少: {', '.join(missing)}")

            # 检查连接
            if not self._check_connection(deepseek_url, deepseek_api_key):
                logger.warning(f"DeepSeek模型服务连接失败: {deepseek_url}")
                raise ModelConnectionError(f"无法连接到DeepSeek模型服务: {deepseek_url}")

            llm = ChatOpenAI(
                base_url=deepseek_url,
                model=deepseek_model_id,
                api_key=deepseek_api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
                extra_body={
                    "enable_thinking": False,
                })

            logger.info(f"DeepSeek模型初始化成功: {deepseek_model_id}")
            return llm

        except ModelConfigError:
            raise
        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"DeepSeek模型初始化异常: {e}")
            return self._handle_model_error("deepseek", error)

    def _get_lq_qwen3_8b_model(self):
        """
        获取本地Qwen3-8B-Instruct模型

        Returns:
            ChatOpenAI: 配置好的本地Qwen3-8B模型实例
        """
        try:
            server_url = "http://192.168.91.253:9002/v1"
            model_id = "Qwen3-8B"

            # 检查本地服务连接
            if not self._check_connection(server_url, "dummy", timeout=3):
                logger.warning(f"本地Qwen3-8B模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到本地Qwen3-8B模型服务: {server_url}")

            llm = ChatOpenAI(
                base_url=server_url,
                model=model_id,
                api_key="dummy",
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"本地Qwen3-8B模型初始化成功: {model_id}")
            return llm

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"本地Qwen3-8B模型初始化异常: {e}")
            return self._handle_model_error("lq_qwen3_8b", error)

    def _get_lq_qwen3_8b_lora_model(self):
        """
        获取本地Qwen3-8B-lq-lora模型

        Returns:
            ChatOpenAI: 配置好的本地Qwen3-8B-lq-lora模型实例
        """
        try:
            server_url = self.config.get("lq_qwen3_8B_lora", "LQ_QWEN3_8B_LQ_LORA_SERVER_URL")
            model_id = self.config.get("lq_qwen3_8B_lora", "LQ_QWEN3_8B_LQ_LORA_MODEL_ID")
            api_key = self.config.get("lq_qwen3_8B_lora", "LQ_QWEN3_8B_LQ_LORA_API_KEY", "dummy")

            # 验证配置完整性
            if not all([server_url, model_id]):
                missing = []
                if not server_url:
                    missing.append("LQ_QWEN3_8B_LQ_LORA_SERVER_URL")
                if not model_id:
                    missing.append("LQ_QWEN3_8B_LQ_LORA_MODEL_ID")
                raise ModelConfigError(f"本地Qwen3-8B-lq-lora模型配置不完整，缺少: {', '.join(missing)}")

            # 检查本地服务连接
            if not self._check_connection(server_url, api_key, timeout=3):
                logger.warning(f"本地Qwen3-8B-lq-lora模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到本地Qwen3-8B-lq-lora模型服务: {server_url}")

            llm = ChatOpenAI(
                base_url=server_url,
                model=model_id,
                api_key=api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"本地Qwen3-8B-lq-lora模型初始化成功: {model_id}")
            return llm

        except ModelConfigError:
            raise
        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"本地Qwen3-8B-lq-lora模型初始化异常: {e}")
            return self._handle_model_error("lq_qwen3_8b_lora", error)

    def _get_lq_qwen3_4b_model(self):
        """
        获取本地Qwen3-4B-Instruct模型

        Returns:
            ChatOpenAI: 配置好的本地Qwen3-4B模型实例
        """
        try:
            server_url = "http://192.168.91.253:9001/v1"
            model_id = "Qwen3-4B"

            # 检查本地服务连接
            if not self._check_connection(server_url, "dummy", timeout=3):
                logger.warning(f"本地Qwen3-4B模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到本地Qwen3-4B模型服务: {server_url}")

            llm = ChatOpenAI(
                base_url=server_url,
                model=model_id,
                api_key="dummy",
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"本地Qwen3-4B模型初始化成功: {model_id}")
            return llm

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"本地Qwen3-4B模型初始化异常: {e}")
            return self._handle_model_error("lq_qwen3_4b", error)

    def _get_qwen_local_14b_model(self):
        """
        获取本地Qwen3-14B-Instruct模型

        Returns:
            ChatOpenAI: 配置好的本地Qwen3-14B模型实例
        """
        try:
            server_url = "http://192.168.91.253:9003/v1"
            model_id = "Qwen3-14B"

            # 检查本地服务连接
            if not self._check_connection(server_url, "dummy", timeout=3):
                logger.warning(f"本地Qwen3-14B模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到本地Qwen3-14B模型服务: {server_url}")

            llm = ChatOpenAI(
                base_url=server_url,
                model=model_id,
                api_key="dummy",
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"本地Qwen3-14B模型初始化成功: {model_id}")
            return llm

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"本地Qwen3-14B模型初始化异常: {e}")
            return self._handle_model_error("qwen_local_14b", error)

    def _get_qwen3_5_35b_a3b_model(self):
        """
        获取 DashScope Qwen3.5-35B-A3B 模型

        Returns:
            ChatOpenAI: 配置好的 DashScope Qwen3.5-35B-A3B 模型实例
        """
        try:
            url = self.config.get("qwen3_5_35b_a3b", "DASHSCOPE_SERVER_URL")
            model_id = self.config.get("qwen3_5_35b_a3b", "DASHSCOPE_MODEL_ID")
            api_key = self.config.get("qwen3_5_35b_a3b", "DASHSCOPE_API_KEY")

            # 验证配置完整性
            if not all([url, model_id, api_key]):
                missing = []
                if not url:
                    missing.append("DASHSCOPE_SERVER_URL")
                if not model_id:
                    missing.append("DASHSCOPE_MODEL_ID")
                if not api_key:
                    missing.append("DASHSCOPE_API_KEY")
                raise ModelConfigError(f"DashScope Qwen3.5-35B 模型配置不完整，缺少: {', '.join(missing)}")

            llm = ChatOpenAI(
                base_url=url,
                model=model_id,
                api_key=api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
                extra_body={
                    "chat_template_kwargs": {"enable_thinking": False}
                }
            )

            logger.info(f"DashScope Qwen3.5-35B 模型初始化成功: {model_id} (思考模式: 关闭)")
            return llm
        except ModelConfigError:
            raise
        except Exception as e:
            return self._handle_model_error("qwen3_5_35b_a3b", ModelAPIError(str(e)))

    def _get_qwen3_5_27b_model(self):
        """
        获取 DashScope Qwen3.5-27B 模型

        Returns:
            ChatOpenAI: 配置好的 DashScope Qwen3.5-27B 模型实例
        """
        try:
            url = self.config.get("qwen3_5_27b", "DASHSCOPE_SERVER_URL")
            model_id = self.config.get("qwen3_5_27b", "DASHSCOPE_MODEL_ID")
            api_key = self.config.get("qwen3_5_27b", "DASHSCOPE_API_KEY")

            # 验证配置完整性
            if not all([url, model_id, api_key]):
                missing = []
                if not url:
                    missing.append("DASHSCOPE_SERVER_URL")
                if not model_id:
                    missing.append("DASHSCOPE_MODEL_ID")
                if not api_key:
                    missing.append("DASHSCOPE_API_KEY")
                raise ModelConfigError(f"DashScope Qwen3.5-27B 模型配置不完整，缺少: {', '.join(missing)}")

            llm = ChatOpenAI(
                base_url=url,
                model=model_id,
                api_key=api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
                extra_body={
                    "chat_template_kwargs": {"enable_thinking": False}
                }
            )

            logger.info(f"DashScope Qwen3.5-27B 模型初始化成功: {model_id} (思考模式: 关闭)")
            return llm
        except ModelConfigError:
            raise
        except Exception as e:
            return self._handle_model_error("qwen3_5_27b", ModelAPIError(str(e)))

    def _get_qwen3_5_122b_a10b_model(self):
        """
        获取 DashScope Qwen3.5-122B-A10B 模型

        Returns:
            ChatOpenAI: 配置好的 DashScope Qwen3.5-122B-A10B 模型实例
        """
        try:
            url = self.config.get("qwen3_5_122b_a10b", "DASHSCOPE_SERVER_URL")
            model_id = self.config.get("qwen3_5_122b_a10b", "DASHSCOPE_MODEL_ID")
            api_key = self.config.get("qwen3_5_122b_a10b", "DASHSCOPE_API_KEY")

            # 验证配置完整性
            if not all([url, model_id, api_key]):
                missing = []
                if not url:
                    missing.append("DASHSCOPE_SERVER_URL")
                if not model_id:
                    missing.append("DASHSCOPE_MODEL_ID")
                if not api_key:
                    missing.append("DASHSCOPE_API_KEY")
                raise ModelConfigError(f"DashScope Qwen3.5-122B 模型配置不完整，缺少: {', '.join(missing)}")

            llm = ChatOpenAI(
                base_url=url,
                model=model_id,
                api_key=api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
                extra_body={
                    "chat_template_kwargs": {"enable_thinking": False}
                }
            )

            logger.info(f"DashScope Qwen3.5-122B 模型初始化成功: {model_id} (思考模式: 关闭)")
            return llm
        except ModelConfigError:
            raise
        except Exception as e:
            return self._handle_model_error("qwen3_5_122b_a10b", ModelAPIError(str(e)))

    def _get_lq_qwen3_8b_emd(self):
        """
        获取本地Qwen3-Embedding-8B嵌入模型

        Returns:
            OpenAIEmbeddings: 配置好的本地Qwen3-Embedding-8B嵌入模型实例
        """
        try:
            server_url = "http://192.168.91.253:9003/v1"
            model_id = "Qwen3-Embedding-8B"

            # 检查本地服务连接
            if not self._check_connection(server_url, "dummy", timeout=3):
                logger.warning(f"本地Qwen3-Embedding-8B模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到本地Qwen3-Embedding-8B模型服务: {server_url}")

            # 使用 langchain_openai 的 OpenAIEmbeddings
            embeddings = OpenAIEmbeddings(
                base_url=server_url,
                model=model_id,
                api_key="dummy",  # 本地模型使用虚拟API key
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"本地Qwen3-Embedding-8B模型初始化成功: {model_id}")
            return embeddings

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"本地Qwen3-Embedding-8B模型初始化异常: {e}")
            return self._handle_model_error("lq_qwen3_8b_emd", error)

    def _get_siliconflow_embedding_model(self):
        """
        获取硅基流动Qwen3-Embedding-8B嵌入模型

        Returns:
            OpenAIEmbeddings: 配置好的硅基流动Qwen3-Embedding-8B嵌入模型实例
        """
        try:
            server_url = self.config.get("siliconflow_embed", "SLCF_EMBED_SERVER_URL")
            api_key = self.config.get("siliconflow_embed", "SLCF_EMBED_API_KEY")
            model_id = self.config.get("siliconflow_embed", "SLCF_EMBED_MODEL_ID", "Qwen/Qwen3-Embedding-8B")
            dimensions = self.config.get("siliconflow_embed", "SLCF_EMBED_DIMENSIONS", "4096")

            # 验证配置完整性
            if not all([server_url, api_key, model_id]):
                missing = []
                if not server_url:
                    missing.append("SLCF_EMBED_SERVER_URL")
                if not api_key:
                    missing.append("SLCF_EMBED_API_KEY")
                if not model_id:
                    missing.append("SLCF_EMBED_MODEL_ID")
                raise ModelConfigError(f"硅基流动Embedding模型配置不完整，缺少: {', '.join(missing)}")

            # 检查连接
            if not self._check_connection(server_url, api_key):
                logger.warning(f"硅基流动Embedding模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到硅基流动Embedding模型服务: {server_url}")

            # 使用 langchain_openai 的 OpenAIEmbeddings
            embeddings = OpenAIEmbeddings(
                base_url=server_url,
                model=model_id,
                api_key=api_key,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"硅基流动Embedding模型初始化成功: {model_id} (dimensions: {dimensions})")
            return embeddings

        except ModelConfigError:
            raise
        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"硅基流动Embedding模型初始化异常: {e}")
            return self._handle_model_error("siliconflow_embed", error)

    def _get_shutian_qwen3_5_122b_model(self):
        """
        获取蜀天Qwen3.5-122B-A10B模型

        Returns:
            ChatOpenAI: 配置好的蜀天Qwen3.5-122B模型实例
        """
        try:
            server_url = self.config.get("shutian", "SHUTIAN_122B_SERVER_URL", "http://183.220.37.46:25423/v1")
            model_id = self.config.get("shutian", "SHUTIAN_122B_MODEL_ID", "/model/Qwen3.5-122B-A10B")
            api_key = self.config.get("shutian", "SHUTIAN_122B_API_KEY", "lq123456")

            # 检查服务连接
            if not self._check_connection(server_url, api_key, timeout=3):
                logger.warning(f"蜀天Qwen3.5-122B模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到蜀天Qwen3.5-122B模型服务: {server_url}")

            llm = ChatOpenAI(
                base_url=server_url,
                model=model_id,
                api_key=api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"蜀天Qwen3.5-122B模型初始化成功: {model_id}")
            return llm

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"蜀天Qwen3.5-122B模型初始化异常: {e}")
            return self._handle_model_error("shutian_qwen3_5_122b", error)

    def _get_shutian_qwen3_8b_model(self):
        """
        获取蜀天Qwen3-8B模型

        Returns:
            ChatOpenAI: 配置好的蜀天Qwen3-8B模型实例
        """
        try:
            server_url = self.config.get("shutian", "SHUTIAN_8B_SERVER_URL", "http://183.220.37.46:25424/v1")
            model_id = self.config.get("shutian", "SHUTIAN_8B_MODEL_ID", "/model/Qwen3-8B")
            api_key = self.config.get("shutian", "SHUTIAN_8B_API_KEY", "lq123456")

            # 检查服务连接
            if not self._check_connection(server_url, api_key, timeout=3):
                logger.warning(f"蜀天Qwen3-8B模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到蜀天Qwen3-8B模型服务: {server_url}")

            llm = ChatOpenAI(
                base_url=server_url,
                model=model_id,
                api_key=api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"蜀天Qwen3-8B模型初始化成功: {model_id}")
            return llm

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"蜀天Qwen3-8B模型初始化异常: {e}")
            return self._handle_model_error("shutian_qwen3_8b", error)

    def _get_shutian_qwen3_5_35b_model(self):
        """
        获取蜀天Qwen3.5-35B模型

        Returns:
            ChatOpenAI: 配置好的蜀天Qwen3.5-35B模型实例
        """
        try:
            server_url = self.config.get("shutian", "SHUTIAN_35B_SERVER_URL", "http://183.220.37.46:25427/v1")
            model_id = self.config.get("shutian", "SHUTIAN_35B_MODEL_ID", "/model/Qwen3.5-35B")
            api_key = self.config.get("shutian", "SHUTIAN_35B_API_KEY", "lq123456")

            logger.info(f"正在初始化蜀天Qwen3.5-35B模型，服务器地址: {server_url}")

            # 检查服务连接（可通过配置禁用）
            skip_check = self.config.get("shutian", "SKIP_CONNECTION_CHECK", "false").lower() == "true"
            if not skip_check:
                connection_ok = self._check_connection(server_url, api_key, timeout=5)
                if not connection_ok:
                    # 连接检查失败时记录警告，但不阻止初始化（实际调用时如果失败会报错）
                    logger.warning(f"蜀天Qwen3.5-35B模型服务连接检查失败: {server_url}，但仍尝试初始化")
                else:
                    logger.info(f"蜀天Qwen3.5-35B模型服务连接检查通过: {server_url}")
            else:
                logger.info(f"跳过蜀天Qwen3.5-35B模型连接检查（SKIP_CONNECTION_CHECK=true）")

            llm = ChatOpenAI(
                base_url=server_url,
                model=model_id,
                api_key=api_key,
                temperature=0.7,
                timeout=self.REQUEST_TIMEOUT,
            )

            # 记录模型实例的详细信息用于调试
            logger.info(f"蜀天Qwen3.5-35B模型初始化成功: model_id={model_id}, base_url={llm.base_url if hasattr(llm, 'base_url') else server_url}")
            return llm

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"蜀天Qwen3.5-35B模型初始化异常: {e}")
            return self._handle_model_error("shutian_qwen3_5_35b", error)

    def _get_shutian_qwen3_embed(self):
        """
        获取蜀天Qwen3-Embedding-8B嵌入模型

        Returns:
            OpenAIEmbeddings: 配置好的蜀天Embedding模型实例
        """
        try:
            server_url = self.config.get("shutian", "SHUTIAN_EMBED_SERVER_URL", "http://183.220.37.46:25425/v1")
            model_id = self.config.get("shutian", "SHUTIAN_EMBED_MODEL_ID", "/model/Qwen3-Embedding-8B")
            api_key = self.config.get("shutian", "SHUTIAN_EMBED_API_KEY", "lq123456")

            # 检查服务连接
            if not self._check_connection(server_url, api_key, timeout=3):
                logger.warning(f"蜀天Qwen3-Embedding模型服务连接失败: {server_url}")
                raise ModelConnectionError(f"无法连接到蜀天Qwen3-Embedding模型服务: {server_url}")

            embeddings = OpenAIEmbeddings(
                base_url=server_url,
                model=model_id,
                api_key=api_key,
                timeout=self.REQUEST_TIMEOUT,
            )

            logger.info(f"蜀天Qwen3-Embedding-8B模型初始化成功: {model_id}")
            return embeddings

        except ModelConnectionError:
            raise
        except Exception as e:
            error = ModelAPIError(f"蜀天Qwen3-Embedding模型初始化异常: {e}")
            return self._handle_model_error("shutian_qwen3_embed", error)


# 创建全局实例
model_handler = ModelHandler()

def get_models():
    """
    获取模型的全局函数

    Returns:
        tuple: (llm, chat, embed) - LLM模型、聊天模型和嵌入模型实例
               注意：当前llm和chat使用相同模型实例，embed暂时返回None

    Note:
        这是一个便捷函数，直接使用全局model_handler实例获取模型
    """
    try:
        llm = model_handler.get_models()
        # 暂时返回相同的模型作为chat和embed
        return llm, llm, None
    except Exception as e:
        logger.error(f"获取模型失败: {e}")
        raise ModelConnectionError(f"无法获取模型服务: {e}")